Vlad Khononov - Learning Domain-Driven Design

Прочитал книгу Vlad Khononov “Learning Domain-Driven Design. Aligning Software Architecture and Business Strategy”, 2022 год. Эта книга посвящена практикам Domain Driven Design, применяющимся в программной инженерии.

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

Важнейшая концепция в DDD это единый язык (ubiqutious language). Идея единого языка состоит в том, что при разработке используется язык, используемый предметными экспертами. Таким образом разработчик погружается в предметную область - ему помимо специфических знаний в разработке необходимо разбираться и в предметной области. Причем этот язык используется вплоть до названий переменных или типов. Через общение с предметными экспертами выявляем язык и далее используем этот язык везде в работе. Единый язык помогает сократить расстояние между разработчиком и предметным экспертом и решает известную проблему испорченного телефона, когда задача несколько раз переформулируется и до разработчика доходит в искаженном виде. Также в книге упомянуты аналитики и проговаривается, что аналитики - если они просто берут информацию от заказчика и передают разработчику - не нужны, у них должна быть некая инженерная функция, т.е. они должны принимать решения по продукту и в таком случае получается, что слово “аналитик” вводит в заблуждение, они являются инженерами.

Если домен/предметная область большая, то в ней есть смысл выделить несколько поддоменов. Вводятся три типа поддоменов: общий (generic), поддерживающий (support), основной (core).

Общие поддомены могут быть сложными, но не является ключевым преимуществом бизнеса, его используют все похожие бизнесы, тут можно использовать некий готовый софт или отдавать реализацию на аутсорс. Поддерживающие поддомены - это некоторая простая функциональность. Суть бизнеса заключена в основных поддоменах - именно здесь сосредоточены инновации и сложность. Основные поддомены обязательно должны разрабатываться In-house. Представьте себе, что компания Google отдала на аутсорс алгоритмы ранжирующие выдачу в поиске? Кстати, сложность и инновации основного поддомена могут быть и не завязаны на разработку, а лежать в других областях. В этом плане DDD конечно хорош, что он в первую очередь глядит не на софт, а на бизнес. А в бизнесе софт может играть совсем второстепенную роль, это нормально, а DDD это подсветит.

Подобное разделение на типы поддоменов позволяет сосредоточить внимание на важном и использовать для соответствующих поддоменов необходимые решения.

Важный вопрос в том, как именно провести границы между поддоменами. Это концепция Bounded Context. По границам Bounded Context проводится разделение функциональности и разделение команд, реализующих функциональность. Вообще Bounded Context и поддомены это немного разное - поддомены это то, что существует в реальном мире, на что делится предметная область - это данность, выявляемая в сотрудничестве с предметными экспертами. А Bounded Context проектируется - т.е. это то, как будет создаваться программный продукт, внутри этих границ создаются части продукта, и по этим границам проходят команды.

Есть такой закон Конвея, что создаваемый продукт повторяет структуру организации и обратный маневр Конвея, когда структура организации формируется под нужды создаваемого продукт. DDD и Bounded Context позволяют выявить границы продукта и выстроить продукт и организацию правильным образом.

В книге много советов и тактических приемов как выстраивать взаимодействие между частями продукта. Все крутится вокруг идеи единого языка. Разные части продукта будут работать в разных поддоменах и там будут разные языки. В таком случае важен перевод языка. Тут интересный момент - если мы говорим о разработке в рамках одного поддомена, то смена языка это то, с чем мы боремся - разработчик должен разрабатывать продукт и разговаривать с предметным экспертом на одном и том же языке. В то же время если идет взаимодействие двух частей продукта из разных поддоменов, то у них естественным образом два разных языка и с необходимостью требуется перевод с одного языка на другой. Разные тактические приемы предназначены для того, чтобы обеспечивать перевод таким образом, чтобы влияние частей продукта было минимально. Много говорится о слабой связности (loose coupling) частей продукта и про то как обеспечивать подобную связность.

Рассказано о конкретных практиках, которые можно использовать для разработки в стиле DDD. Некоторые практики предназначены для выявления предметной области, а некоторые - это паттерны разработки. Интересной показалась практика EventStorming, предназначенная как раз для того, чтобы выявить как работает предметная область, захотелось попробовать попрактиковать.

