Введение

Схемы архитектуры часто сводятся к совокупности «квадратиков и стрелочек», которые каждый участник команды интерпретирует по-своему. C4 помогает решить эту проблему и предоставляет возможность последовательно представить систему на различных уровнях.

В 2023 году я опубликовала на Хабре статью «Нотация моделирования архитектуры С4 — примеры диаграмм и инструменты». К моменту ее публикации она уже набрала более 280 тысяч просмотров.

С того времени я накопила большой объем новых примеров, уточнений и выявила множество типичных ошибок, которые встречаются у новичков. Поэтому я выпускаю обновленный и расширенный гайд с кучей картинок, который сделает работу с C4 максимально понятной и удобной.

Это полное практическое руководство я создала для русскоязычных IT-команд и сообщества GetAnalyst. В спорных ситуациях вы сможете обратиться к статье и быстро найти ответ, а мне не придётся снова и снова разбирать одни и те же вопросы в личных сообщениях и чатах :)

В нём я разбираю все уровни C4, их элементы и правила, реальные примеры архитектуры, распространенные ошибки и инструменты. Статья будет полезна системным аналитикам, архитекторам, разработчикам, тестировщикам и другим IT-специалистам, которые работают с архитектурой систем и для которых важно ее понимать.

Оглавление:
Теория для знакомства с C4

Все элементы нотации C4
- Draw.io - визуальный редактор для C4
- Structurizr - диаграмма C4 через код
- Общие правила оформления

Как создать C4 с нуля
- C4 / Context
- C4 / Container
- C4 / Component
- C4 / Code

Инструменты
Создание C4 с ИИ
Как создать C4-диаграмму с помощью нейросетей (ИИ)
Подборка примеров и проектов
Заключение

Теория для знакомства с C4

Нотация моделирования архитектуры C4 была создана британским разработчиком и программным архитектором Саймоном Брауном. Первая публикация с названием C4 вышла в 2011 году, что делает эту нотацию относительно новой.

Она была создана из-за реальной проблемы: отсутствие нормальных нотаций моделирования, которые помогают разложить архитектуру для команды. Это мешало Саймону Брауну в процессе чтения лекций его студентам, поэтому он решил свою проблему созданием своей нотации.

Созданная им нотация C4 позволяет рассматривать архитектуру последовательно. Мы начинаем с общего представления системы, а затем приближаем отдельные части и изучаем их более детально.

Название C4 происходит как раз из-за четырех уровней детализации:

  • Context (Контекст) - система, ее пользователи и окружающая среда.

  • Container (Контейнер) - приложения и хранилища внутри системы.

  • Component (Компонент) - компоненты внутри выбранного контейнера.

  • Code (Код) - реализация отдельного компонента на уровне кода.

Каждый последующий уровень раскрывает элементы предыдущего. Однако, создание всех четырех диаграмм не обязательно. Достаточно выбрать те уровни, которые действительно необходимы вашей команде.


Все элементы нотации C4

В C4 используется небольшой набор элементов. На каждом следующем уровне к ним добавляются новые детали.

В разных инструментах набор и внешний вид элементов могут немного отличаться. Поэтому я подготовила для вас шпаргалки с ключевыми элементами из инструментов, которыми сама постоянно пользуюсь для построения C4-диаграмм.

Draw.io - визуальный редактор

https://app.diagrams.net/

Чтобы начать работать с C4 в draw.io, убедитесь, что у вас включены соответствующие фигуры в настройках.

Как включить нотацию C4 в draw.io
Как включить нотацию C4 в draw.io

Level 1:

Элементы диаграммы C4 Context
Элементы диаграммы C4 Context

Level 2:

Ссылка на документацию от автора нотации Саймона Брауна, которая поясняет, как правильно использовать элемент "контейнер-труба", предназначенный для брокеров или очередей/топиков: https://c4model.com/abstractions/queues-and-topics

Элементы диаграммы C4 Container
Элементы диаграммы C4 Container

Level 3:

Элементы диаграммы C4 Component
Элементы диаграммы C4 Component

Level 4:

Я не буду детально разбирать элементы уровня C4/Code в этой статье. Этот уровень описывает уже не общую архитектуру системы, а структуру программного кода внутри конкретного компонента: классы, интерфейсы, функции, объекты, таблицы баз данных и их связи.

Чаще всего для этого используется UML-диаграмма классов, но это также может быть ER-диаграмма или другая подходящая визуализация. То есть у C4/Code нет отдельного универсального набора фигур: способ отображения зависит от языка программирования, структуры приложения и выбранного инструмента.

Такие диаграммы обычно создаются уже после начала разработки и могут автоматически генерироваться средствами IDE или UML-инструментов.

Кроме того, код меняется достаточно часто, поэтому нарисованная вручную схема быстро устаревает. Автор C4 также считает этот уровень необязательным и рекомендует использовать его только для наиболее важных или сложных компонентов. Поэтому здесь я просто покажу два примера C4/Code с официального сайта, чтобы было понятно, как может выглядеть этот уровень детализации.

Пример диаграммы C4 / Code с комментариями
Пример диаграммы C4 / Code с комментариями

https://c4model.com/diagrams/code

Structurizr - диаграмма C4 через код

https://playground.structurizr.com/

Все подробные описания элементов и их назначение я оставила на картинках выше к элементам draw.io. Здесь нюансы и детали по элементам уже не дублирую.

При работе со Structurizr важно разделять тип элемента и его визуальное оформление.

  • В коде уже предусмотрены основные элементы C4: Person, Software System, Container и Component.

  • Дополнительные обозначения — например, External, Database, MessageBroker или Gateway — мы создаём с помощью тегов.

Сам по себе тег не меняет внешний вид элемента. Для него необходимо задать стиль: цвет, форму, границу и другие параметры. Например, база данных, брокер и API Gateway на уровне C4 остаются контейнерами, но визуально могут отображаться как цилиндр, труба или шестиугольник.

В draw.io форма выбирается вручную, а в Structurizr задаётся через связку tags и styles.

По умолчанию набор форм в C4 минимален: та же база данных, брокер или Backend-приложение могут выглядеть как обычные прямоугольники. Поэтому дальше для каждого элемента я покажу не только код его создания, но и теги со стилями, которые помогут сделать диаграмму более наглядной и единообразной.

