Почему не делаю DDD, но использую его идеи

Начну с провокации. Эрик Эванс, автор 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 организует код на основе этого знания.

Вопросы для обсуждения

  1. Рассинхронизация Pack и Code — Эванс считает это неизбежным. Наш ответ — Capture-to-Pack + downstream contract. Достаточно ли этого?

  2. Knowledge Aggregate — в DDD Aggregate = группа объектов, обновляемых вместе. Нужен ли нам аналог — группа сущностей Pack, которые ДОЛЖНЫ обновляться вместе?

  3. Классификация Pack’ов — DDD делит домены на Core / Supporting / Generic. Можно ли применить к нашим Pack’ам? Что у нас Core?

  4. Overhead формализации — DDD работает через устное общение и код. Мы добавляем Pack между ними. Когда это оправдано, а когда избыточно?


Source-of-truth: Полный маппинг с таблицами и обоснованиями — SPF/docs/ddd-mapping.md

Не специалист в DDD. Проходил 5-ти дневные курсы в 2024 г и одна из основных мыслей, на которой делал акцент лектор (практик а не просто теоретик) - DDD это в первую очередь про разобраться в бизнесе и корректно описать предметную область. А до разработки может и вовсе дело не дойти

Это общие впечатления

Насколько помню, кроме классики Эванса рекомендовали книгу “Изучаем DDD”, Влад Хононов

2 лайка

Провокация не удалась - Эванс устарел. Женя уже ответил, руководство по системной инженерии говорит читать Хононова)

Сложность и реальность вещи в предметной области, а не в коде. Поэтому предметную область должны знать все участники инженерного процесса. Про сделать код моделью, такого нет. Нужна модель из предметной области. И делают её явно не программисты. Её делают методологи, даже если не они не в курсе как их роль называется. Потом эта модель прорастает/реализуется в коде.

Это важно.

А это и неважно и не нужно зачастую. Потому что это набор паттернов реализации в стиле ООП (не универсально и т.д.). В стиле любых программерских и не только карго культов.

Это модель самого DDD. И она при адекватной реализации прорастает везде. Единый язык везде - в созвоне разных ролей, в описании фичи, в описании теста, в структуре и неймингах на уровне кода, на уровне модели данных, на уровне таблиц и колонок БД, интерфейсов пользователя, тикетов из техподдержки, планов на релизы. Везде) И в dev time и в run time.

Не служат. Я повторяться не буду. Цель код у оторванных от жизни программистов. У DDD цель обратная, вернуть разработку в реальный мир, который она моделирует.

В коде заперта реализация. Знание в головах у экспертов предметной области. Всё таки Хононова надо почитать сначала. Я сейчас книжку пересказываю.

Бизнес эксперт может на естественном языке описать модель, алгоритм, бизнес кейсы, даже тест кейсы (через BDD). И отдать их разработчику. И если команда правда работает по DDD, то разработчик сделает ровно то, что нужно.

И дальше по пунктам кто куда уходит, что где зафиксировано, могут команды друг с другом общаться или нет. Это детали реализации метода DDD в каждой конкретной фирме. Как реализуете так и будет. Мы внутри блока из 7 ми команд нормально общаемся на “языке”, с соседями из условно другого поддомена тоже вполне себе. Хотя у нас язык расчётно-бухгалтерский, а у них кадровый.

2 лайка

Да, Наталья, спасибо большое за детальный разбор. Приходится постепенно погружаться в эту тему. Мне кажется, что основную суть я уловил. Я то точно не ИТшник и точно не могу утверждать обратное этому: «Сложность и реальность вещи в предметной области, а не в коде»))).

Интересная фраза «знания в головах экспертов». Я бы сказал, что мировоззрение в головах, а вот знания хотелось бы уметь получать и формализовывать. Мировоззрение - это уже какое-то представление знания. Было бы интересно узнать ваше мнение именно относительно предложенной 4х уровневой структуру знаний?

1 лайк

