Модель C4

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

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

  1. System Context
  2. Container
  3. Component
  4. Code

Логика похожа на карту:

  • сначала показываем страну;
  • затем город;
  • затем конкретное здание;
  • затем комнаты внутри здания.

Главная идея C4: не пытаться изобразить всю архитектуру на одной гигантской диаграмме.


1. System Context Diagram

Что показывает

Контекстная диаграмма отвечает на вопрос:

Что это за система, кто ею пользуется и с какими внешними системами она взаимодействует?

На ней обычно присутствуют:

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

Например, для системы бронирования отелей:

[Пользователь]
      |
      v
[Система бронирования отелей]
      |
      +----> [Платёжный провайдер]
      |
      +----> [Система отеля]
      |
      +----> [Email/SMS-провайдер]

Здесь не показываются:

  • микросервисы;
  • базы данных;
  • Kafka;
  • Redis;
  • внутренние API;
  • классы;
  • конкретные технологии.

Для кого предназначена

Контекстная диаграмма понятна практически всем:

  • бизнесу;
  • менеджерам;
  • аналитикам;
  • разработчикам;
  • архитекторам;
  • новым участникам проекта.

Главный вопрос уровня

Где проходит граница нашей системы?

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


2. Container Diagram

Что такое container в C4

В C4 слово container не означает исключительно Docker-контейнер.

Container здесь — это отдельно запускаемая или развёртываемая часть системы.

Контейнером может быть:

  • backend-приложение;
  • frontend-приложение;
  • мобильное приложение;
  • микросервис;
  • база данных;
  • message broker;
  • отдельный batch-процесс;
  • сервер авторизации.

Например:

                    [Пользователь]
                           |
                           v
                 [Web Application]
                    React / TypeScript
                           |
                           v
                    [Backend API]
                 Java / Spring Boot
                    /      |       \
                   v       v        v
          [PostgreSQL]  [Redis]  [Kafka]

На этом уровне уже можно показывать технологии:

  • Java;
  • Spring Boot;
  • PostgreSQL;
  • Redis;
  • Kafka;
  • React;
  • Android;
  • Kubernetes.

Что показывает Container Diagram

Она отвечает на вопросы:

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

Например:

[Web Application]
Позволяет пользователю искать отели и создавать бронирования.

[Booking API]
Обрабатывает поиск, бронирование и отмену.

[PostgreSQL]
Хранит пользователей, бронирования и инвентарь номеров.

[Redis]
Хранит кэш результатов поиска и временные блокировки.

[Kafka]
Передаёт события BookingCreated, BookingCancelled.

Главный вопрос уровня

Из каких отдельно работающих частей состоит система?

Именно Container Diagram обычно является основной диаграммой на system design interview.

На собеседовании, когда ты рисуешь:

  • API Gateway;
  • Booking Service;
  • Payment Service;
  • PostgreSQL;
  • Redis;
  • Kafka;

ты фактически создаёшь C4 Container Diagram, даже если не называешь её этим термином.


3. Component Diagram

Что такое компонент

Component в C4 — это логически выделенная часть внутри контейнера.

Например, внутри Booking Service могут быть:

[Booking Controller]
        |
        v
[Booking Application Service]
        |
        +----> [Availability Component]
        |
        +----> [Pricing Component]
        |
        +----> [Booking Repository]
        |
        +----> [Event Publisher]

Component Diagram отвечает на вопрос:

Как устроен конкретный контейнер изнутри?

Например, контейнер:

Booking Service

может состоять из компонентов:

  • Booking API;
  • Booking Application Service;
  • Availability Checker;
  • Pricing Engine;
  • Booking Repository;
  • Payment Client;
  • Event Publisher;
  • Idempotency Handler.

Важное различие

Компонент — это не обязательно:

  • отдельный процесс;
  • отдельный микросервис;
  • отдельный deployment;
  • отдельный Docker-контейнер.

Компоненты обычно работают внутри одного приложения и одного процесса.

Например:

Booking Service
└── BookingController
└── BookingService
└── AvailabilityService
└── BookingRepository
└── OutboxPublisher

Все эти компоненты могут находиться внутри одного Spring Boot-приложения.

Главный вопрос уровня

Какие внутренние архитектурные части реализуют ответственность контейнера?


4. Code Diagram

Code Diagram показывает реализацию отдельного компонента на уровне кода.