По паттернам разработки тоже много интересного. Мне показалось, что DDD более тяготеет к объектно-ориентированному подходу, особенно если смотреть на объекты в стиле BORO-фреймворка, тогда получается хорошо. Тут конечно много интересного и кажется самое важное - умение бегло работать с базовыми паттернами такими как Value Object, Transaction Script, Active Record.

Понял что Clojure как язык не очень сочетается с подходом DDD. В DDD основа это ubiqutios language. Это означает, что мы должны использовать концепты из предметной области и задавать типы. Clojure динамический, т.е. типы играют небольшую роль и проверка типов происходит только в рантайме. Также Clojure функциональный, что означает что основная работа будет происходить с простыми уже заданными структурами.

DDD хорошо согласуется с микросервисной архитектурой, это вопрос выделения Bounded Context, микросервисы должны проходить по границам поддоменов.

Также приведены паттерны для объединения всех частей в единый продукт.

Что хорошо, DDD не является all-or-nothing подходом. Отдельная глава посвящена приемам, позволяющим начать применять приемы DDD в существующих brownfield проектах. Вообще, мне нравится, что сейчас меньше говорят о legacy проектах, а более о brownfield проектах, т.к. концепция brownfield говорит о живущем проекте, а legacy - о проекте, которую в какой-то момент придется заменить. Кажется, что концепция legacy, устаревшего проекта сама уже чутка устарела - понятно, что проект в момент выпуска уже становится несовременным, это норма жизни сегодня.

Итого, для меня было довольно сложно читать эту книгу. Приведенные паттерны непривычные, я не привык разрабатывать и мыслить так. Что клево: приведенный подход кажется позволяет справится с системами большого размера и работать в команде, это круто. Одна из самых полезнейших техник мне кажется EventStorming, про нее есть отдельная книга, еще прочитаю.

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

Текст также опубликован в личном блоге Telegram: Contact @burganov

4 лайка

Спасибо за пост!

Интересный момент в том, что говоря с бизнес аналитиком на одном языке - языке предметной области. Я всё же говорю на естественном языке - на русском. А программируя, я вынуждена термины предметной области переводить на инглиш. И т.к. он для большинства совсем не родной, качество перевода страдает. Точнее чем сложнее понятия, тем больше страдает перевод. И эта дополнительная интерпретация даёт лишнюю когнитивную нагрузку. Хорошо, если где-то в коде фиксируется документация (комментарии) на языке предметной области, но на русском. Но донести пользу таких комментариев до коллег - целое дело.

Не думаю, что ЯП будь он функциональный и динамический может мешать реализовывать DDD. Ооп тоже бывает с динамической типизацией. Мне нравится идея, что любое программирование это работа с данными. А именно работа по преобразованию данных. У вас же наверное данные всё равно в БД лежат? Отсутствие типов не мешает работать с концептами предметной области. Та же запись в БД, которая мапится в какую-то структуру на ЯП это и есть тип. Таблица это класс, запись это экземпляр класса, если в терминах моделирования говорить.

У меня есть опыт с Java и TypeScript, все ООП. Недавно сел за React, а там как раз отказались от компонентов сайта как объектов - теперь это функции. У меня сначала мозг сломался, мол, как так, тут же ООП так хорошо подходит - каждый компонент-объект является каким-то объектом из домена.

Однако от компонента нужно поведение, как его описать - функцией или поведением, уже не так важно.

Мне кажется, что ключ к решению как раз в этой фразе. Есть домен со своими типами, например танцевальная пара::роль, а есть его описание в языке программирования: function DanceCouple() {...} или class DanceCouple {...} и здесь у танцевальной пары тип из парадигмы/синтаксиса языка программирования. Тип этот уже не зависит от типа в DDD.

Как вам такая идея?

1 лайк

Мне кажется, все равно зависит. Прям в типе проявляется динамизм или его отсутствие, class DanceCouple {...} - это прям статическое описание. А function DanceCouple() {...} прям так и кричит о “системе в момент эксплуатации”)) По крайней мере так у меня в голове