C4/Context

Элемент

Как использовать

Код Structurizr

Пользователь (Person)

Пользователь или группа пользователей, которые взаимодействуют с моделируемой системой. Это может быть покупатель, администратор, оператор или курьер.

Маска:
elementName = person "Название элемента" "Описание элемента"

Пример:
customer = person "Покупатель" "Авторизованный пользователь. Покупает товары в интернет-магазине."

Внешний пользователь (Person с tag)

На схеме можно выделить неавторизованного пользователя, сотрудника внешней организации, пользователя внешней системы.

В C4 это не отдельный тип элемента, а обычный person с дополнительным тегом.

Маска:
elementName = person "Название элемента" "Описание элемента" { tags "External"}

Пример:
customer = person "Покупатель" "Неавторизованный пользователь. Просматривает товары." { tags "External"}

Основная система (Software System)

Система, для которой строится контекстная диаграмма. Здесь показывается вся система целиком, а не отдельный Frontend, Backend или микросервис.

Маска:
elementName = softwareSystem "Название элемента" "Описание элемента"

Пример:
shop = softwareSystem "Интернет-магазин" "Система для продажи товаров онлайн."

Внешняя система

Система за пределами моделируемой системы, с которой выполняется интеграция. Это может быть платёжный сервис, служба доставки или сервис уведомлений.

Маска:
elementName = softwareSystem "Robokassa" "Интернет-эквайринг для приёма платежей." { tags "External"}

Пример:
robokassaPay = softwareSystem "Robokassa" "Интернет-эквайринг для приёма платежей." { tags "External"}

Обычная связь

Показывает взаимодействие между пользователем и системой или между двумя системами. Название связи лучше формулировать как действие.

Маска:
elementName1 -> elementName2 "Название действия"

Пример:
customer -> shop "Использует"

Связь с указанием протокола или технологии

Показывает не только действие, но и способ интеграции. Автор C4 предлагает не перегружать Context техническими деталями, но в GetAnalyst мы рекомендуем указывать их, если это делает схему понятнее.

Маска:
elementName1 -> elementName2 "Название действия" "Указание протокола"

Пример:
shop -> robokassaPay "Вызывает API для проведения платежей" "JSON/HTTPS (REST API)"

Представление и название диаграммы для System Context

Определяет, для какой системы строится контекстная диаграмма, какие элементы в неё входят и как они располагаются.

Пример:
systemContext shop "SystemContext" { include * autoLayout lr title "C4 / Context — Интернет-магазин" description "Система для продажи товаров онлайн."}

Все стили и теги из последней колонки в итоговом коде объединяются в один блок:

views {
    systemContext shop "SystemContext" {
        include *
        autoLayout lr
        title "C4 / Context — Интернет-магазин"
        description "Система для продажи товаров онлайн."
    }

    styles {
        element "Person" {
            shape Person
            background #08427b
            color #ffffff
        }

        element "Software System" {
            shape RoundedBox
            background #1168bd
            color #ffffff
        }

        element "External" {
            background #8c8596
            color #ffffff
        }

        relationship "Relationship" {
            color #707070
            thickness 2
            routing Orthogonal
        }
    }
}

C4/Container

Элемент

Как использовать

Код Structurizr

Граница системы (System Boundary)

Граница показывает, какие приложения и хранилища относятся к интернет-магазину, а какие системы находятся за его пределами.

Пример:

Frontend-приложение

Web-приложение или Desktop-приложение, которое запускается на устройстве пользователя.

Обычный виджет или UI-модуль внутри приложения отдельным контейнером не является.

Пример:
webApp = container "Web-приложение покупателя" "Просмотр каталога и оформление заказов" "JavaScript" { tags "WebApp"}

Стиль:
element "WebApp" {
shape WebBrowser
}

Мобильное приложение.

Аналог Frontend.

Пример:
mobApp = container "iOS-приложение покупателя" "Просмотр каталога и оформление заказов" "Swift" { tags "MobileApp"}

Стиль:
element "MobileApp" {
shape MobileDevicePortrait
}

Backend-приложение

Монолитное Backend-приложение, сервис, микросервис, независимые сервис-Worker или другое самостоятельно запускаемое приложение.

Классы, модули и библиотеки внутри него относятся уже к уровню Component.

Пример:
authService = container "Сервис авторизации" "Аутентифицирует пользователей и выдаёт JWT-токены" "Java, Spring Boot"

Базовый стиль контейнера:
element "Container" {
shape RoundedBox
background #2aa3d4
color #ffffff
}

API Gateway / BFF

Единая точка входа в систему, которая выполняет маршрутизацию, проксирование и общую обработку запросов.

Шестиугольник — визуальное правило, рекомендуемое GetAnalyst. Можно использовать обычный прямоугольник.

Пример:
gateway = container "API Gateway" "Принимает и маршрутизирует внешние запросы" "Kong" { tags "Gateway"}

Стиль:
element "Gateway" {
shape Hexagon
}

База данных или файловое хранилище

Реляционная или NoSQL-база данных, объектное либо файловое хранилище.

Если система использует несколько независимых хранилищ, каждое показывается отдельным контейнером.

Пример БД:
ordersDb = container "БД заказов" "Хранит заказы и их статусы" "PostgreSQL" { tags "Database"}

Пример ФХ:

productFiles = container "Файловое хранилище" "Хранит изображения товаров" "S3" {
tags "FileStorage"
}

Стили:
element "Database" { shape Cylinder }

element "FileStorage" { shape Cylinder }

Можно добавить цвета, чтобы различать что есть ФХ, а что - БД. Будет далее в примерах диаграмм.

Брокер, очередь или топик

Можно показать Kafka или RabbitMQ целиком, если очереди и топики неважны для этой схемы.

Если необходимо раскрыть каналы обмена, лучше показать отдельные очереди и топики — именно этот вариант рекомендует автор C4.

Пример 1 — брокер целиком:
broker = container "Брокер" "Обеспечивает асинхронный обмен сообщениями" "Apache Kafka" { tags "MessageBroker"}

Пример 2 — отдельный топик:
ordersEvents = container "Топик orders.events" "События о создании и изменении заказов" "Apache Kafka" {
tags "QueueOrTopic"
}

Стили:

element "MessageBroker" { shape Pipe }


element "QueueOrTopic" { shape Pipe }

