1 апреля 2024
Архитектурная диаграмма должна отвечать на конкретный вопрос. C4 Model Саймона Брауна помогает выбрать уровень детализации: Context, Containers, Components или Code. В статье разбираю, что показывать на каждом уровне, как избегать смешения масштабов и как поддерживать диаграммы в актуальном состоянии.
Не обязательно рисовать все четыре уровня C4. Для многих команд достаточно Context и Container; Component и Code добавляют, когда они помогают ответить на конкретный вопрос. Модель не привязана к инструменту или нотации.
Проблема абстракции диаграмм
Для разных вопросов нужны разные диаграммы. Продуктовому руководителю важно видеть пользователей и границы системы, инженеру при изменении сервиса нужны компоненты и зависимости. Если смешать эти уровни на одном рисунке, читателю трудно понять, что именно он объясняет.
UML, доминирующая нотация 1990-х и 2000-х, давал 14 типов диаграмм (class, sequence, activity, component, deployment и т. д.), но не давал правила, как эти типы между собой соотносятся. Grady Booch в «Object Solutions» (Addison-Wesley, 1996) и в более поздней «Object-Oriented Analysis and Design with Applications» (3rd ed., 2007) подробно обсуждал levels of abstraction как ключевую инженерную дисциплину - но в самом UML это правило осталось имплицитным. В результате команды, искренне старавшиеся «делать UML», получали либо переусложненные class diagrams (которые быстро устаревали и переставали обновляться), либо вырожденные рисунки в Visio, формально похожие на UML, но без какой-либо дисциплины уровней.
Свободная нотация дает гибкость, но требует договориться о значении элементов и уровне детализации. Это относится и к визуальным редакторам, и к текстовым описаниям диаграмм.
Саймон Браун, британский архитектор и автор «Software Architecture for Developers» (Leanpub, 2014), сформулировал проблему так: команды разучились разговаривать об архитектуре общим языком. Не потому что они не знают паттернов или не умеют рисовать, а потому что у них нет согласованного словаря зума. Когда один говорит «архитектура», он имеет в виду набор сервисов; когда другой - расположение классов в кодовой базе; когда третий - схему деплоя в облако. Все три ответа правильные, но они о разных слоях, и без явной фиксации уровня обсуждение распадается.
C4 Model - ответ на эту проблему. Браун предложил не новую нотацию, а правило: всегда явно фиксировать, на каком из четырех уровней (Context, Containers, Components, Code) находится диаграмма, и не смешивать элементы разных уровней на одном холсте. Аналогия Брауна, которую он повторяет в большинстве своих выступлений - карты Google: страна → город → район → улица. На каждом уровне виден свой набор объектов, и переключение между уровнями - это именно зум, а не переключение типа карты. Тот же набор объектов реального мира описывается с разной степенью детализации в зависимости от того, какой вопрос вы задаете.
Четыре уровня абстракции
C4 предлагает четыре уровня детализации для разных аудиторий. Каждый уровень отвечает на свой вопрос и адресован своей аудитории; на одном уровне нельзя ответить на вопрос соседнего, и попытка это сделать - первый признак того, что диаграмма выйдет из-под контроля.
- Context - для всех, включая бизнес. Вопрос: что эта система делает в окружении пользователей и других систем?
- Containers - для технической команды. Вопрос: из каких deployable units состоит система и как они общаются?
- Components - для разработчиков. Вопрос: из каких структурных блоков состоит конкретный container?
- Code - для глубокого погружения. Вопрос: как именно реализован отдельный component?
Уровни C4 описывают детализацию архитектуры, а bounded contexts в DDD - границы моделей предметной области. Эти понятия можно использовать вместе, но они решают разные задачи.
Mark Richards и Neal Ford в «Software Architecture: The Hard Parts» (O'Reilly, 2021) формулируют похожий тезис в терминах архитектурных решений: каждое решение принимается на определенном уровне (системном, сервисном, компонентном), и попытка решать вопрос одного уровня инструментами другого - частая причина проваленных миграций. C4 операционализирует эту идею: уровень обсуждения должен быть виден на самой диаграмме, а не подразумеваться.
C4-диаграмма показывает структуру системы, ADR объясняет принятое решение. Их можно связывать в документации, указав владельца и порядок обновления.
Level 1: System Context
Показывает систему как черный ящик в окружении пользователей и внешних систем. Это bird's eye view - вид с высоты птичьего полета.
| Scope | Одна software system |
| Primary elements | Ваша система (в центре) |
| Supporting elements | Пользователи (роли, персоны) и внешние системы |
| Аудитория | Все: технические и нетехнические люди |
Пример: B2B SaaS платформа
Стилизованный пример на основе типичных паттернов B2B SaaS - юридические, финансовые и tax-сервисы для малого бизнеса. Структура персон и интеграций здесь типична для класса систем, а не привязана к конкретной компании.
┌─────────────────────────────────────────────────────────────────┐
│ USERS (Personas) │
├─────────────┬─────────────┬─────────────┬─────────────┬─────────┤
│ Employee │ Client │ Admin │ Support │ SRE │
│ (firm user) │ (end user) │ (system) │ (L2 help) │ (ops) │
└──────┬──────┴──────┬──────┴──────┬──────┴──────┬──────┴────┬────┘
│ │ │ │ │
▼ ▼ ▼ ▼ ▼
┌─────────────────────────────────────────────────────────────────┐
│ │
│ ┌───────────────────┐ │
│ │ THE PLATFORM │ │
│ │ (Our System) │ │
│ └─────────┬─────────┘ │
│ │ │
└──────────────────────────────┼──────────────────────────────────┘
│
┌───────────────────────┼───────────────────────┐
▼ ▼ ▼
┌─────────────┐ ┌─────────────┐ ┌─────────────┐
│ Payment │ │ E-Signature │ │ Email │
│ Provider │ │ Service │ │ Integration │
└─────────────┘ └─────────────┘ └─────────────┘
EXTERNAL SYSTEMS (Third-Party Services)
Что НЕ включать: технологии, протоколы, низкоуровневые детали. Диаграмма должна быть понятна product manager или CEO. Самая частая ошибка на этом уровне - попытка показать «как у нас устроено внутри»: появляются микросервисы, базы данных, очереди. Из-за внутренних деталей нетехнической аудитории может быть сложно понять назначение системы и ее внешние связи.
Level 2: Containers
Zoom-in внутрь системы. Показывает deployable units: веб-приложения, API, базы данных, очереди сообщений, мобильные приложения.
| Scope | Одна software system |
| Primary elements | Containers внутри системы |
| Supporting elements | Пользователи и внешние системы, связанные с containers |
| Аудитория | Технические люди: архитекторы, разработчики, DevOps |
Показывает:
- Общую структуру архитектуры
- Распределение ответственности
- Основные технологические решения
- Как containers общаются друг с другом
Пример: Backend платформы
Стилизованный пример на основе типичных паттернов B2B SaaS-бэкенда: web app + background jobs + scheduler поверх общего набора storage, плюс несколько внешних gRPC-сервисов для специализированных функций. Конкретные имена обобщены, структура отражает класс систем.
CLIENT APPS: [React SPA] [Mobile Employee] [Mobile Client] [Desktop] [Admin]
| | | | |
+-------+------+-------+-------+-------------+---------+
| |
v HTTPS API v
+--------------------------------------------------+
| BACKEND |
| |
| +----------+ +------------+ +-----------+ |
| | Web App | | Background | | Scheduler | |
| | (Rails) | | Jobs | | (Cron) | |
| +----+-----+ +-----+------+ +-----+-----+ |
| | | | |
| +--------------+--------------+ |
| | |
| v |
| +----------------------------------------------+|
| | DATABASES ||
| | [PostgreSQL] [Redis] [OpenSearch] [S3] ||
| +----------------------------------------------+|
+--------------------------------------------------+
| | |
v gRPC v gRPC v gRPC
+---------+ +---------+ +-----------+
|Payments | | OCR | |Marketplace|
| Service | | Service | | Service |
+---------+ +---------+ +-----------+
Что НЕ включать: кластеризацию, load balancers, репликацию - это для Deployment diagram. Показывать «у нас 3 инстанса web app за nginx-балансировщиком» на Container-диаграмме - распространенная ошибка: вы смешиваете логическую структуру (какие containers есть) с физическим деплоем (как они размещены), и диаграмма перестает быть полезной для обоих вопросов.
Level 3: Components
Zoom-in внутрь одного container. Показывает структурные блоки: сервисы, контроллеры, репозитории и их взаимодействие.
| Scope | Один container |
| Primary elements | Components внутри container |
| Supporting elements | Другие containers и внешние системы |
| Аудитория | Архитекторы и разработчики |
Пример: Web Application container
Стилизованный пример на основе типичных паттернов Rails-приложения с разделением на controllers, services, repositories и background jobs. Структура отражает класс архитектур, а не конкретный продукт.
+--------------------------------------------------------------+
| WEB APPLICATION (Rails) |
| |
| +--------------+ +--------------+ +------------------+ |
| | Controllers | | Services | | Background | |
| | | | | | Jobs | |
| | - Invoices |->| - Billing | | - PaymentSync | |
| | - Users | | - Payments |->| - ReportBuilder | |
| | - Reports | | - Analytics | | - Notifications | |
| +------+-------+ +------+-------+ +--------+---------+ |
| | | | |
| v v | |
| +--------------+ | |
| | Repositories |<------------+ |
| | | |
| | - UserRepo | |
| | - InvoiceRepo| |
| +------+-------+ |
| | |
+--------------------------------------------------------------+
|
v SQL
+--------------+
| PostgreSQL |
+--------------+
Level 4: Code
Самый детальный уровень: UML-диаграммы классов, ER-диаграммы и т. д. В большинстве случаев не нужен - код сам себя документирует, а IDE позволяет навигировать по нему.
Когда полезен:
- Сложные алгоритмы, где визуализация помогает понять последовательность шагов
- Design patterns - наглядно показать связи между классами
- Критические части системы, которые редко меняются (ядро платежной логики, state machine)
Для большинства проектов достаточно уровней 1-3. Если код хорошо структурирован и покрыт тестами, Level 4 создает больше работы по поддержанию актуальности, чем приносит пользы. Браун в публичных выступлениях прямо говорит: если вы рисуете Code-диаграмму вручную и обновляете ее при каждом коммите, вы тратите время; если Code-диаграмма не генерируется из самого кода, она устареет в течение спринта.
Дополнительные диаграммы
System Landscape Diagram
Когда нужно показать несколько систем в рамках организации. По сути - Context diagram без фокуса на конкретной системе.
| Scope | Enterprise / организация / департамент |
| Аудитория | Все |
Dynamic Diagram
Показывает взаимодействие элементов во время выполнения: user story, use case, конкретный сценарий. Похоже на UML sequence diagram, но с более свободной раскладкой.
| Scope | Конкретная feature / user story |
| Elements | На выбор: systems, containers или components |
Deployment Diagram
Показывает, как containers деплоятся на инфраструктуру: серверы, VM, Kubernetes, cloud services. Основан на UML deployment diagram.
| Scope | Одна или несколько систем в конкретном environment (prod, staging, dev) |
| Elements | Deployment nodes, container instances, infrastructure (DNS, load balancers, firewalls) |
| Аудитория | Архитекторы, разработчики, DevOps, SRE |
Можно использовать иконки AWS, Azure, GCP - но включайте их в легенду. AWS Reference Architectures, публикуемые на aws.amazon.com/architecture, - один из самых известных публичных примеров иерархического подхода: на верхнем уровне они показывают workload как черный ящик в окружении managed-сервисов (структурно это Context), на следующем - какие сервисы и каким образом интегрированы (структурно это Containers), и только на глубоких страницах появляется детализация по компонентам и зонам доступности (Deployment view). Сами AWS не называют это «C4», но дисциплина уровней узнаваема.
Стратегия внедрения в команде
Внедрение C4 - это не «договорились рисовать четыре диаграммы вместо одной». Это смена дисциплины обсуждения архитектуры, и она не приживается за счет одного письма от архитектора. Команды, которые успешно внедряют C4, обычно проходят через несколько шагов в одном и том же порядке.
Шаг 1: одна Context-диаграмма на каждую систему. Это минимальный артефакт, который окупается на первой же встрече с новым человеком в команде. Context рисуется за 30 минут, отвечает на вопрос «что это и кто этим пользуется» и не требует от команды менять процессы. Если за месяц после введения дисциплины каждая ваша система обзавелась актуальной Context-диаграммой - это успех первого этапа.
Шаг 2: Container-диаграмма для каждой системы, которая шире одного приложения. Это уровень, на котором живут технические дискуссии: куда добавить новый сервис, что мигрировать, где провести границу. Здесь важно сразу класть диаграмму в репозиторий рядом с кодом (см. раздел про Diagrams as Code), а не в Confluence - иначе она устареет за квартал.
Шаг 3: Component-диаграммы выборочно. Не для каждой системы, а для тех container-ов, где есть онбординг или где структура нетривиальна. Хороший индикатор: если новый разработчик задает вопрос «как у вас устроен этот сервис», и ответ занимает 20 минут устной речи - пора нарисовать Component-диаграмму.
Шаг 4: Deployment-диаграмма как минимум для production. Этот уровень особенно важен для on-call: когда инцидент происходит в 3 утра, дежурный должен по диаграмме понимать, какие узлы за что отвечают и где смотреть метрики. Code-уровень в большинстве команд так и не появляется в явном виде, и это нормально.
Антипаттерны внедрения
Внедрения C4 обычно ломаются на нескольких типовых ошибках. Каждая из них выглядит как мелочь, но в сумме они приводят к тому, что через полгода диаграммы либо устаревают, либо команда возвращается к свободным рисункам.
Смешивание уровней на одном холсте. Самая частая ошибка. На Container-диаграмме появляются классы из одного из сервисов («ну у нас тут важная PaymentProcessor-логика»), на Component - облачные сервисы AWS («ну тут же видно, куда мы пишем в S3»). Каждый раз этому есть локальное оправдание, но результат тот же: диаграмма перестает принадлежать одному уровню, и аудитория, для которой этот уровень предназначен, перестает ее понимать. Правило простое: если элемент не подходит к уровню, значит, он живет на соседнем; нарисуйте на нем отдельную диаграмму или просто опустите.
Переусложненный Context. Команда соблазняется показать «как у нас все устроено внутри» уже на Context-диаграмме - и появляются микросервисы, базы, очереди. Продукт-менеджеру или CEO по цепочке «UserService → AuthService → Redis» трудно понять, какую задачу решает система. Context должен оставаться черным ящиком; все внутреннее - на следующем уровне.
Переусложненный Code. Зеркальная ошибка: команда рисует UML-диаграмму классов на 60 классов, фиксирует ее в Confluence и через месяц перестает обновлять. Через квартал диаграмма расходится с кодом настолько, что становится дезинформацией - новый разработчик пользуется ею и принимает неверные решения. Code-диаграммы оправданы только тогда, когда они либо генерируются автоматически из кода, либо описывают редко меняющееся ядро (state machine, доменное ядро) - то есть существуют в режиме, который защищает от стихийного устаревания.
Диаграммы нужно обновлять вместе с изменениями системы. Хранение в репозитории позволяет включать их в ревью; визуальный редактор тоже подходит, если есть владелец, история изменений и понятный порядок обновления. Важно, чтобы читатель мог определить актуальную версию.
Попытка показать всю архитектуру на одном рисунке часто делает его трудным для чтения. Лучше разделить вопросы по уровням и связать диаграммы ссылками. Большой обзор может быть полезен, если у него есть понятная аудитория и задача.
Зависимость от единственного автора диаграмм. Когда диаграммы рисует один человек в Visio, а остальная команда не умеет с ними работать, после ухода этого человека диаграммы могут перестать обновляться. Дисциплина C4 окупается только тогда, когда ею владеет команда, а не один архитектор. Это связано с переходом к Diagrams as Code: текстовый исходник диаграммы доступен любому разработчику, и обновление - это PR, а не визит к автору.
Примеры из production систем
C4 как формальная нотация принят далеко не везде, но иерархическая дисциплина зума - принципиально шире, и ее используют команды, которые формально про C4 не упоминают. Полезно посмотреть на несколько публичных примеров с разным контекстом.
c4model.com - example bank. Самый прямой пример - официальный сайт модели Брауна (c4model.com) и связанная коллекция в Structurizr DSL (structurizr.com/dsl). Там приведены полные комплекты диаграмм для нескольких эталонных систем (Big Bank plc, Internet Banking System), которые показывают, как все четыре уровня выглядят на одной системе и как они между собой связаны. Эти примеры помогают увидеть, как описать одну систему на разных уровнях детализации.
AWS Reference Architectures. Публичная библиотека референсных архитектур AWS (aws.amazon.com/architecture) формально не использует C4-нотацию, но дисциплина уровней в их диаграммах узнаваема: верхняя диаграмма показывает workload в окружении пользователей и managed-сервисов (структурно это Context), следующие слои показывают логические компоненты и data flow (структурно это Containers и Components), и только дальше - детализация по AZ и сетевой топологии (Deployment). Это полезный пример, потому что он показывает: иерархический подход работает не только для одной системы внутри одной компании, но и как способ публичной коммуникации между вендором и клиентом.
Стилизованный антипример. Стилизованный пример на основе типичных паттернов: команда из 30 инженеров поддерживает «архитектурный wiki», в котором лежит одна большая диаграмма «Overall Architecture» в Lucidchart, нарисованная два года назад техлидом, который с тех пор ушел. Диаграмма показывает 40 прямоугольников (микросервисы, базы, очереди) на одном холсте; уровни не разделены. Каждый новый разработчик видит ее при онбординге, ничего не понимает, идет спрашивать у соседей. Через год команда обнаруживает, что 7 из 40 прямоугольников описывают сервисы, которых уже нет, еще 5 - сервисы под другими именами, и обновлять диаграмму некому, потому что владелец один и он перегружен. Это типовой шаблон, который дисциплина C4 предотвращает с помощью правила «одна диаграмма на zoom-уровень, диаграмма живет в репо рядом с кодом».
Для знакомства с моделью достаточно официальных примеров C4. Диаграммы других авторов можно использовать для сравнения, но сходство уровней не доказывает, что компания применяет C4 как процесс.
Diagrams as Code в production
Для долгоживущих диаграмм нужны владелец, история изменений и порядок актуализации. Diagrams as Code удобен, если команда уже работает через Git и PR. Визуальный редактор также подходит, если обеспечивает необходимые для команды версионирование, совместное редактирование и проверку актуальности.
Diagrams as Code решает эту проблему за счет текстового исходника, который живет в том же репозитории, что и код:
- Версионирование в Git. Каждое изменение диаграммы фиксируется коммитом, у которого есть автор, дата и сообщение.
- Code review через PR. Диаграмма меняется тем же процессом, что и код; рецензент видит diff и может оспорить решение.
- Связь с изменением, которое ее мотивировало. Если PR добавляет новый сервис, в том же PR обновляется Container-диаграмма. Расхождение становится заметно при review.
- Актуальная версия диаграммы. Не возникает вопроса «какая версия актуальная» - согласованная версия хранится в main.
- Автоматический рендеринг в CI. Из текстового исходника собирается PNG/SVG, который публикуется в README, на сайт документации или в pages.
Я использую PlantUML с C4 extension - библиотекой макросов от Ricardo Niepel, которая добавляет в PlantUML синтаксис C4 (Person, System, Container, Component, Rel и т. д.). Пример Context-диаграммы:
@startuml
!include <C4/C4_Context>
title My Platform - System Context
Person(employee, "Employee", "Firm user")
Person(client, "Client", "End user")
System(platform, "Platform", "Main system")
System_Ext(payments, "Payment Provider", "Processes payments")
System_Ext(email, "Email Service", "Sends notifications")
Rel(employee, platform, "Manages operations")
Rel(client, platform, "Uses services")
Rel(platform, payments, "Processes payments", "HTTPS")
Rel(platform, email, "Sends emails", "SMTP")
@enduml
Какие диаграммы создавать
| Диаграмма | Рекомендация |
|---|---|
| System Context | Да - для каждой системы |
| Container | Да - для каждой системы |
| Component | По необходимости |
| Code | Редко, только для сложных частей |
| Deployment | Да - как минимум для production |
Инструменты
- Structurizr DSL - DSL и инструмент от самого Саймона Брауна, спроектированный специально под C4. Вариант от автора модели, поддерживает single source of truth для нескольких диаграмм одной системы.
- PlantUML + C4 macro library - библиотека Ricardo Niepel поверх PlantUML; мой выбор, отлично интегрируется с CI/CD, рендерится локально и в Docker.
- Mermaid C4 - экспериментальный синтаксис. Поддержку проверяют для версии Mermaid в конкретном рендере; поддержка Mermaid в хостинге не гарантирует C4.
- Graphviz - не C4-специфичный, но удобный для генерации диаграмм из метаданных кода.
- draw.io / Lucidchart - визуальные редакторы для команд, которые не готовы к Diagrams as Code. Подходят для разовых иллюстраций; для долгоживущей документации проигрывают text-based инструментам.
Советы
- Начинайте с Context - это фундамент понимания.
- Не смешивайте уровни - каждая диаграмма должна быть на одном уровне абстракции.
- Включайте легенду - особенно если используете нестандартные обозначения или иконки облачных провайдеров.
- Обновляйте регулярно - устаревшая диаграмма хуже, чем ее отсутствие.
- Diagrams as code - версионируйте вместе с кодом, ревьюйте через PR.
- Монолит vs Микросервисы - когда какая архитектура подходит.
- Когда переходить на Kubernetes - примитивы как словарь общения с платформой; перекликается с примитивами C4 как словарем общения об архитектуре.
- Системный менеджмент - закон Конвея и связь между структурой команды и структурой системы, которая видна в C4.
Источники
Первоисточники по C4 и архитектурной коммуникации
- c4model.com - официальный сайт модели; автор Simon Brown. Каноническое описание уровней, FAQ, example bank.
- Simon Brown. Software Architecture for Developers (Leanpub, 2014, постоянно обновляется). Развёрнутое объяснение, откуда взялась модель и какие проблемы UML-эпохи она решает.
- Grady Booch. Object Solutions: Managing the Object-Oriented Project (Addison-Wesley, 1996). Levels of abstraction как фундаментальная инженерная дисциплина.
- Eric Evans. Domain-Driven Design (Addison-Wesley, 2003). Bounded context как способ зафиксировать уровень обсуждения; концептуально близко к C4 System Landscape.
- Vaughn Vernon. Implementing Domain-Driven Design (Addison-Wesley, 2013). Context maps - визуальный язык для соотношения нескольких bounded contexts.
- Mark Richards, Neal Ford. Software Architecture: The Hard Parts (O'Reilly, 2021). Архитектурные решения по уровням и цена смешивания уровней.
- Michael Nygard. Documenting Architecture Decisions (cognitect.com, 2011). Оригинальный пост про ADR; естественный спутник C4-диаграмм.
Реальные примеры и case studies
- Structurizr - DSL и платформа для C4 от автора модели; включает example bank с полными комплектами диаграмм для эталонных систем.
- AWS Reference Architectures - публичная библиотека иерархических архитектурных диаграмм; не используют C4-нотацию, но дисциплина уровней узнаваема.
- Atlassian Engineering Blog - публикации про практику ADR и архитектурную документацию.
- Примеры диаграмм конкретных компаний нужно сопровождать ссылкой на конкретную публикацию и датой.
- Spotify Engineering - публикации про архитектуру и Squad-модель.
Инструменты Diagrams as Code
- PlantUML standard library - включает C4 macro library от Ricardo Niepel.
- C4-PlantUML на GitHub - исходники макросов и примеры использования.
- Mermaid C4 - экспериментальный синтаксис. Поддержку проверяют для версии Mermaid в конкретном рендере; поддержка Mermaid в хостинге не гарантирует C4.
- Graphviz - универсальный движок для генерации диаграмм из текстового описания.
- draw.io - визуальный редактор для команд, которые ещё не на Diagrams as Code.
- C4 model: Diagrams
- C4 model: Notation
- Mermaid: C4 Diagrams