Есть проверенный способ - писать названия переменных и функции латинницей.
Этакий самодокументирующийся подход.

Заодно прокачивается и предметная область.

Для “англоговорящих” эстетов выглядит некошерно, но когнитивная нагрузка снижается.

Русские слова латиницей писать, как во времена экономии на длине смсок?

про экономию на СМСках не знаю ))
но да, русские слова - латинницей
типа:

function obrabotka_zayavki  () {
....
}

:slight_smile:

Спасибо, какой кошмар) Лучше уж русский язык затащить в ЯП, он чаще всего поддерживается.

Если бы я в фирме на испытательном сроке такие штуки увидела в БД и в коде, был бы большой вопрос, стоит ли тут оставаться. “Некрасиво - не полетит”.

:slight_smile:
я практикую такую штуку.
джуны быстро начинают ориентироваться в предметной области.

Наверно, их тоже коробит вначале, т.к. “по русски == не красиво”, но думаю - проходит, т.к. соображалка прогрессирует семимильными шагами, начинают за кодом видеть физ.обьекты.

Как виртуозно сравнили в чате:Telegram: Contact @ailev_blog_discussion
с Зоей Виксельштейн… Так вот она уходит лесом :slight_smile:

Если у вас на проекте только джуны и вы взяли такую практику, почему бы и нет. Когда у тебя проекту 4 года и 12 человек сеньоров и ты им даже не лид, просто навязать мнение не получится.

По-русски как раз красиво.

function рассчитать_служебную_командировку_по_сотруднику(идентификатор_сотрудника)

А вот склонять через транслитерацию, так себе. Я предпочитаю, что-то такое:

# Расчёт служебной командировки
function calculate_business_trip(employee_id)

И тут же остаётся вопрос, а что вы там программируете. Т.к. где “заявка” и где за ней физический объект?) Заземление всем хорошо. Но области разные и до земли от тех объектов предметной области, с которыми мы работаем, может быть неблизко.

“за кодом видеть физ.обьекты” – вот, самое главное во всём этом DDD. И если от физического мира далеко, то всё одно дотягивать. Ибо нефизическое не хранит состояний, там нет событий. DDD про объекты, меняющие состояния, а хранение состояний – в физическом мире. Далее дисциплина учёта: синхронизация состояний программных объектов (в базах данных или памяти, это без разницы) и физических объектов – это реестры, регистры, в случае бухучёта и депозитарного учёта это счета, и т.д.

Вот этот ликбез надо всем давать, с него корпоративное айти начинается.

А по-русски объекты или по-английски, это без разницы. Но если в жизни они по-русски, а в коде по-английски, будут проблемы – это против духа DDD, вот эта разница будет проблемна. Другое дело, что тогда не спутаешь физический объект и реальный объект, но физики-теоретики не так работают: они добиваются более-менее полноценного моделирования физических объектов математическими, а потом начинают просто говорить на языке математических объектов, перенося их свойства на физические, пока не вылезет какая-нибудь невязка (тогда уточняют модель). Если же там будет ещё и переход с языка на язык – это не будет работать. Мышление должно быть одно у “бизнесОв” и “программеров”, на одном языке, на одной модели.

Помню, как первый раз увидев язык 1С (до этого сталкиваясь только с языками на латиннице) решил, что это шаг назад. А потом где-то (не вспомню) встретил исследование, что разработка на языке, совпадающим с нативным, происходит на 30% быстрее, и впечатлился смелостью идеологов 1С, сделавших шаг против традиции.

Выбрал для себя специализацию - ритейл (включая интернет-магазины, склады, логистику, производство): API, интеграции, оптимизации и т.п.

вроде как большинство языков поддерживают UTF-8 и кириллицу. Это, конечно, максимально странно писать на русском, но да: если проект не интернациональный и если следовать DDD, то надо делать так. А в DDD это как бы прям ключевой момент, ubiquitous language, однако.

1 лайк

Насколько я понимаю, во время эксплуатации уже будет по вызову:

const aCouple = new DanceCouple(…);
const somethingElse = DanceCouple(…);