Это могут быть:

  • классы;
  • интерфейсы;
  • структуры;
  • модули;
  • функции;
  • отношения наследования;
  • зависимости между классами.

Например:

BookingController
        |
        v
BookingService
        |
        +----> AvailabilityService
        |
        +----> BookingRepository
        |
        +----> PaymentGateway

Или подробнее:

interface BookingRepository {
    Booking save(Booking booking);
    Optional<Booking> findById(BookingId id);
}

class BookingService {
    private final BookingRepository repository;
    private final AvailabilityService availabilityService;
}

На этом уровне C4 часто пересекается с UML:

  • class diagram;
  • package diagram;
  • dependency diagram.

Главный вопрос уровня

Какими программными конструкциями реализован компонент?

Но этот уровень используется значительно реже остальных.

Причина проста: код меняется быстро, и подробные диаграммы классов быстро превращаются в архитектурные окаменелости.


Полная иерархия

Software System
    |
    +-- Container
            |
            +-- Component
                    |
                    +-- Code

Например:

Hotel Booking Platform                         System
    |
    +-- Booking Service                        Container
            |
            +-- Availability Component         Component
                    |
                    +-- AvailabilityService     Code
                    +-- InventoryRepository     Code
                    +-- DateRange               Code

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


Пример C4 для системы бронирования отелей

Level 1: Context

[Клиент]
    |
    v
[Hotel Booking System]
    |
    +----> [Payment Provider]
    +----> [Hotel Management System]
    +----> [Notification Provider]

На этом уровне система бронирования является одним прямоугольником.


Level 2: Containers

[Web Application]
        |
        v
[API Gateway]
        |
        +----> [Search Service] ------> [OpenSearch]
        |
        +----> [Booking Service] -----> [Booking PostgreSQL]
        |             |
        |             +--------------> [Redis]
        |             |
        |             +--------------> [Kafka]
        |
        +----> [Payment Service] -----> [Payment Provider]
        |
        +----> [Notification Service] -> [Email/SMS Provider]

Здесь уже видна техническая архитектура.


Level 3: Components внутри Booking Service

[Booking REST Controller]
          |
          v
[Booking Application Service]
          |
          +----> [Availability Checker]
          |
          +----> [Booking Domain Model]
          |
          +----> [Booking Repository]
          |
          +----> [Inventory Lock Manager]
          |
          +----> [Outbox Event Publisher]

Здесь мы раскрываем только один контейнер, Booking Service.

Не нужно одновременно раскрывать внутренности всех микросервисов. Иначе диаграмма снова превращается в архитектурные макароны.


Level 4: Code внутри Availability Checker

AvailabilityService
    |
    +-- AvailabilityRepository
    |
    +-- ReservationPolicy
    |
    +-- DateRange
    |
    +-- RoomTypeId
    |
    +-- AvailabilityResult

C4 не определяет архитектуру

Это принципиальное различие.

C4 не говорит, что нужно использовать:

  • микросервисы;
  • Kafka;
  • Kubernetes;
  • Clean Architecture;
  • DDD;
  • REST;
  • PostgreSQL.

C4 только задаёт способ показывать уже выбранную архитектуру.

То есть:

Архитектурное решение:
использовать Booking Service и PostgreSQL.

C4:
способ отобразить Booking Service и PostgreSQL на диаграмме.

Нельзя сказать:

Мы построили систему по архитектуре C4.

Точнее будет:

Мы описали архитектуру системы с помощью модели C4.


C4 и UML

C4 не является прямой заменой UML.

UML

UML содержит большое количество типов диаграмм:

  • class diagram;
  • sequence diagram;
  • component diagram;
  • deployment diagram;
  • activity diagram;
  • state machine diagram;
  • use case diagram.

UML формальнее и детальнее.

C4

C4 даёт простую иерархию масштабов:

Context → Container → Component → Code

Его преимущество в том, что человек сразу понимает:

  • какой уровень он сейчас видит;
  • что находится внутри рассматриваемого объекта;
  • какая детализация намеренно скрыта.

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

Например:

  • C4 Context Diagram для общей картины;
  • C4 Container Diagram для сервисов;
  • sequence diagram для сценария бронирования;
  • state machine diagram для жизненного цикла бронирования;
  • deployment diagram для Kubernetes-инфраструктуры.

Статическая и динамическая архитектура

