Модель C4 в Structurizr: шаблоны для системного аналитика

Привет Хабр! Меня зовут Татьяна Ошуркова, я разработчик, аналитик и автор телеграм-канала IT Talks. Большой объем документации и её классическое представление сегодня нередко уходят на второй план, а популярность подхода «Документация как код» (Docs as Code) растет с каждым днем. Поэтому сейчас особенно актуально использование инструментов для текстового описания различных диаграмм.
Одним из таких инструментов для моделирования архитектуры программного обеспечения является Structurizr. В этой статье я разберу построение диаграмм модели C4 с использованием Structurizr и дам их исходное описание.
Что такое Structurizr?
В моем опыте модель C4 по началу казалась мне крайне сложной. Для начинающего системного аналитика действительно непростым может показаться построение диаграмм, так как работа с архитектурой может требовать определённых углубленных навыков понимания работы системы, особенно в части интеграционного взаимодействия. Особенно сложным для новичка может быть построение диаграмм в текстовом виде, так здесь необходимо не только понимание архитектуры, модели, но также и языка описания.
На самом деле, построение различных диаграмм в текстовом формате может быть даже проще и лучше для начинающего специалиста, так как это помогает глубже погрузиться в логику и «понять» процесс построения схем. Стоит воспринимать Structurizr, PlantUML и схожие инструменты как еще один подход, а не усложнение процесса.
Structurizr — это инструмент для моделирования архитектуры программного обеспечения, основанный на C4-модели (Context, Container, Component, Code). Он позволяет создавать диаграммы архитектуры на разных уровнях детализации, используя текстовое описание на языке Structurizr DSL (Domain-Specific Language) или программный API (например, на Java, TypeScript, C#).
DSL (Domain-Specific Language) — это язык, предназначенный для решения задач в конкретной предметной области. В отличие от универсальных языков программирования, DSL упрощает работу с определенным видом данных или процессов, предоставляя удобный синтаксис и команды. В контексте Structurizr DSL — это текстовый формат, который позволяет описывать архитектурные диаграммы C4-модели. Он используется для создания схем путем текстового описания компонентов системы, их взаимодействий и визуальных представлений.
Structurizr поддерживает генерацию диаграмм не только с помощью DSL, но и через программные API для Java, TypeScript, C#, Python. Это значит, что можно интегрировать создание диаграмм в код. В Java существует официальный API Structurizr, позволяющий создавать модели архитектуры программным способом и экспортировать их в DSL, а в Python можно генерировать DSL-файлы или отправлять модели в Structurizr через API.
Постановка задачи
Рассмотрим задачу интеграции системы управления заказами с внешними API. Наша цель — построить архитектурные диаграммы, которые помогут системному аналитику и разработчикам лучше понять взаимодействие между компонентами системы.
У нас есть система управления заказами, которая взаимодействует с несколькими внешними API:
Платежный сервис (например, Stripe) для обработки платежей.
Аналитическая система для сбора и анализа данных о заказах.
CRM-система для передачи данных о клиентах и заказах.
Мы построим три диаграммы:
Общая схема интеграции — показывает все внешние взаимодействия системы.
Разбиение на контейнеры — детализация внутренней архитектуры системы.
Детализация взаимодействия с API — описание конкретного API (например, платежного сервиса).
Общая схема интеграции
Эта диаграмма соответствует уровню Context в C4-модели и предназначена для отображения всех внешних взаимодействий системы. Она помогает увидеть ключевые интеграции и их направление. Этот уровень позволяет определить границы системы и внешние зависимости, что особенно важно при проектировании архитектуры.
workspace {
model {
customer = person "Пользователь" "Оформляет заказы."
orderSystem = softwareSystem "Система управления заказами" "Обрабатывает заказы."
paymentAPI = softwareSystem "Платежный сервис" "Обрабатывает платежи."
analyticsAPI = softwareSystem "Аналитическая система" "Анализирует данные о заказах."
crmSystem = softwareSystem "CRM-система" "Управляет клиентами."
customer -> orderSystem "Оформляет заказ"
orderSystem -> paymentAPI "Отправляет платежные данные"
orderSystem -> analyticsAPI "Передает данные о заказах"
orderSystem -> crmSystem "Передает информацию о клиенте"
}
views {
systemContext orderSystem {
include *
autolayout lr
}
theme default
}
}
Определяем основные элементы: создаем пользователя (person) и систему управления заказами (softwareSystem).
Добавляем внешние API: создаем три новых softwareSystem для платежного сервиса, аналитической системы и CRM-системы.
Устанавливаем связи: определяем взаимодействие с помощью →, указывая передаваемые данные.
Создаем представление: добавляем views с systemContext, включаем все элементы, активируем autolayout lr.

Разделение на контейнеры
Эта диаграмма соответствует уровню Container в C4-модели и предназначена для детализации внутренней структуры системы. Она демонстрирует, какие контейнеры (основные программные компоненты) составляют систему, как они взаимодействуют между собой и какие внешние зависимости у них есть. В отличие от уровня Context, который фокусируется на границах системы и её внешних связях, уровень Container показывает внутреннее устройство системы, её ключевые сервисы и их взаимодействие.
workspace {
model {
orderSystem = softwareSystem "Система управления заказами" "Обрабатывает заказы." {
webApp = container "Web-приложение" "Интерфейс для клиентов." "Web Application"
apiGateway = container "API Gateway" "Обрабатывает запросы клиентов." "Server-side Application"
orderService = container "Order Service" "Управляет заказами." "Server-side Application"
analyticsService = container "Analytics Service" "Обрабатывает данные заказов." "Server-side Application"
paymentService = container "Payment Service" "Обрабатывает платежи." "Server-side Application"
}
paymentAPI = softwareSystem "Платежный сервис"
analyticsAPI = softwareSystem "Аналитическая система"
crmAPI = softwareSystem "CRM-система"
webApp -> apiGateway "Отправляет запросы"
apiGateway -> orderService "Передает запрос на заказ"
orderService -> paymentService "Вызывает оплату"
orderService -> analyticsService "Передает данные о заказах"
orderService -> crmAPI "Передает данные о клиентах"
paymentService -> paymentAPI "Отправляет платежные данные"
analyticsService -> analyticsAPI "Передает агрегированные данные"
}
views {
container orderSystem {
include *
autolayout lr
}
theme default
}
}
Определяем основную систему: создаем softwareSystem для системы управления заказами.
Разбиваем систему на контейнеры: добавляем Web-приложение, API Gateway, Order Service, Analytics Service, Payment Service и CRM Service.
Добавляем внешние API: создаем softwareSystem для платежного сервиса, аналитической системы и CRM-системы.
Определяем связи между контейнерами: указываем, как Web-приложение взаимодействует с API Gateway, как сервисы обмениваются данными, как передаются запросы в внешние API.
Создаем представление диаграммы: добавляем views с container orderSystem, включаем все элементы и активируем autolayout lr.

Детализация взаимодействия
Эта диаграмма соответствует уровню Component в C4-модели и предназначена для детализации конкретных сервисов системы. В отличие от уровня Container, который показывает основные программные блоки и их связи, уровень Component фокусируется на внутренних элементах каждого контейнера и их взаимодействиях. Эта детализация позволяет глубже понять логику работы сервисов и их интеграцию. Рассмотрим построение диаграммы на примере paymentService и orderService.
workspace {
model {
orderSystem = softwareSystem "Система управления заказами" "Обрабатывает заказы." {
orderService = container "Order Service" "Управляет заказами." "Server-side Application" {
orderController = component "Order Controller" "Обрабатывает входящие запросы"
orderProcessor = component "Order Processor" "Обрабатывает заказы"
orderRepository = component "Order Repository" "Доступ к базе заказов"
eventDispatcher = component "Event Dispatcher" "Рассылка событий"
}
paymentService = container "Payment Service" "Обрабатывает платежи." "Server-side Application" {
paymentController = component "Payment Controller" "Принимает платежные запросы"
paymentProcessor = component "Payment Processor" "Обрабатывает платежи"
paymentValidator = component "Payment Validator" "Проверяет корректность платежей"
transactionManager = component "Transaction Manager" "Управляет транзакциями"
}
orderDB = container "Order DB" "Хранит данные о заказах" "Database"
paymentDB = container "Payment DB" "Хранит данные о платежах" "Database"
}
paymentAPI = softwareSystem "Платежный сервис"
# Взаимодействие внутри Order Service
orderController -> orderProcessor "Передает заказ на обработку"
orderProcessor -> orderRepository "Запрашивает данные заказов"
orderProcessor -> eventDispatcher "Отправляет события"
orderRepository -> orderDB "Сохраняет и получает данные заказов"
# Взаимодействие внутри Payment Service
orderProcessor -> paymentController "Вызывает оплату"
paymentController -> paymentProcessor "Обрабатывает платеж"
paymentProcessor -> paymentValidator "Проверяет платеж"
paymentProcessor -> transactionManager "Создает транзакцию"
transactionManager -> paymentDB "Сохраняет данные о платежах"
transactionManager -> paymentAPI "Отправляет платежные данные"
}
views {
container orderSystem {
include *
autolayout lr
}
component orderService {
include *
autolayout lr
}
component paymentService {
include *
autolayout lr
}
theme default
}
}
Создаем рабочую область workspace с моделью системы и представлениями.
В model определяем систему orderSystem, содержащую два сервиса (Order Service и Payment Service), базы данных (Order DB и Payment DB) и внешнюю систему paymentAPI.
Внутри сервисов создаем компоненты (Controller, Processor, Repository и др.), определяя их роли и связи.
Устанавливаем взаимодействия между элементами системы: обработка заказов, работа с базой данных, вызовы платежей и интеграция с paymentAPI.
В views создаем три диаграммы: container orderSystem для контейнеров, component orderService для компонентов Order Service, component paymentService для компонентов Payment Service.
Добавляем autolayout lr для удобного расположения элементов и применяем стандартную тему.
В результате получаем три диаграммы, каждая из которых отражает разные уровни детализации системы.
Structurizr позволяет создавать несколько представлений одной и той же системы. В данном случае формируются 3 диаграммы, в том числе и диаграмма контейнеров. Это происходит из-за структуры кода в views и того, как Structurizr DSL интерпретирует вложенность элементов. Разберем подробнее:
container orderSystem создает диаграмму контейнеров. Она включает все элементы внутри orderSystem, то есть сервисы (Order Service, Payment Service), базы данных (Order DB, Payment DB) и внешний paymentAPI.
component orderService и component paymentService должны бы создавать диаграммы компонентов для каждого сервиса, но внутри них используется include *. Это приводит к тому, что в диаграмму компонентов попадает не только уровень компонентов (Controller, Processor, Repository и т. д.), но и вложенные контейнеры (если они есть).


Подведем итоги
Использование Structurizr и модели C4 позволяет эффективно документировать архитектуру программных систем, не прибегая к сложным графическим инструментам. Благодаря текстовому описанию диаграмм в DSL, можно легко вносить изменения, управлять версиями и интегрировать документацию с кодовой базой.
В данном примере мы рассмотрели построение трех диаграмм для системы управления заказами, от общей схемы контейнеров до детализации компонентов. Structurizr позволяет гибко определять представления системы, но важно учитывать вложенность элементов и корректно использовать include *, чтобы избежать неожиданных пересечений между уровнями диаграмм.
Этот подход особенно полезен для больших и распределенных систем, где требуется четкое понимание взаимодействий между компонентами. Документация как код в связке с C4-моделью помогает системным аналитикам описывать архитектуру не только для решения задач, но и для понимания системы как внутри команды, так и среди новых участников проекта.
Удачи в работе!