Начну с провокации. Эрик Эванс, автор DDD, говорит:
«The model is not the diagram. The model is not the document. The model IS the code.»
Я с этим согласен. И одновременно — нет. Объясню.
Что такое DDD в двух абзацах, как я это понял (хотя не работал с ним, но хочу разобраться в том числе за счет этой публкации)? Domain-Driven Design — подход к разработке ПО, ключевая идея которого состоит в следующем: сложность софта — в предметной области, а не в технологии. Поэтому нужно: глубоко разобраться в домене, построить модель, и сделать именно код этой моделью.
DDD даёт два набора инструментов:
- Стратегические — как разделить большой домен на части (Bounded Context, Context Map, Ubiquitous Language)
- Тактические — как написать код внутри одной части (Entity, Value Object, Aggregate, Domain Event)
*Основные рабочие продукты DDD.
DDD создаёт артефакты двух видов:
Описания (не-код): Ubiquitous Language Glossary, Bounded Context Definitions, Context Map, Subdomain Classification, Domain Vision Statement, Event Storming Board — всего 6 описательных артефактов.
Код: Entity, Value Object, Aggregate, Domain Event, Domain Service, Repository, Factory, Layered Architecture — 8 кодовых артефактов.
Важное уточнение: описания в DDD служат коду. Glossary — чтобы классы назывались правильно. Context Map — чтобы микросервисы нарезать верно. Все описания — промежуточные, их конечная цель — код.
Что DDD делает хорошо
Bounded Context — граница, внутри которой термины и правила непротиворечивы. Это, возможно, самая ценная идея DDD. Без неё — «Big Ball of Mud»: все термины значат всё и ничего.
Ubiquitous Language — единый язык домена. Класс в коде называется так же, как бизнес-концепция. Разработчик и эксперт говорят на одном языке.
Knowledge Crunching — процесс «перемалывания» знаний. Команда вместе с экспертами разбирается в домене, и это понимание напрямую становится кодом.
Богатая таксономия интеграции — 9 паттернов Context Map (Partnership, Anti-Corruption Layer, Conformist, Open Host Service…) — точные инструменты для описания связей между контекстами.
Эванс решает конкретную проблему 2003 года: аналитик пишет документ → архитектор рисует диаграммы → разработчик пишет код, который не соответствует ни тому, ни другому → документы устаревают на следующий день.
Решение DDD: выбросьте промежуточные документы. Сделайте код моделью.
Это не упущение — это осознанный выбор. DDD не порождает доменное знание как самостоятельный, отделённый от кода артефакт. И у этого есть серьёзные последствия:
| Проблема | Суть |
|---|---|
| Знание заперто в коде | Бизнес-эксперт не читает код → не может валидировать модель |
| Нет передачи без людей | Уходит разработчик → знание о «почему модель устроена так» уходит с ним. Код показывает что, но не почему |
| Нет верификации | Доменные правила нигде не зафиксированы отдельно от кода в структурированной форме |
| Масштабирование через людей | UL работает в одной команде. При 10 командах устный язык не масштабируется |
| Смешение домена и реализации | Код на конкретном языке, в конкретном фреймворке неизбежно смешивает «что истинно в домене» с «как это реализовано» |
Были попытки компенсировать: Event Storming (стикеры), BDD (Given-When-Then), ADR. Но ни одна не стала стандартной частью DDD и не решает проблему полностью.
Что можно попробовать сделать иначе
Выделить 4-уровневую структуру знаний:
FPF — первые принципы (мета-онтология) изложенные в виде фреймворка
↓
SPF — вторые принципы (форма и процесс) изложенные в виже фреймворка
↓
Pack — формализованное знание домена (source-of-truth), обычно в репозитории
↓
Downstream — код, курсы, агенты (производные), обычно не только в репозиториях
Ключевое отличие от DDD: Pack — самостоятельный артефакт доменного знания, не привязанный к коду. Он создается не для человека и не для ИИ, для них создается представление знания или Downstream.
В DDD source-of-truth — код. У нас — Pack (структурированный текст). Код — это один из downstream-продукт, производный от Pack.
Как можно использовать идеи
Не отвергаем DDD, а используем его стратегические паттерны, обобщив их за пределы кода (это уже есть в FPF):
| Идея DDD | Как живёт у нас | Что добавили |
|---|---|---|
| Bounded Context | Каждый Pack = Bounded Context | Обобщение на любой домен, не только код. 4 обязательных компонента: Glossary, Invariants, Roles, Bridges |
| Ubiquitous Language | Glossary + Distinctions | Формальные тесты на нарушение + failure modes |
| Knowledge Crunching | 11-шаговый процесс + Capture-to-Pack | Формализованный процесс вместо неформальных воркшопов |
| Model-Driven Design | Pack-driven Downstream | Код пишется на основе Pack, не наоборот |
FPF A.1.1 прямо указывает на это: «FPF обобщает доказанную идею DDD Bounded Context из ПО в универсальный моделирующий примитив.»
В чем наше преимущество:
| Наше | Зачем | Почему DDD не покрывает |
|---|---|---|
| Failure Modes | Типовые ошибки понимания домена, с тестами | DDD не формализует ошибки интерпретации |
| Trust Metrics (F, G, R) | Зрелость каждой сущности знания | DDD: модель либо есть, либо нет |
| SoTA + revision criteria | Когда пересматривать знание | DDD: рефакторинг по необходимости |
| Мета-уровни (FPF → SPF) | Переносимость паттернов между доменами | DDD — сам себе метод, без мета |
| Downstream contract | Явный интерфейс знание → применение | DDD не разделяет знание и применение |
Что есть в DDD, чего нет у нас:
| DDD | Зачем | Можно ли взять |
|---|---|---|
| Aggregate | Группа объектов, обновляемых вместе | Да → «Knowledge Aggregate» для Pack |
| Context Map Integration Patterns (9 шт.) | Типы связей между контекстами | Да → расширить наши Bridges |
| Core/Supporting/Generic | Приоритизация инвестиций | Да → классификация Pack’ов |
| Domain Events | Runtime-уведомления | Нет (Pack = knowledge-time, не runtime) |
Минусах нашего подхода:
| Минус | Суть |
|---|---|
| Рассинхронизация | Pack и код могут разойтись — та самая проблема Эванса |
| Overhead | Создание Pack’а с ID, frontmatter, distinctions — затраты |
| Нет runtime-паттернов | Aggregate, Domain Event — при переходе к коду нужен DDD |
| Молодая методология | Нет широкого community, мало tooling |
Вопрос: что делать, когда Pack ≠ Code?
В DDD ответ простой: код прав, обнови документ.
В waterfall: документ прав, обнови код.
У нас: определи, где истина, и обнови то, что ошибается.
| Ситуация | Что обновить |
|---|---|
| Новое знание обнаружено при разработке | Pack, затем код подтягивается |
| Технический компромисс в коде | Code (с записью, почему), Pack остаётся «идеальной» моделью |
| Эксперт нашёл ошибку в модели | Pack, затем код |
| Рефакторинг кода открыл лучшую структуру | Оба: Pack уточняется, код закрепляет |
Это сложнее, чем «код всегда прав». Но в этом есть свои плюсы.
Наш подход обобщает стратегические паттерны DDD (Bounded Context, Ubiquitous Language, явные границы) за пределы кода — на формализацию доменного знания как самостоятельного артефакта.
При этом добавляет то, чего нет в DDD: мета-уровни, failure modes, trust metrics, SoTA, downstream contracts.
DDD и наш подход не конкурируют — они последовательны: Pack формализует знание → DDD организует код на основе этого знания.
Вопросы для обсуждения
-
Рассинхронизация Pack и Code — Эванс считает это неизбежным. Наш ответ — Capture-to-Pack + downstream contract. Достаточно ли этого?
-
Knowledge Aggregate — в DDD Aggregate = группа объектов, обновляемых вместе. Нужен ли нам аналог — группа сущностей Pack, которые ДОЛЖНЫ обновляться вместе?
-
Классификация Pack’ов — DDD делит домены на Core / Supporting / Generic. Можно ли применить к нашим Pack’ам? Что у нас Core?
-
Overhead формализации — DDD работает через устное общение и код. Мы добавляем Pack между ними. Когда это оправдано, а когда избыточно?
Source-of-truth: Полный маппинг с таблицами и обоснованиями — SPF/docs/ddd-mapping.md