Название Container-диаграммы

Создаёт Container-диаграмму интернет-магазина и показывает его Frontend, Backend, базы данных, брокер и связанные внешние системы.

container shop "Containers" { include * autoLayout lr title "C4 / Container — Интернет-магазин" description "Приложения и хранилища интернет-магазина"}

C4/Component

Элемент

Как использовать

Код Structurizr

Граница контейнера (Container Boundary)

Граница объединяет все части кода, которые относятся к сервису заказов.

Сам сервис является контейнером, а контроллеры, сервисы и репозитории внутри него — компонентами.

Пример:
orderService = container "Сервис заказов" "Управляет заказами" "Java, Spring Boot" { // Здесь объявляются компоненты}

Граница не создаётся отдельной командой. Structurizr автоматически показывает её вокруг компонентов, вложенных в контейнер.

Компонент (Component)

Компонентом может быть контроллер, сервис с бизнес-логикой, репозиторий, модуль авторизации, клиент внешней системы или обработчик событий из брокера. Это модуль кода.

Компонент работает внутри контейнера и не разворачивается отдельно.

Пример:
ordersController = component "Контроллер заказов" "Принимает и проверяет API-запросы" "Spring REST Controller"

orderManagement = component "Сервис управления заказами" "Выполняет бизнес-логику создания и изменения заказов" "Spring Service"

ordersRepository = component "Репозиторий заказов" "Сохраняет и получает заказы из БД" "Spring Data Repository"

Первое значение — идентификатор компонента в коде. Далее указываются его название, ответственность и технология реализации.

Стиль:
element "Component" {
shape RoundedBox
background #5db7e8
color #ffffff
}

Название Component-диаграммы

Создаёт диаграмму внутреннего устройства сервиса заказов: контроллер, бизнес-сервис, репозиторий, интеграционные клиенты и обработчики событий.

Пример:
component shop.orderService "OrderServiceComponents" { include * autoLayout lr title "C4 / Component — Сервис заказов" description "Внутреннее устройство сервиса заказов"}

shop.orderService — контейнер, устройство которого раскрывается.


OrderServiceComponents — уникальный ключ диаграммы.
include * — добавляет компоненты и непосредственно связанные с ними элементы.

Пример полной структуры в model:

model {
    shop = softwareSystem "Интернет-магазин" {
        orderService = container "Сервис заказов" "Управляет заказами" "Java, Spring Boot" {
            ordersController = component "Контроллер заказов" "Принимает и проверяет API-запросы" "Spring REST Controller"
            orderManagement = component "Сервис управления заказами" "Выполняет бизнес-логику работы с заказами" "Spring Service"
            ordersRepository = component "Репозиторий заказов" "Работает с данными заказов" "Spring Data Repository"
            paymentClient = component "Клиент платёжной системы" "Вызывает API платёжного сервиса" "REST Client"
            eventsHandler = component "Обработчик событий заказов" "Обрабатывает сообщения из брокера" "Kafka Consumer"
        }
    }
}

Общие правила оформления

  1. У каждого элемента должны быть понятные название, тип и краткое описание.

  2. Для контейнеров и компонентов указывают технологии реализации.

  3. Каждая стрелка показывает однонаправленную связь. На ней нужно указать назначение взаимодействия, а для связей между контейнерами — протокол или технологию.


Как создать C4 с нуля

Если вы только осваиваете C4-модель, рекомендую начать с нашего обучающего видео-подкаста на YouTube. В нем я пошагово разбираю уровни модели и на примерах демонстрирую, как строить архитектурные диаграммы.

В этой статье я покажу примеры C4-диаграмм для двух проектов из этого видео:

  • платформу аренды недвижимости,

  • систему интернет-магазина.

Я покажу готовые схемы, созданные в draw.io в ходе обучающего подкаста, и затем их код для Structurizr DSL. Этот код можно будет использовать в качестве шаблона для своих проектов и передавать нейросетям (ИИ) как пример, чтобы они помогали строить и обновлять архитектурные диаграммы.

Нотация C4 за 90 минут: как проектировать архитектуру на примере реальной задачи
https://t.me/getanalysts/3364

C4 / Context

Цель: наглядно видеть пользователей и интеграции системы.

Контекстная диаграмма представляет систему с точки зрения ее окружения, то есть что именно мы разрабатываем, кто будет пользоваться нашей системой и с какими внешними системами наша система будет взаимодействовать.

На этом уровне система рассматривается как единое целое — без Frontend, Backend, микросервисов, баз данных и других внутренних деталей.

На схеме показываются:

  • моделируемая система;

  • пользователи и их роли;

  • внешние системы;

  • основные связи между ними.

Пример C4 / Context для Интернет-магазина (draw.io):

Пример C4 / Context для Интернет-магазина
Пример C4 / Context для Интернет-магазина

Код C4 / Context для Интернет-магазина:

Код C4 / Context для Интернет-магазина
workspace "Интернет-магазин — C4 / Context" "Контекстная диаграмма интернет-магазина" {

    !identifiers hierarchical
    !impliedRelationships false

    model {
        registeredCustomer = person "Покупатель" "Авторизованный пользователь. Покупает товары в интернет-магазине."

        guestCustomer = person "Гость" "Неавторизованный пользователь. Просматривает каталог и покупает товары." {
            tags "External"
        }

        administrator = person "Администратор" "Управляет системой, работает с отчётами, регистрирует поставки и контролирует остатки товаров."

        supportAgent = person "Сотрудник техподдержки" "Помогает пользователям при возникновении сложностей в работе с системой."

        shop = softwareSystem "Интернет-магазин" "Система для продажи товаров онлайн."

        unisender = softwareSystem "Unisender" "Сервис отправки email- и SMS-сообщений." {
            tags "External"
        }

        firebase = softwareSystem "Firebase" "Сервис отправки push-уведомлений." {
            tags "External"
        }

        raifPay = softwareSystem "RaifPay" "Интернет-эквайринг для приёма онлайн-платежей." {
            tags "External"
        }

        cdek = softwareSystem "СДЭК" "Система доставки товаров покупателям и формирования данных для складской обработки отправлений." {
            tags "External"
        }

        registeredCustomer -> shop "Использует"
        guestCustomer -> shop "Использует"
        administrator -> shop "Использует"
        supportAgent -> shop "Использует"

        shop -> unisender "Вызывает API для отправки email и SMS" "JSON/HTTPS (REST API)"
        shop -> firebase "Вызывает API для отправки push-уведомлений" "JSON/HTTPS (REST API)"
        shop -> raifPay "Вызывает API для проведения платежей" "JSON/HTTPS (REST API)"
        shop -> cdek "Вызывает API для оформления доставки" "JSON/HTTPS (REST API)"
    }

    views {
        systemContext shop "SystemContext" "C4 / Context — Интернет-магазин" {
            include *
            autoLayout lr 300 200
            title "C4 / Context — Интернет-магазин"
        }

        theme default

        styles {
            element "Person" {
                shape Person
                background #08427b
                color #ffffff
                stroke #06345f
                fontSize 22
            }

            element "Software System" {
                shape RoundedBox
                background #1168bd
                color #ffffff
                stroke #0b4f91
                fontSize 22
            }

            element "External" {
                background #8c8596
                color #ffffff
                stroke #6c6577
                border solid
            }

            relationship "Relationship" {
                color #707070
                thickness 2
                routing Orthogonal
                jump true
                fontSize 18
            }
        }
    }
}
Пример кода для C4 / Context в Structurizr для Интернет-магазина
Пример кода для C4 / Context в Structurizr для Интернет-магазина

Вопросы и ответы по C4 / Context

>> Что делать, если внутри одной компании есть две системы, за которые отвечают разные подразделения и которые фактически являются отдельными продуктами экосистемы?

В начале необходимо выяснить, какая система находится в центре внимания данной диаграммы. На классической диаграмме C4 / Context отображается одна система. В нашей цветовой палитре она обозначена темно-синим прямоугольником.

Если же речь идет о второй системе, то даже если она относится к той же фирме, то в рамках выбранного контекста она будет представлена как внешняя (серая). Затем для этой второй системы будут созданы отдельные диаграммы Context и Container. При этом на ее собственной контекстной диаграмме она станет главной и будет показана синим, а первая система — внешней.

Если же необходимо продемонстрировать несколько равных систем в рамках одной экосистемы, то следует использовать System Landscape Diagram от C4 — карту систем организации без акцента на одной из них. Официально областью System Context является одна программная система, а System Landscape предназначена исключительно для нескольких систем организации или подразделения.

>> Каких пользователей показывать серыми?

В нашей легенде синим цветом обозначены пользователи, которые имеют дело с приложениями нашей системы, которые моделируются. Пользователи могут быть не только сотрудниками организации, но и её клиентами, а также сотрудниками других организаций. Главное, что они применяют наше web-, mobile- или другое клиентское приложение.

Пользователь, который взаимодействует с интерфейсами внешних систем, но не взаимодействует с нашими приложениями, может быть обозначен серым цветом. Неавторизованные пользователи могут быть также обозначены серым цветом, если это поможет визуально отделить их от авторизованных пользователей.

В то же время, цвет — это дополнительное правило, а не обязательное требование C4. Его значение должно быть объяснено в легенде диаграммы.

>> Типичная ошибка новичка: внешняя система показывается фигурой пользователя.
Внешнюю систему нельзя обозначать фигурой пользователя. Пользователь всегда остаётся Person, а система — Software System, независимо от выбранного цвета.

Дополнительный пример C4 / Context для Платформы Аренды Недвижимости (draw.io):

Пример C4 / Context для платформы аренды недвижимости
Пример C4 / Context для платформы аренды недвижимости

C4 / Container

Цель: показать карту приложений системы и их взаимодействия как внутренние, так и внешние.

Диаграмма C4 / Container детализирует систему с предыдущего уровня и демонстрирует внутреннюю архитектуру системы: какие приложения и хранилища входят в систему, за что они отвечают, какие технологии используют и как взаимодействуют между собой.

Важно не путать C4-контейнер с Docker-контейнером. В C4 контейнер — это самостоятельная среда выполнения приложения или хранения данных. Приложение-контейнер, как правило, можно отдельно запустить и развернуть.

Отдельный репозиторий, команда или релизный цикл — хорошие дополнительные признаки контейнера, но не обязательные. Например, два приложения могут храниться в одном монорепозитории, но запускаться как отдельные процессы — тогда это два контейнера. И наоборот: библиотека может находиться в отдельном репозитории, но выполняться внутри Backend-приложения — тогда это не контейнер.

Если просто: отдельно запускаемое и исполняемое приложение = отдельный контейнер.

На Container-диаграмме могут быть показаны:

  • веб-приложение;

  • монолитный Backend;

  • клиентское веб-приложение: Single Page Application или PWA;

  • мобильное приложение;

  • Desktop-приложение;

  • отдельный сервис или микросервис;

  • API Gateway;

  • Worker или фоновый обработчик, если это самостоятельно работающее приложение;

  • отдельно запускаемый консольный скрипт;

  • БД;

  • файловое или объектное хранилище;

  • кэш;

  • брокер или очередь / топик брокера сообщений.

Пример C4 / Container для Интернет-магазина (draw.io):

Пример C4 / Container с монолитным Backend-приложением для интернет-магазина
Пример C4 / Container с монолитным Backend-приложением для интернет-магазина
Пример C4 / Container с микросервисным Backend для интернет-магазина
Пример C4 / Container с микросервисным Backend для интернет-магазина

Код C4 / Container для Интернет-магазина с микросервисной архитектурой:

Код C4 / Container для Интернет-магазина с микросервисной архитектурой
workspace "Интернет-магазин — C4 / Container" "Контейнерная диаграмма микросервисной архитектуры интернет-магазина" {

    !identifiers hierarchical
    !impliedRelationships false

    model {
        registeredCustomer = person "Покупатель" "Авторизованный пользователь. Покупает товары в интернет-магазине."

        guestCustomer = person "Гость" "Неавторизованный пользователь. Просматривает каталог и покупает товары." {
            tags "External"
        }

        administrator = person "Администратор" "Управляет системой, работает с отчётами, регистрирует поставки и контролирует остатки товаров."

        supportAgent = person "Сотрудник техподдержки" "Помогает пользователям при возникновении сложностей в работе с системой."

        shop = softwareSystem "Интернет-магазин" "Система для продажи товаров онлайн." {

            iosApp = container "iOS Покупателя" "Авторизация, просмотр каталога, работа с корзиной и оформление заказов." "Swift" {
                tags "MobileApp"
            }

            androidApp = container "Android Покупателя" "Авторизация, просмотр каталога, работа с корзиной и оформление заказов." "Kotlin" {
                tags "MobileApp"
            }

            webApp = container "Web-Покупателя" "Авторизация, просмотр каталога, работа с корзиной и оформление заказов." "JavaScript" {
                tags "WebApp"
            }

            adminApp = container "Админка" "Работа с отчётами, товарами, поставками, пользователями и обращениями." "JavaScript" {
                tags "WebApp"
            }

            apiGateway = container "API Gateway" "Единая точка входа. Проверяет и маршрутизирует запросы к внутренним сервисам." "Kong" {
                tags "Gateway"
            }

            authService = container "Сервис аутентификации и авторизации" "Аутентифицирует пользователей, проверяет права доступа и управляет токенами." "Java, Spring Boot"

            authDb = container "БД аутентификации" "Хранит учётные данные и параметры аутентификации пользователей." "PostgreSQL" {
                tags "Database"
            }

            paymentService = container "Сервис платежей" "Создаёт платежи, обрабатывает их статусы и возвраты." "Java, Spring Boot"

            paymentDb = container "БД платежей" "Хранит платежи, статусы операций и данные возвратов." "PostgreSQL" {
                tags "Database"
            }

            catalogService = container "Сервис каталога товаров" "Управляет товарами, категориями, ценами и остатками." "Java, Spring Boot"

            catalogDb = container "БД каталога товаров" "Хранит карточки товаров, категории, цены и остатки." "PostgreSQL" {
                tags "Database"
            }

            searchService = container "Сервис поиска товаров" "Выполняет полнотекстовый поиск и фильтрацию товаров." "Java, Spring Boot"

            searchIndex = container "Поисковый индекс товаров" "Хранит поисковый индекс каталога товаров." "Elasticsearch" {
                tags "Database"
            }

            cartService = container "Сервис корзины" "Управляет составом и состоянием корзины покупателя." "Java, Spring Boot"

            cartDb = container "БД корзины" "Хранит корзины покупателей и добавленные товары." "PostgreSQL" {
                tags "Database"
            }

            deliveryService = container "Сервис доставки" "Оформляет доставку и отслеживает статусы отправлений." "Java, Spring Boot"

            deliveryDb = container "БД доставки" "Хранит данные отправлений и историю изменения их статусов." "PostgreSQL" {
                tags "Database"
            }

            loyaltyService = container "Сервис лояльности" "Управляет скидками, бонусами и персональными предложениями." "Java, Spring Boot"

            loyaltyDb = container "БД лояльности" "Хранит бонусные счета, скидки и историю начислений." "PostgreSQL" {
                tags "Database"
            }

            supportService = container "Сервис техподдержки" "Управляет обращениями и чатом покупателей с техподдержкой." "Java, Spring Boot"

            supportDb = container "БД чата и поддержки" "Хранит обращения, сообщения и статусы обработки." "PostgreSQL" {
                tags "Database"
            }

            supportFiles = container "Файловое хранилище чата" "Хранит файлы, прикреплённые к обращениям и сообщениям." "Amazon S3" {
                tags "FileStorage"
            }

            userService = container "Сервис управления пользователями" "Управляет профилями, контактными данными и настройками пользователей." "Java, Spring Boot"

            userDb = container "БД пользователей" "Хранит профили и настройки пользователей." "PostgreSQL" {
                tags "Database"
            }

            userFiles = container "Файловое хранилище пользователей" "Хранит аватары и документы пользователей." "Amazon S3" {
                tags "FileStorage"
            }

            notificationService = container "Сервис уведомлений" "Формирует и отправляет email-, SMS- и push-уведомления." "Java, Spring Boot"

            notificationDb = container "БД уведомлений" "Хранит шаблоны, историю и статусы отправки уведомлений." "PostgreSQL" {
                tags "Database"
            }

            broker = container "Брокер" "Обеспечивает асинхронный обмен событиями между сервисами." "Apache Kafka" {
                tags "MessageBroker"
            }
        }

        unisender = softwareSystem "Unisender" "Сервис отправки email- и SMS-сообщений." {
            tags "External"
        }

        firebase = softwareSystem "Firebase" "Сервис отправки push-уведомлений." {
            tags "External"
        }

        raifPay = softwareSystem "RaifPay" "Интернет-эквайринг для приёма онлайн-платежей." {
            tags "External"
        }

        cdek = softwareSystem "СДЭК" "Система оформления и отслеживания доставки товаров покупателям." {
            tags "External"
        }

        registeredCustomer -> shop.iosApp "Использует"
        registeredCustomer -> shop.androidApp "Использует"
        registeredCustomer -> shop.webApp "Использует"
        guestCustomer -> shop.webApp "Использует"
        administrator -> shop.adminApp "Использует"
        supportAgent -> shop.adminApp "Использует"

        shop.iosApp -> shop.apiGateway "Вызывает API" "JSON/HTTPS (REST API)"
        shop.androidApp -> shop.apiGateway "Вызывает API" "JSON/HTTPS (REST API)"
        shop.webApp -> shop.apiGateway "Вызывает API" "JSON/HTTPS (REST API)"
        shop.adminApp -> shop.apiGateway "Вызывает API" "JSON/HTTPS (REST API)"

        shop.iosApp -> raifPay "Отображает платёжную форму" "HTTPS"
        shop.androidApp -> raifPay "Отображает платёжную форму" "HTTPS"
        shop.webApp -> raifPay "Отображает платёжную форму" "HTTPS"

        shop.apiGateway -> shop.authService "Вызывает API" "JSON/HTTPS (REST API)"
        shop.apiGateway -> shop.paymentService "Вызывает API" "JSON/HTTPS (REST API)"
        shop.apiGateway -> shop.catalogService "Вызывает API" "JSON/HTTPS (REST API)"
        shop.apiGateway -> shop.searchService "Вызывает API" "JSON/HTTPS (REST API)"
        shop.apiGateway -> shop.cartService "Вызывает API" "JSON/HTTPS (REST API)"
        shop.apiGateway -> shop.deliveryService "Вызывает API" "JSON/HTTPS (REST API)"
        shop.apiGateway -> shop.loyaltyService "Вызывает API" "JSON/HTTPS (REST API)"
        shop.apiGateway -> shop.supportService "Вызывает API" "JSON/HTTPS (REST API)"
        shop.apiGateway -> shop.userService "Вызывает API" "JSON/HTTPS (REST API)"

        shop.authService -> shop.authDb "Читает и пишет данные" "SQL/TCP"
        shop.paymentService -> shop.paymentDb "Читает и пишет данные" "SQL/TCP"
        shop.catalogService -> shop.catalogDb "Читает и пишет данные" "SQL/TCP"
        shop.searchService -> shop.searchIndex "Читает и пишет данные" "JSON/HTTPS"
        shop.cartService -> shop.cartDb "Читает и пишет данные" "SQL/TCP"
        shop.deliveryService -> shop.deliveryDb "Читает и пишет данные" "SQL/TCP"
        shop.loyaltyService -> shop.loyaltyDb "Читает и пишет данные" "SQL/TCP"
        shop.supportService -> shop.supportDb "Читает и пишет данные" "SQL/TCP"
        shop.supportService -> shop.supportFiles "Читает и пишет файлы" "HTTPS"
        shop.userService -> shop.userDb "Читает и пишет данные" "SQL/TCP"
        shop.userService -> shop.userFiles "Читает и пишет файлы" "HTTPS"
        shop.notificationService -> shop.notificationDb "Читает и пишет данные" "SQL/TCP"

        shop.cartService -> shop.catalogService "Получает данные о товарах и ценах" "JSON/HTTPS (REST API)"

        shop.paymentService -> raifPay "Создаёт платежи и возвраты" "JSON/HTTPS (REST API)"
        raifPay -> shop.paymentService "Передаёт статусы платежей" "Webhook/JSON/HTTPS"

        shop.deliveryService -> cdek "Создаёт отправления" "JSON/HTTPS (REST API)"
        cdek -> shop.deliveryService "Передаёт статусы доставки" "Webhook/JSON/HTTPS"

        shop.notificationService -> unisender "Отправляет email и SMS" "JSON/HTTPS (REST API)"
        shop.notificationService -> firebase "Отправляет push-уведомления" "Firebase Admin SDK/HTTPS"

        shop.paymentService -> shop.broker "Публикует события платежей" "Kafka Protocol"
        shop.catalogService -> shop.broker "Публикует события каталога" "Kafka Protocol"
        shop.deliveryService -> shop.broker "Публикует события доставки" "Kafka Protocol"
        shop.loyaltyService -> shop.broker "Публикует события лояльности" "Kafka Protocol"
        shop.userService -> shop.broker "Публикует события пользователей" "Kafka Protocol"
        shop.searchService -> shop.broker "Читает события каталога" "Kafka Protocol"
        shop.notificationService -> shop.broker "Читает события для отправки уведомлений" "Kafka Protocol"
    }

    views {
        container shop "Containers" "C4 / Container — Интернет-магазин — микросервисы" {
            include *
            autoLayout lr 400 180
            title "C4 / Container — Интернет-магазин — микросервисы"
        }

        theme default

        styles {
            element "Person" {
                shape Person
                background #08427b
                color #ffffff
                stroke #06345f
                fontSize 20
            }

            element "Software System" {
                shape RoundedBox
                background #1168bd
                color #ffffff
                stroke #0b4f91
                fontSize 20
            }

            element "External" {
                background #8c8596
                color #ffffff
                stroke #6c6577
                border solid
            }

            element "Container" {
                shape RoundedBox
                background #28a4d9
                color #ffffff
                stroke #1f8fbd
                fontSize 19
            }

            element "WebApp" {
                shape WebBrowser
                width 500
                height 300
            }

            element "MobileApp" {
                shape MobileDevicePortrait
                width 320
                height 500
            }

            element "Gateway" {
                shape Hexagon
                width 420
                height 340
            }

            element "Database" {
                shape Cylinder
                width 460
                height 280
            }

            element "FileStorage" {
                shape Cylinder
                width 460
                height 280
            }

            element "MessageBroker" {
                background #FFC0CB
                color #000000
                shape Pipe
                width 500
                height 300
            }

            relationship "Relationship" {
                color #707070
                thickness 2
                routing Orthogonal
                jump true
                fontSize 16
                width 300
            }
        }
    }
}
Пример кода для C4 / Container в Structurizr для Интернет-магазина с микросервисной архитектурой
Пример кода для C4 / Container в Structurizr для Интернет-магазина с микросервисной архитектурой

Вопросы и ответы по C4 / Container

>> Worker — контейнер или компонент?

  • Если Worker работает как самостоятельное приложение или процесс, отдельно запускается, разворачивается и масштабируется, то его следует изображать как отдельный контейнер.

  • Если же обработчик сообщений работает внутри основного Backend-приложения и разворачивается вместе с ним, то это не отдельный контейнер, а компонент этого Backend-приложения.

  • Аналогичное правило применимо к Consumer, Scheduler и Batch-задачам: важно не то, какую они выполняют функцию, а наличие у них собственной runtime-границы.

>> Что не является контейнером?

Контейнерами обычно не являются:

  • классы, пакеты и модули кода;

  • библиотеки, JAR, DLL и другие сборки;

  • контроллеры, сервисы и репозитории внутри приложения;

  • обычные виджеты и UI-компоненты;

  • копии и реплики одного приложения;

  • серверы, виртуальные машины, Kubernetes Pods и Nodes.

Последние относятся уже к физическому развёртыванию системы и показываются на Deployment-диаграмме. Docker-контейнер также не обязательно соответствует C4-контейнеру: это разные уровни абстракции.

При этом отдельную схему БД можно показать как отдельный контейнер, если это помогает в понимании архитектуры.

>> Монолит и микросервисы (выше оба примера)

Container-диаграммы монолитной и микросервисной системы будут заметно различаться.

В монолитной архитектуре Backend обычно показывается одним контейнером. Его внутренние контроллеры, сервисы, репозитории и модули затем можно раскрыть на уровне C4 / Component.

В микросервисной архитектуре каждый независимо работающий сервис представлен отдельным контейнером. На этом же уровне отображаются его базы данных, очереди, топики, Gateway, Worker-приложения и другие важные компоненты архитектуры.

>> Можно ли показать весь сервисный / микросервисный Backend одним прямоугольником, а далее детализировать его на диаграмме компонентов? Т.е. компоненты будут сервисами и микросервисами

По нотации — нет, нельзя.

Если очень хочется и вы готовы добавить пояснения к картинке — можно.

Компоненты — это части кода внутри одного приложения. Микросервисы — самостоятельно работающие приложения, поэтому они должны быть показаны только на уровне Container.

Если микросервисов слишком много, лучше создать несколько сфокусированных Container-диаграмм, чем переносить их на уровень Component. Формы и цвета элементов можно выбирать самостоятельно.

Дополнительный пример C4 / Container для Платформы Аренды Недвижимости (draw.io):

Пример C4 / Container для платформы аренды недвижимости
Пример C4 / Container для платформы аренды недвижимости

C4 / Component

Диаграмма C4 / Component раскрывает внутреннее устройство одного контейнера и показывает основные части его кода: за что они отвечают, с какими компонентами взаимодействуют и какие технологии используются для их реализации.

Компонент — это логическая группа связанной функциональности, модуль кода. Он работает внутри своего контейнера, в том же процессе, и не запускается, не масштабируется и не разворачивается самостоятельно.

Компонентами могут быть:

  • контроллер или группа контроллеров, принимающих API-запросы;

  • сервис с бизнес-логикой;

  • модуль авторизации внутри сервиса;

  • репозиторий или другой слой доступа к данным;

  • клиент или адаптер для работы с внешней системой;

  • обработчик сообщений из очереди или топика;

  • внутренний планировщик задач;

  • модуль формирования уведомлений;

  • функциональный модуль Frontend-приложения;

  • API-клиент, хранилище состояния или модуль авторизации на Frontend.

Не нужно показывать каждый класс, метод, кнопку, виджет или вспомогательную функцию. На этом уровне оставляют только те части кода, которые важны для понимания архитектуры контейнера.

Типичная ошибка — сначала показать весь Backend как один контейнер, а затем раскрыть самостоятельно разворачиваемые микросервисы как его компоненты. Это смешение уровней: микросервисы должны быть показаны на Container-диаграмме.

Уровень Component необязателен. Его стоит использовать для сложных или важных приложений, когда Container-диаграммы уже недостаточно для понимания внутренней архитектуры. Поскольку структура кода меняется чаще, такие схемы необходимо регулярно обновлять или по возможности генерировать автоматически.

Пример C4 / Container для Сервиса Уведомлений в системе Интернет-магазина:

Пример кода Structurizr C4 / Container для Сервиса Уведомлений
Пример кода Structurizr C4 / Container для Сервиса Уведомлений
Пример кода Structurizr для C4 / Container Сервиса Уведомлений в системе Интернет-магазина
workspace "Интернет-магазин — C4 / Component" "Компонентная диаграмма сервиса уведомлений" {

    !identifiers hierarchical
    !impliedRelationships false

    model {
        shop = softwareSystem "Интернет-магазин" "Система для продажи товаров онлайн." {

            apiGateway = container "API Gateway" "Единая точка входа и маршрутизации запросов." "Kong" {
                tags "Gateway"
            }

            broker = container "Брокер" "Передаёт события между сервисами интернет-магазина." "Apache Kafka" {
                tags "MessageBroker"
            }

            notificationService = container "Сервис уведомлений" "Формирует и отправляет email-, SMS- и push-уведомления." "Java, Spring Boot" {

                apiController = component "API уведомлений" "Принимает команды на отправку уведомлений и запросы истории." "Spring REST Controller" {
                    tags "Controller"
                }

                eventHandler = component "Обработчик событий" "Получает события из Kafka и передаёт их в бизнес-логику уведомлений." "Spring Kafka Listener" {
                    tags "EventHandler"
                }

                notificationManager = component "Управление уведомлениями" "Определяет канал доставки и управляет процессом отправки уведомления." "Spring Service" {
                    tags "BusinessLogic"
                }

                templateRenderer = component "Формирование сообщений" "Выбирает шаблон и подставляет данные получателя и события." "Spring Service" {
                    tags "BusinessLogic"
                }

                notificationRepository = component "Репозиторий уведомлений" "Сохраняет историю и статусы отправки уведомлений." "Spring Data JPA" {
                    tags "Repository"
                }

                unisenderClient = component "Клиент Unisender" "Отправляет email и SMS через API Unisender." "REST Client" {
                    tags "Integration"
                }

                firebaseClient = component "Клиент Firebase" "Отправляет push-уведомления через Firebase." "Firebase Admin SDK" {
                    tags "Integration"
                }
            }

            notificationDb = container "БД уведомлений" "Хранит шаблоны, историю и статусы отправки уведомлений." "PostgreSQL" {
                tags "Database"
            }
        }

        unisender = softwareSystem "Unisender" "Сервис отправки email- и SMS-сообщений." {
            tags "External"
        }

        firebase = softwareSystem "Firebase" "Сервис отправки push-уведомлений." {
            tags "External"
        }

        shop.apiGateway -> shop.notificationService.apiController "Вызывает API" "JSON/HTTPS (REST API)"
        shop.notificationService.apiController -> shop.notificationService.notificationManager "Передаёт команду на отправку"

        shop.notificationService.eventHandler -> shop.broker "Читает события" "Kafka Protocol"
        shop.notificationService.eventHandler -> shop.notificationService.notificationManager "Передаёт данные события"

        shop.notificationService.notificationManager -> shop.notificationService.templateRenderer "Формирует сообщение"
        shop.notificationService.notificationManager -> shop.notificationService.notificationRepository "Сохраняет статус отправки"
        shop.notificationService.notificationManager -> shop.notificationService.unisenderClient "Отправляет email или SMS"
        shop.notificationService.notificationManager -> shop.notificationService.firebaseClient "Отправляет push-уведомление"

        shop.notificationService.notificationRepository -> shop.notificationDb "Читает и пишет данные" "SQL/TCP"
        shop.notificationService.unisenderClient -> unisender "Вызывает API" "JSON/HTTPS (REST API)"
        shop.notificationService.firebaseClient -> firebase "Отправляет push-уведомление" "Firebase Admin SDK/HTTPS"
    }

    views {
        component shop.notificationService "Components" "C4 / Component — Сервис уведомлений" {
            include *
            autoLayout lr 350 180
            title "C4 / Component — Интернет-магазин — Сервис уведомлений"
        }

        theme default

        styles {
            element "Software System" {
                shape RoundedBox
                background #1168bd
                color #ffffff
                stroke #0b4f91
                fontSize 20
            }

            element "External" {
                background #8c8596
                color #ffffff
                stroke #6c6577
                border solid
            }

            element "Container" {
                shape RoundedBox
                background #28a4d9
                color #ffffff
                stroke #1f8fbd
                fontSize 19
            }

            element "Gateway" {
                shape Hexagon
                width 420
                height 340
            }

            element "Database" {
                shape Cylinder
                width 460
                height 280
            }

            element "MessageBroker" {
                shape Pipe
                width 500
                height 300
            }

            element "Component" {
                shape Component
                background #5cb9e1
                color #ffffff
                stroke #1f8fbd
                width 470
                height 260
                fontSize 19
            }

            element "Controller" {
                background #28a4d9
            }

            element "EventHandler" {
                background #74bcde
                color #10384a
            }

            element "BusinessLogic" {
                background #4bb0dc
            }

            element "Repository" {
                background #1f9dd6
            }

            element "Integration" {
                background #3aa9d8
            }

            relationship "Relationship" {
                color #707070
                thickness 2
                routing Orthogonal
                jump true
                fontSize 17
                width 280
            }
        }
    }
}

C4 / Code

Не разбираю этот уровень, о чем писала при обзоре элементов нотации.

Это могут быть ER-диаграммы БД или диаграммы классов UML.

Полезные ссылки:

Инструменты для создания диаграмм C4

Полный обзор инструментов я делала в статье «Нотация моделирования архитектуры С4 — примеры диаграмм и инструменты».

Здесь оставляю список с моими реальными впечатлениями и опытом:

  • Draw.io — мой основной выбор. Бесплатный, удобный и отлично подходит для ручной отрисовки C4. Использую постоянно.

  • Miro — не рекомендую: без специальных шаблонов нет нормального набора элементов C4, а работать с ними в итоге не очень удобно.

  • Microsoft Visio — не использую. При наличии Draw.io не вижу причин начинать.

  • Structurizr — использую постоянно, особенно сейчас, когда код диаграмм удобно генерировать с помощью ИИ. Позволяет хранить все уровни C4 в одной модели, увеличивать схемы и отслеживать связи. На больших диаграммах визуал и автоматическое расположение элементов иногда неудобны.

  • PlantUML — позволяет строить C4 через код, в том числе с помощью ИИ, но визуально мне не нравится, поэтому не использую.

  • MermaidChart — позволяет строить C4 через код. Он удобен, но из-за лимитов по тарифу и привычки работы в Structurizr почти не трогаю его.

Как создать C4-диаграмму с помощью нейросетей (ИИ)

Используйте любую нейросеть, которая вам нравится. Главное — хороший промпт и/или настроенный с ним AI-скилл.

Для визуализации кода: https://playground.structurizr.com/

Работай как опытный системный архитектор с опытом проектирования enterprise-решений и систем уровня Big Tech. Ты на экспертном уровне владеешь моделью C4 Саймона Брауна и создал сотни архитектурных диаграмм с помощью Structurizr DSL.
Основной источник истины — официальный сайт модели C4: https://c4model.com. Для правил оформления и практических рекомендаций используй статью Екатерины Ананьевой: <сюда вставьте ссылку на статью>. Если между источниками возникнет противоречие, приоритет имеет официальный сайт C4.

Твоя задача:
Создать диаграмму системы <название системы> на уровне C4 / <название уровня> и предоставить полный код Structurizr DSL.

Описание системы:
<Включите голосовой ввод и расскажите ИИ, что именно хотите показать на диаграмме.
Не знаете, с чего начать? Укажите хотя бы:
+ список приложений и хранилищ системы;
+ пользователей и их роли;
+ внешние системы;
+ основные взаимодействия;
+ тип Backend: монолит, сервисы, микросервисы или другой вариант.
Даже при подробном описании воспринимайте полученный результат как черновик архитектуры, который необходимо проверить.>
Если информации недостаточно или возможны разные архитектурные решения, сначала задай уточняющие вопросы. Не придумывай отсутствующие требования самостоятельно.

Пример кода Structurizr для C4 / <название уровня>:
<Сюда вставьте подходящий пример кода Structurizr из этой статьи>

Перед выдачей результата проверь корректность синтаксиса и убедись, что код запускается в Structurizr: https://playground.structurizr.com/.
Сначала предоставь полный готовый код одним блоком, а после него кратко перечисли принятые допущения и решения, которые необходимо дополнительно проверить.

А ещё в одном файле Structurizr можно организовать сразу все три уровня C4.
Но хотя бы что-то в этой статье я всё-таки не расскажу — оставлю это вам для самостоятельного изучения 😉

Подборка примеров и проектов

Можно изучать и использовать как ориентир для своих проектов. В подборке есть и монолиты, и микросервисные системы с брокерами.

🔗 RideFlow — заказ такси
🔗 TelMed — телемедицина
🔗 BookingGA — сервис аренды недвижимости
🔗 GreenChargeGA  — зарядки для электроавто
🔗 CityGA — поиск мероприятий в городе
🔗 AdFlowGA — рекламный сервис
🔗 Пример архитектуры C4 в Miro

Все эти ссылки и дополнительные материалы по C4 можно найти в посте TG или посте VK.

Также у меня в базе знаний есть задача с собеседования с максимально подробным разбором ошибок, которые допускают в C4:

  • Условие задачи на нахождение ошибок в готовой C4: TG / VK

  • Решение этой задачи: TG / VK

Заключение

Надеюсь, этот гайд оказался для вас действительно полезным: я попыталась подробно разобрать все уровни C4, привести практические примеры и указать на типичные ошибки.

Если я что-то упустила или у вас остались вопросы, пишите в комментариях — постараюсь на всё ответить.

А в качестве благодарности за эту большую работу буду искренне рада, если вы подпишетесь на GetAnalyst — моё сообщество для системных и бизнес-аналитиков — в Telegram, ВК или MAX.

Спасибо за внимание — и понятных вам архитектурных схем! 💙