Что такое архитектурная модель C4?

Ответ

Модель C4 (C4 Model) — это иерархический подход к визуализации архитектуры программного обеспечения, созданный Саймоном Брауном. Он позволяет описывать систему на разных уровнях детализации, делая архитектуру понятной для разной аудитории (от менеджеров до разработчиков).

Модель состоит из 4 уровней диаграмм:

  1. Уровень 1: Контекст (System Context)

    • Что показывает: Систему как «чёрный ящик», её пользователей (акторов) и внешние системы, с которыми она взаимодействует.
    • Аудитория: Все (бизнес, менеджмент, разработчики).
  2. Уровень 2: Контейнеры (Containers)

    • Что показывает: Основные строительные блоки системы: веб-приложения, мобильные приложения, базы данных, файловые системы, микросервисы.
    • Важно: «Контейнер» здесь — это не Docker-контейнер, а развертываемая единица (deployable unit).
    • Аудитория: Разработчики, архитекторы, DevOps-инженеры.
  3. Уровень 3: Компоненты (Components)

    • Что показывает: Основные модули или компоненты, из которых состоит отдельный контейнер. Например, в Go-приложении это могут быть UserController, AuthService, PaymentGatewayClient.
    • Аудитория: Разработчики, архитекторы.
  4. Уровень 4: Код (Code)

    • Что показывает: Детали реализации конкретного компонента (диаграммы классов, схемы баз данных). Этот уровень часто не визуализируют, а ссылаются прямо на исходный код.

Пример для Go-бэкенда:

Представим, что мы детализируем архитектуру:

  • Контейнер: API Server (веб-сервис на Go).
  • Компонент: UserService (отвечает за бизнес-логику пользователей).
  • Код: Реализация компонента UserService.
// Уровень 4 (Код) — реализация компонента "UserService"

// UserRepository - это интерфейс, который является частью другого компонента (например, "PersistenceComponent")
type UserRepository interface {
    FindByID(id string) (*User, error)
}

type UserService struct {
    repo UserRepository
}

func (s *UserService) GetUser(id string) (*User, error) {
    // Бизнес-логика и вызов другого компонента
    return s.repo.FindByID(id)
}

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

Ответ 18+ 🔞

Давай разберём эту вашу C4 Model, а то как слышу «архитектура» — сразу представляю себе бородатого дядьку в свитере, который рисует квадратики и стрелочки, пока все остальные реально кодят. Но эта штука, блядь, оказалась не такой уж и бесполезной!

Представь, что ты объясняешь, как устроен твой дом. Ты же не начнёшь с того, из какого конкретно сплава сделана защёлка на двери в сортире? Нет, сука. Ты скажешь: «Вот дом, в нём живём мы, а вот почтальон приносит письма». Это и есть первый уровень — Контекст. Просто чёрный ящик и кто к нему лезет.

Потом, если собеседнику не похуй, ты уточнишь: «А внутри дома есть кухня, ванная и серверная стойка в чулане». Это второй уровень — Контейнеры. И да, «контейнер» — это не про докер, ёпта! Это про то, что можно воткнуть/выключить как отдельную штуку: веб-сервак, базу данных, тот же твой Go-бэкенд.

Дальше уже для своих: «На кухне стоит холодильник, плита и кофе-машина, которая без кофеина не работает». Это третий уровень — Компоненты. То есть из каких основных модулей состоит твой Go-сервис: UserController, AuthService и прочая пиздопроебибна.

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

Вот смотри, как это для нашего Go-приложения выглядит:

Мы на уровне контейнеров решили, что у нас есть API Server. Хорошо. Залезаем в него — видим компонент UserService. Отлично. А вот и его кишки, уровень кода:

// Это уже четвёртый уровень, детка. Смотри, как оно внутри.

// UserRepository — это интерфейс, он из другого компонента, "PersistenceComponent"
type UserRepository interface {
    FindByID(id string) (*User, error)
}

type UserService struct {
    repo UserRepository // держим за зависимость, как полагается
}

func (s *UserService) GetUser(id string) (*User, error) {
    // Ну тут наша бизнес-логика, вся соль
    // И вызов другого компонента через интерфейс — красота, блядь!
    return s.repo.FindByID(id)
}

И вся фишка C4 в чём? В том, что ты можешь объяснить систему и менеджеру, который думает, что Go — это настольная игра, и сеньору, который уже готов спорить про внедрение зависимостей. Каждый получает ровно ту детализацию, которая ему нужна, без лишней хуйни. Никаких мартышлюшек с диаграммами на 100500 страниц, где всё связано со всем. Чистая иерархия, как у нормальных людей. В рот меня чих-пых, а ведь работает!