Основные четыре уровня C4 показывают преимущественно статическую структуру:

  • какие элементы существуют;
  • как они связаны;
  • кто от кого зависит.

Но они не всегда хорошо показывают последовательность выполнения операции.

Например, Container Diagram может показать:

Booking Service → Payment Service

Однако она не объясняет:

  1. когда вызывается Payment Service;
  2. сначала блокируется номер или списываются деньги;
  3. что происходит при ошибке;
  4. как выполняется компенсация;
  5. где используется идемпотентность.

Для этого нужна динамическая диаграмма:

User
  |
  v
Booking API
  |
  v
Inventory Service: hold room
  |
  v
Payment Service: authorize payment
  |
  v
Booking Service: confirm booking
  |
  v
Kafka: BookingConfirmed

Поэтому C4 обычно дополняют:

  • sequence diagrams;
  • dynamic diagrams;
  • deployment diagrams;
  • state diagrams;
  • data models.

Какие элементы должны быть подписаны

Хороший элемент C4 содержит три вещи:

[Название]
[Тип или технология]
[Ответственность]

Например:

Booking Service
[Java / Spring Boot]

Создаёт, подтверждает и отменяет бронирования.
Управляет жизненным циклом бронирования.

Плохой вариант:

Service 1
Service 2
Database
Queue

Такие названия показывают геометрию системы, но не её смысл.

Связи тоже желательно подписывать:

Booking Service
    |
    | Creates payment authorization
    v
Payment Service

А не просто рисовать стрелку без объяснения.


Что считается ошибкой в C4

1. Смешивание уровней

Например, на одной диаграмме одновременно показаны:

User
Booking Platform
Booking Service
BookingController
BookingRepository
PostgreSQL table bookings
Kubernetes Pod

Здесь смешаны:

  • context;
  • container;
  • component;
  • code;
  • deployment;
  • data model.

Получается не C4, а архитектурный винегрет.


2. Изображение технологий вместо ответственности

Например:

Java Service
Redis
Kafka
PostgreSQL

Но непонятно:

  • что делает Java Service;
  • зачем используется Redis;
  • какие события передаются через Kafka;
  • какие данные лежат в PostgreSQL.

Технология не заменяет назначение.

Лучше:

Booking Service
Создаёт и отменяет бронирования.

Redis
Хранит кратковременные блокировки доступности.

Kafka
Передаёт события изменения состояния бронирования.

3. Попытка показать всё

C4 не требует одной полной энциклопедической диаграммы.

Можно иметь:

1 Context Diagram

1 общую Container Diagram

3 Component Diagram:
- Booking Service
- Payment Service
- Search Service

Необязательно рисовать Component Diagram для каждого сервиса.


4. Представление базы данных как нейтральной общей коробки

Если несколько сервисов используют одну базу, это должно быть видно.

Например:

Booking Service ----\
                     > Shared Database
Payment Service ----/

Такая схема означает архитектурную связанность. Общая база превращается в скрытую точку интеграции.

C4 не запрещает это, но делает зависимость видимой.


C4 на System Design Interview

На собеседовании обычно достаточно двух уровней.

Сначала Context

Очень кратко:

Пользователь → Система бронирования → Отели / Платежи / Уведомления

Это фиксирует границы задачи.

Затем Containers

Основная рабочая диаграмма:

Client
  |
Load Balancer / API Gateway
  |
  +-- Search Service
  +-- Booking Service
  +-- Payment Service
  +-- Notification Service
  |
PostgreSQL / Redis / Kafka / OpenSearch

Затем точечное приближение

Когда интервьюер спрашивает:

Как избежать двойного бронирования?

Ты приближаешь только соответствующую часть:

Booking Service
    |
    +-- Availability Checker
    +-- Inventory Lock Manager
    +-- Booking Repository
    +-- Idempotency Handler

После этого можно показать последовательность:

Check availability
→ acquire hold
→ create pending booking
→ authorize payment
→ confirm booking
→ publish event

Это и есть практическая сила C4: архитектура раскрывается по запросу, а не вываливается на стол целиком.


Как понимать C4 одной формулой

Context:
кто взаимодействует с системой.

Container:
из каких запускаемых частей состоит система.

Component:
из каких логических частей состоит отдельное приложение.

Code:
какими конструкциями реализована отдельная логическая часть.

Самая полезная мысль:

C4 управляет не самой архитектурой, а масштабом разговора об архитектуре.

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

Прокрутить вверх