Мне тут сложно. У нас есть верхнеуровневые разговоры, где знания/дисциплины/теории/объяснения. И это мета и мета-мета. Есть рассуждение о том, что знания в головах мокрых сеток это конечно какие-то концепты, в этом смысле они представления о моделях/онтологиях/знаниях? В голове у эксперта предметной области скорее всего свой какой-то view/viewpoint. О мировоззрении мне тут сложно говорить, для меня это не синоним view, это какая-то более философско-человеческая штука. Если вернуться к живым экспертам, я от них ожидаю мета-У вперемешку с мета-С тех мест, где они работали. Никакой хорошо формализованной мета-У не ожидаю. Плюс на эти экспертные модели, на нашей стороне будет какой-то методолог делать свою мета-С или пачку мета-С, и там тоже на разных уровнях разные модели…

Из роли соклубника - красиво) Из роли человека-на-конкретной-работе - непонятна жизнеспособность. Раньше рассуждение какое было: 1й уровень вот получили в МИМ, 2й это мета-У, идите ищите хороший учебник, 3й пилите свою мета-С, 4й это уже то, что мы руками делаем. Теперь - 1й уровень формализован (и это прекрасно), 2й уровень всё ещё непонятно где брать, 3й это скорее запрос на то, что можно свою мета-С вот так оформить, 4й - ещё больше вопросов. Насколько я понимаю, весь фокус в (полу)автоматическом разворачивании знаний сквозь все уровни и возможности поправить на каком-то этапе и оно дальше раскатается/эволюционирует. Но я второй пост на эту пока не прочитала, поэтому остановлюсь.

1 лайк

Пост этот навёл меня на определённые размышления.

Я занимаюсь информационным моделированием в строительстве (BIM), и у нас в сообществе уже не первый год идут споры о том что же такое модель, в этом самом строительном контексте.

Есть те кто считает что модель - это любое структурированное представление информации о строительном объекте. Вот например перевели пояснительную записку по объекту в формат xml согласно утверждённой Минстроем схеме - и это уже модель.
Моё мнение здесь в том что модель (здания) должна всё же обладать некоей прогностической функцией, то есть если мы например собираем конечноэлементную трёхмерную модель строительной конструкции в специальном программном комплексе - это можно назвать моделью, так как прикладывая к ней виртуальные же нагрузки, мы моделируем процесс её нагружения (снегом, оборудованием, людьми, землетрясением) и предсказываем результаты - как поведёт себя реальный объект, соответствующий этой модели. То же можно сказать про CFD-моделирование воздушного потока в помещении, или гидравлический расчёт. Можно это сделать и в табличке Excel, главное чтобы там была зависимость изменения выходных данных от входных, то есть где-то внутри экселя должны быть формулы, переводящие входные данные в выходные. Тогда модель работает. А просто xml ничего не прогнозирует, это просто структурированные данные, констатирующие некий факт.
Почему я об этом подумал при прочтении данного поста.
Дело в том что IT и строительство - изначально максимально далёкие друг от друга сферы. И BIM - технология, которая их объединяет, притягивает друг к другу. Но идёт этот процесс сложно, и в нём как раз ярко проявляются те эффекты, победить которые, судя по этому посту, был призван DDD. Научить строителя и айтишника (разработчика, аналитика, продакта) говорить на одном языке.
Для этого даже был создан специальный классификатор, он же код, и он же формат файлов - ifc. Проблема однако состоит в том что даже производители софта для моделирования зданий (различных САПР) до сих пор не на 100% смогли настроить корректный маппинг, чтобы из своих внутренних форматов конвертировать модели корректно в ifc, то есть настроить 100% однозначное соответствие своих внутренних классов классам ifc. В итоге отдельно взятые специалисты отрасли (как правило представители разработчиков САПР, либо их продвинутые пользователи) в принципе понимают, какой элемент корректно относить в IfcWall а какой к IfcSlab, но классов так много, что даже они ориентируются лишь в какой-то его части, в домене зданий например, или только в домене мостов, но не в классификаторе целиком. Что и говорить о спеицалистах по монтажу, от которых это бесконечно далеко.
Таким образом получается что чтобы дать возможность всем говорить на одном языке и им же писать код - размер домена должен быть гораздо меньше чем отрасль строительства, меньше чем строительство зданий, а например только конструктивная часть (система), либо только система водоснабжения (и то отдельно внутренняя и отдельно наружная).
Но пока примеров такого применения ifc я не встречал, хотя как будто по описанию он бы мог для этого подойти.