Модуль 4.2. DDD для системного аналитика
Темы: Ubiquitous Language, Bounded Context, агрегаты, инварианты. Артефакты: контекст-мапа, словарь домена. Практика: event storming по домену.
DDD (Domain-Driven Design) помогает делать изменения дешёвыми и уменьшать связность. Ваша роль как системного аналитика — обеспечить единый язык домена, очертить границы контекстов, описать агрегаты и их инварианты, связать это с процессом разработки (UC/API/ER/события).
В конце модуля у вас будут:
- контекст-мапа (map границ и отношений контекстов);
- словарь домена (Ubiquitous Language);
- модель агрегатов с инвариантами и событиями;
- результат event storming (события/команды/политики/агрегаты).
Ubiquitous Language (UL) — единый язык домена
UL — это не «словарик терминов», а живой язык команды. Он должен звучать в требованиях, тестах, коде, БД и событиях.
Правила
- Термин ⇄ поле ⇄ событие ⇄ API имя — одинаково (например, Order, OrderPaid, /orders).
- Определение включает что это и что не это (границы смысла).
- Для каждого термина указан владелец (data steward/PO) и источник правды.
Мини-шаблон записи термина
Термин: Order Определение: Юридически значимая заявка на покупку; ценность — послепродажный учёт. Не путать с: Cart (корзина до оплаты), Shipment (отгрузка). Источник правды: сервис Checkout, таблица order. Атрибуты: orderId (UUID), status (CREATED/PAID/SHIPPED/DELIVERED/CANCELLED), totalAmount (Money) События: OrderCreated, OrderPaid, OrderShipped Инварианты: totalAmount = Σ(items.qty*price)
Bounded Context — границы смысла и изменения
Что такое контекст
Область, в которой UL последователен и устойчив. В разных контекстах одинаковые слова могут значить разное («Order» в Checkout vs «Order» в WMS).
Как выделять (чек-лист)
- Capability (возможность): «Приём платежей», «Комплектация», «Каталог».
- Ритм изменений: «двигается часто» ≠ «редко».
- Данные и инварианты: кто владелец? где источник правды?
- NFR/комплаенс: PCI-зона, PII, latency.
- Оргструктура: одна команда — один контекст (Conway/Team Topologies).
Отношения между контекстами (Context Map)
- Customer/Supplier — зависимость вниз по течению; Supplier публикует язык (контракт).
- Conformist — downstream вынужден подстраиваться (мигрировать позже).
- Anti-Corruption Layer (ACL) — «переводчик» между моделями.
- Published Language — общий, формально описанный язык (схемы/контракты).
- Open Host Service (OHS) — открытый API/сервис.
Шаблон карточки контекста
Контекст: Payments Цель: Авторизация/списание, возвраты; PCI-зона. Владелец: Payments Squad UL: Payment, Authorization, Capture, Refund Агрегаты: Payment (capturedAmount ≤ authorizedAmount), Refund Интеграции: REST create/capture, событие PaymentCaptured Отношения: Supplier для Checkout (Published Language: payment events), ACL для PSP Источник правды: db_payments.*
Агрегаты и инварианты — тактическое DDD
Определение
Агрегат — кластер сущностей/объектов-значений с корнем (Aggregate Root), внутри которого инварианты поддерживаются атомарно. Вне агрегата — только через публичные методы корня.
Когда агрегат «правильный»
- Одна причина изменений (SRP); изменения не «тянут» другие агрегаты.
- Инварианты формулируются локально и проверимы синхронно.
- Границы транзакций совпадают с агрегатом.
Типовые тактические элементы
- Entity (имеет идентичность), Value Object (иммутабелен, сравнение по значению).
- Domain Event (факт внутри контекста), Domain Service (поведение, не принадлежащее сущности).
- Repository (доступ к агрегатам), Factory (создание агрегата с инвариантами).
Примеры агрегатов (e-commerce)
-
Order:
Инварианты — totalAmount = Σ(items), статусные переходы CREATED→PAID→..., запрет смены адреса после SHIPPED.
События — OrderCreated, OrderPaid, OrderCancelled. -
Payment:
Инварианты — captured ≤ authorized, refunded ≤ captured.
События — PaymentAuthorized, PaymentCaptured, RefundCompleted.
Микро-чек-лист агрегата
- Чёткий root и публичные операции (команды).
- Инварианты описаны словами и формализованы (AC/SQL).
- Внутренние объекты — Value Objects (Money, Address).
- Внешние изменения через доменные события или приложения-сервисы.
- Размер агрегата минимален: всё, что можно вынести в отдельный агрегат — вынесено.
Инварианты: как формулировать и проверять
Шаблон формулировки
Инвариант: <утверждение> Сфера действия: <агрегат/контекст> Гарантия: <синхронно/в eventual consistency> Проверка: <SQL/AC/событие> Ошибки/компенсации: <что делаем при нарушении>
Примеры
-
Order.totalAmount = Σ(item.qty*item.price)
Сфера: Order. Гарантия: синхронно. Проверка: SQL чек. -
Payment.refunded ≤ Payment.captured
Сфера: Payment. Гарантия: синхронно. Проверка: SQL + AC в сервисе. -
Нельзя менять адрес заказа после SHIPPED
Сфера: Order. Гарантия: синхронно (guard на команду). Проверка: аудит-лог.
Event Storming — как провести сессии (практика)
Что это
Быстрый визуальный способ выявить доменные события, команды, агрегаты, политики и границы контекстов с участием бизнеса, разработки, QA, аналитиков.
Подготовка
- Стена/борд (живой или Miro/FigJam).
-
Стикеры/карточки (цвета):
оранжевый — событие, синий — команда, жёлтый — актор/роль,
зелёный — политика/правило, розовый — агрегат, фиолетовый — внешняя система,
красный — боль/узкое место, серый — вопрос/неизвестность.
Сценарий 90–120 минут
- Big Picture (30–40 мин): собираем факты (оранжевые события) в хронологии: «Что случилось?»
- Команды/Акторы (20 мин): над событиями добавляем кто/что вызвал (синие + жёлтые).
- Агрегаты (15 мин): группируем события по сущностям, клеим розовые «корни».
- Политики/Правила (15 мин): зелёными отмечаем автоматические реакции/DMN.
- Границы контекстов (15 мин): рисуем «облака» вокруг кластеров; помечаем внешние системы (фиолетовые).
- Hotspots/Вопросы (10 мин): красные/серые карточки.
- Результаты (5 мин): фото/экспорт, список действий/владельцев.
Что должно получиться
- Лента событий (OrderCreated→PaymentCaptured→OrderShipped…).
- Список команд и акторов.
- Кластера событий по агрегатам (Order/Payment/Shipment).
- Политики: «При PaymentCaptured → создать отгрузку».
- Границы контекстов: Checkout, Payments, Shipping.
- Список вопросов и рисков.
От event storming к контекст-мапе и контрактам
- События → каталог событий (см. модуль 3.5).
- Команды/Акторы → входные порты сервисов/модулей.
- Агрегаты/Инварианты → AC/BDD, SQL-проверки (см. 3.6).
- Границы → Context Map; отношения (Customer/Supplier, ACL, Conformist).
- Контракты → OpenAPI/AsyncAPI, правила версионирования (см. 3.4).
Контекст-мапа (артефакт)
Мини-DSL (Mermaid)
flowchart LR
subgraph Checkout[Checkout (Customer)]
direction TB
OC[Order Context]
end
subgraph Payments[Payments (Supplier, OHS+PL)]
direction TB
PC[Payment Context]
end
subgraph Shipping[Shipping (Downstream)]
SC[Shipping Context]
end
OC -- Published Language (events) --> PC
OC -- Conformist (uses API) --> PC
SC -- ACL --> WMS[(WMS External)]
OC -- Domain Events --> SC
Шаблон карточки отношения
Отношение: Checkout (Customer) → Payments (Supplier) Тип: Customer/Supplier + Published Language + Open Host Service Соглашения: Async events payment.captured.v1; REST create/capture; N/N-1 версий Анти-коррапция: не нужна (мы потребляем язык Payments) Риски: ломающие изменения схем; Меры: schema registry, contract tests
Пример: агрегаты и инварианты (оформление как сотруднику)
Агрегат Order (фрагмент спецификации)
Aggregate: Order
Root: Order
Команды: CreateOrder, AddItem, Pay, Ship, Cancel
События: OrderCreated, ItemAdded, OrderPaid, OrderShipped, OrderCancelled
Инварианты:
- totalAmount = Σ(items.qty * price) (синхронно)
- status transitions: CREATED→PAID→SHIPPED→DELIVERED; CANCELLED с оговорками
- Address change forbidden when status ∈ {SHIPPED, DELIVERED}
Пограничные условия: пустой заказ запрещён; валюта едина по заказу
Сторонние зависимости: Pricing (калькуляция), Payments (Pay)
Согласованность: Pay → событие OrderPaid по факту PaymentCaptured (eventual)
Агрегат Payment
Aggregate: Payment Команды: Authorize, Capture, Refund События: PaymentAuthorized, PaymentCaptured, RefundCompleted Инварианты: - capturedAmount ≤ authorizedAmount - refundedAmount ≤ capturedAmount - idempotencyKey UNIQUE (повторы безопасны) Согласованность: события публикуем через outbox+CDC
Типовые ошибки и как их избежать
|
Ошибка |
Симптом |
Как исправить |
|---|---|---|
|
Контексты по слоям (UI/DB), а не по доменам |
Множество кросс-зависимостей |
Резать по возможностям/данным/инвариантам, а не по технологиям |
|
«Жирные» агрегаты |
Команды блокируют друг друга, deadlocks |
Делите по инвариантам; выносите атрибуты в VO/подагрегаты |
|
Общая БД на несколько контекстов |
Тугие связи, сквозные JOIN |
«БД на контекст», обмен — через API/события |
|
События с PII и бизнес-логикой |
Утечки, ломкость |
В событиях — минимум фактов, без PII; правила — в DMN/контексте |
|
Нет UL-дисциплины |
Разные имена одного и того же |
Глоссарий, ревью контрактов/схем, линтеры |
|
Согласованность «кровью» (2PC) |
Хрупкость, ретраи-ад |
Саги/TCC, outbox+CDC, идемпотентность |
Вопрос–Ответ
В: DDD — это про микросервисы?
О: Нет. DDD применим в монолите/модульном монолите тоже. Контексты ↔ модули; события ↔ внутренние уведомления.
В: Чем агрегат отличается от таблицы БД?
О: Таблица — физическое хранилище. Агрегат — единица изменений и инвариантов. Один агрегат может храниться в нескольких таблицах и наоборот.
В: Сколько событий «нужно»?
О: Столько, сколько значимых фактов домена. «UI-клики» — не события домена.
В: Когда использовать ACL?
О: Если upstream язык неконтролируем или «грязный». ACL «переводит» внешний язык в наш UL и изолирует изменения.
В: Как понять, что агрегат слишком большой?
О: Долгие транзакции, блокировки, частые изменения не связанные одной причиной — признаки для разделения.
В: Как фиксировать UL эволюцию?
О: Семантическое версионирование схем/контрактов, openapi/asyncapi-diff в CI, записи в глоссарии с «с какого релиза».
Артефакты модуля
Контекст-мапа (Context Map)
- Перечень контекстов, владельцы, источники правды, отношения (Customer/Supplier, Conformist, ACL, OHS, PL).
- Ссылки на контракты (OpenAPI/AsyncAPI), схемы, события, SLO.
Словарь домена (Ubiquitous Language)
- 20–50 ключевых терминов, определения, не-путать-с, атрибуты, источники правды, события/статусы, DQ-правила.
Практика: Event Storming по домену (2–3 часа)
Вход: выберите домен (финтех «Платёж/Возврат» или e-commerce «Заказ/Доставка»).
Шаги и сдача
- Big Picture: лента из ≥15 событий (оранжевые).
- Команды/Акторы: минимум 8 команд и 4 акторов.
- Агрегаты: не меньше 3 (Order/Payment/Shipment), для каждого — 3+ инварианта.
- Политики: 5+ автоматических реакций (зелёные).
- Контексты: выделить 3–5 bounded contexts, обозначить отношения (Customer/Supplier/ACL/Conformist).
- Каталог событий: 5 карточек (имя, версия, ключ партиции, producer/consumer, совместимость).
- Словарь UL: 15 терминов по шаблону.
Критерии зачёта
- События — факты, а не действия UI.
- Инварианты формализованы (словами и проверкой).
- Контексты разграничены по ответственностям/данным/ритмам.
- Есть отношения и правила совместимости.
- Артефакты версионированы (Git) и связаны с UC/API/ER.
Шпаргалка (распечатайте)
- UL — говорить одним языком; отражать его в коде/схемах/событиях.
- Контекст — это граница смысла и изменений; «БД на контекст».
- Агрегат — единица согласованности и инвариантов; меньше — лучше.
- Согласованность между контекстами — через события/саги, не через 2PC.
- Контекст-мапа + словарь — ваши главные артефакты DDD.
- Event storming — быстрый способ найти события, агрегаты, политики и границы.



