Чтобы правильно задать вопрос, нужно знать большую часть ответа.

Шекли

Дисклаймер – этот текст не принесёт никакой пользы тем, кто не знает, что такое DDD, тем, кто не хочет ничего знать про DDD и тем, кто уже знает про DDD всё, что им нужно.

Когда-то давно, впервые познакомившись с паттернами DDD, я подумал, что эта методология, очевидно, создана теоретиками, изрядно оторвавшимися от реальности. Себя, естественно, я считал опытным практиком. Прошли годы, прежде чем я осознал, что это Эванс был практиком, практиком создания сложных систем с большим временем жизни, а теоретиком в этой области был как раз я.

Мой старый враг - сложность
Мой старый враг - сложность

Кто является главным врагом разработчиков программного обеспечения? Я бы сказал, что наш главный враг – сложность.

Сложность бывает разная. Часть сложности мы сами же себе и создаём – ненужными либо неуклюжими действиями, но тут всё относительно понятно – просто не надо так делать и всё (ну как, «просто»...), но как быть со сложностью невынужденной, сложностью самой задачи?

Может, нужно просто быть умнее? Пить витаминки для мозга, больше стараться, засиживаться на работе допоздна или, напротив, кататься на горных лыжах каждые выходные? Это всё, конечно может помочь, но не может спасти. Мы, работники сравнительно элитного интеллектуального труда, как правило достаточно болезненно относимся к попыткам подвергнуть сомнению наши интеллектуальные способности, но факт остаётся фактом – способности человеческого мозга, даже самого лучшего, далеко (ай, как далеко!) не безграничны, одно только правило 7±2 (учитываемых при принятии решений факторов) чего стоит.

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

Во-первых, есть принцип «я подумаю об этом завтра», также известный, как абстрагирование, позволяющий отложить мелкие детали на потом. Впрочем, как известно, «дьявол в мелочах, в деталях - Бог», иными словами, «гладко было на бумаге, да забыли про овраги».

Во-вторых, у нас есть принцип «одна голова хорошо, а две лучше», также известный, как мозговой штурм. Командная работа позволяет отчасти преодолеть ограничения, заложенные в нас природой, но, разумеется, только до определённого предела, а что за ним?

Также у нас есть возможность соединить эти два подхода, решив задачу «в принципе», разбив её на этапы/подзадачи, а каждый отдельный этап поручить отдельному человеку или отдельной команде. Этот вариант берёт всё лучшее из двух миров, но он же берёт и всё худшее из них тоже.

Наконец, никто не отменял старый-добрый метод проб и ошибок, aka научного тыка (да, agile, это про тебя).

Пару слов о командной работе

Не все команды одинаково полезны
Не все команды одинаково полезны

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

Можно привести лошадь к водопою, но нельзя заставить её пить. Можно собрать людей в одном помещении, вручить им проект и объявить их командой, но становятся ли они от этого командой на самом деле? Исследования показывают, что таким образом формируется не команда, а рабочий коллектив, который всего лишь имеет потенциал стать командой со временем.

В чём разница и почему это вообще имеет значение? Разница в эффективности, которая, согласно некоторым исследованиям, может различаться в разы. Команда это рабочий коллектив, участники которого связаны ощущением глубокого доверия и которые успешно распределили между собой командные роли, каковые, к слову, никак не связаны с позициями и техническими компетенциями.

К сожалению, исследователи неумолимы – подобные тесные и конструктивные отношения возможны исключительно в небольшом коллективе, оптимальный размер которого составляет от 5 до 7 человек, а предельный - 11 человек. Дальше ваша команда разваливается, причём в лучшем случае – на две, как правило же вы остаётесь с небольшим (3-5 человек) ядром, окружённым аморфным коллективом ограниченной полезности. И это не вопрос организации работы, это вопрос организации нашего мозга, по крайней мере, так считают профильные специалисты.

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

Именно поэтому кроссфункциональные продуктовые команды – не блажь, а насущная необходимость, единственный способ приблизить эффективность работы к теоретически возможному пределу. Однако, предела в 7 человек для эффективной работы и 11 человек до развала команды это не отменяет, а значит, по факту, ужесточает его ещё больше, так как в этот предел теперь необходимо впихнуть невпихуемое специалистов с разными хард-скилами, не забывая при этом про необходимые для нормального функционирования команды софт-скилы. А это, в свою очередь, радикально режет предельную когнитивную сложность проблематики, которую такая команда может решить.

А в чём, собственно, проблема?

Вы знаете этого парня
Вы знаете этого парня

Что происходит с проектом, когнитивная сложность которого превосходит возможности команды? А ничего хорошего, но, как это обычно и бывает с серьёзными заболеваниями, симптоматика развивается постепенно.

Поначалу у нас всё хорошо. Мы понимаем, что работы предстоит много, но наверху не дураки сидят и сроки выставляются с учётом этого факта.

Первые звоночки – постепенное, но планомерное увеличение сроков реализации казалось бы простых требований и исправления простых ошибок, а также постепенный рост регресса – каждое изменение создаёт новые ошибки в уже, казалось бы, реализованных сценариях, что заставляет менеджеров сурово морщить брови и сомневаться в компетенции исполнителей (неужели так трудно писать код без ошибок или это у нас просто архитектура кривая?).

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

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

Далее, если нам таки удаётся выйти со всем этим в продакшен, то, рано или поздно, в нашем проекте заводится свой Василий. Василий умён и ответственен, а ещё он на редкость токсичен (и куда, спрашивается, смотрели hr?), поэтому к обсуждениям его привлекают с тяжёлым сердцем, но и не привлекать нельзя, так как он единственный человек, «который реально понимает как у нас всё работает». Иметь с ним дело непросто, потому что он вечно мрачен, напряжён и пассивно-агрессивен, а на предложение «что-то изменить вот так» реагирует так, как будто вы предложили ему что-то глупое и аморальное одновременно. А, кстати, почему?

Представьте себе, что вы работаете на заводе. И вот мимо вас проходит коллега, который тащит что-то очень тяжёлое, хрупкое и ценное одновременно. Коллеге тяжело, он потеет, морда красная, ноги едва заметно подрагивают. Сочтёте ли вы нормальным подойти к нему, завести светскую беседу и попросить «заодно прихватить с собой вот эту коробку, раз уж ты всё равно в цех идёшь», или могучий инстинкт самосохранения предостережёт вас от этого опрометчивого поступка?

Так вот, Василий тоже тащит на себе непомерный груз – в своей голове. И только он один знает, насколько он близок к тому, чтобы его бросить к какой-то там матери и поискать себе другое занятие, желательно на природе и без всяких компьютеров и прочих гаджетов.

У этой сказки нет и не может быть счастливого конца, потому что несмотря на общее интеллектуальное превосходство (реальное или мнимое) над окружающими, Василий – всего лишь человек и его возможности жонглировать логическими связями в своей голове тоже ограничены, а значит, держащийся исключительно на нём проект обречён либо на крах, либо на стагнацию и медленное умирание.

Хитроумный Одиссей

Герой, который нам нужен прямо сейчас
Герой, который нам нужен прямо сейчас

Итак, Одиссей был капитаном корабля, который было необходимо провести через узкий пролив.

Слева располагались скалы, в которых жила Сцилла – местная вариация Змея-Горыныча, отъевшегося слишком сильно, чтобы не то что летать, а и просто выходить из дома, поэтому Сцилла просто поджидала проплывающие мимо корабли, чтобы сжирать с их палуб моряков, вытягивая с этой целью свои длинные и многочисленные шеи.

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

Какова была ширина безопасного коридора между ними? Она была отрицательной, т.е. её не было. Что сделал в этой ситуации Одиссей и почему его подвиг воспет в веках? Он просто провёл свой корабль ровно на том расстоянии от Харибды, которое счёл достаточно безопасным, и таким образом несколько затруднил Сцилле процесс поедания его друзей и соратников (там были предприняты и меры тактического характера, и это тоже важно, но сейчас не об этом). В результате корабль успешно прошёл через пролив, но в процессе потерял несколько членов экипажа, включая ну просто очень-очень близкого друга самого Одиссея.

Просто и очевидно? Как бы не так! Человеческий мозг и сам по себе отвратительно справляется с мультифакторной оптимизацией, но в условиях стресса и внешнего давления эти способности у большинства людей практически обнуляются. Сплошь и рядом умный и рациональный человек упирается в одну идею-фикс и начисто игнорирует все прочие факторы, которые (в силу предшествующего травматического опыта) считает менее важными.

Под управлением такого капитана ваш проект корабль либо попрётся прямо над Харибдой (ведь иначе до вас дотянется страшная Сцилла!), либо пойдёт ровно вдоль стеночки, чтобы минимизировать риск гибели от Харибды, предоставив таким образом Сцилле полную свободу выбора, кого жрать первым, а кого – посмаковать на десерт.

Мораль этой истории очень проста – будьте как Одиссей и оставайтесь как Одиссей, даже когда на вас давят сроки, бюджет и белочки-истерички.

А причём тут DDD?

Комплексное решение
Комплексное решение

Domain Driven Design предлагает не то чтобы революционный, но комплексный подход к борьбе со сложностью в IT-проектах. И его главный секрет зашифрован (не так чтобы очень криптоустойчиво) в его названии – это дизайн, основанный на предметной области.

Для того, чтобы понять, что это означает на практике, нужно сначала разобраться с парой вопросов – что такое предметная область и на чём ещё можно основывать дизайн системы?

Предметная область это объективная реальность. Это то, что не исчезает, когда вы закрываете свои глаза. Это то, что было до вашей системы, существует одновременно с ней и будет существовать, когда ваш проект закроют, а команду разгонят.

Бухгалтерский учёт существовал до 1С. Рельеф земной поверхности существовал до изобретения карт и, если ваша карта не соответствует рельефу, то это ваша проблема, проблема вашей карты, но не проблема рельефа.

Умные люди не спорят с объективной реальностью, они её изучают и используют себе во благо. Есть, правда, гении, которым иногда (но только иногда!) удаётся объективную реальность изменить, но по какой-то странной причине большинство этих гениев сидят в дурке.

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

В чём проблема Требований, Великих и Ужасных? В том, что если бы они работали, как предполагается, то работал бы и Водопад, а это не так. И чем сложнее предметная область, тем сложнее становится задача создания пакета полных и непротиворечивых требований, не говоря уже о требованиях устойчивых. Если ваш дизайн основан на требованиях, то какие бы изощрённые паттерны вы не использовали, вы строите своё здание на песке, вместо нормального фундамента.

В чём проблема Архитектурных Паттернов, своим Величием бросающим вызов Небесам? В том, что любой архитектурный паттерн является не архитектурой, а всего лишь приёмом её создания (пользуюсь случаем передать горячий привет любителям «микросервисной архитектуры», равно как и фанатам архитектуры «объектно-ориентированной»).

Но как конкретно выстраивать дизайн системы на основе предметной области? Есть набор т.н. «стратегических паттернов» DDD, которые с этим помогают (но только если вы задействуете их в комплексе).

Onion Architecture

Даже Шрек ценит луковые метафоры
Даже Шрек ценит луковые метафоры

Это, разумеется не изобретение автора DDD (как, впрочем, и вообще ничто в DDD, кроме самого DDD). Данный подход предлагает разделить то, что обычно называется бизнес-логикой, на две части – логику домена и логику приложения (application layer), причём логика домена при этом не должна зависеть ни от логики приложения, ни, тем более, от логики инфраструктуры. Для чего их нужно разделять и как?

Логика приложения отвечает за логику работы, собственно, приложения, реализованную в соответствии с требованиями бизнеса. Доменная логика моделирует логику, собственно, домена.

Требования могут быть неполны, противоречивы и непостоянны. Домен, будучи объективной реальностью, полон, целостен и слабо подвержен изменениям (хотя к нашему представлению о нём это относится лишь в меру наших скромных сил). Конечно, не все домены одинаково устойчивы – небесная механика останется неизменной сколь угодно долго, в то время, как бизнес-процессы компании постоянно плавают, подстраиваясь под рыночную ситуацию, но в любом случае требования к работе приложения обычно меняются куда чаще и, главное, по другим поводам.

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

Опытным разработчикам это, конечно, известно, но для новичков стоит упомянуть, что два этих слоя настолько различаются в своём предназначении, что на практике для их реализации используются разные наборы тактических паттернов проектирования, разные стили.

Всегда ли легко провести границу между слоями? Скорее нет, чем да, всегда существует определённая серая зона (пускай с опытом она и будет становиться всё тоньше и тоньше). Сам я люблю применять «метод гномиков». Если заменить компьютеры на не особо умных, но чрезвычайно исполнительных гномиков, жёсткие диски на блокнотики с карандашами, а сетевое взаимодействие на перекрикивание с помощью рупоров, то что в вашей системе останется неизменным? Это и будет вашим доменом.

Мой личный совет – не придавайте этому вопросу слишком большого значения. Если принадлежность какой-то логики к доменному или application слою вызывает сомнения или споры, то, скорее всего, к какому бы решению вы не пришли, идеальным оно не будет, так что просто займитесь реальной работой и оставьте религиозные дискуссии профессионалам в этой области. Жизнь в виде поддержки этого кода по мере развития продукта вас рассудит. Its a numbers game, в конце концов.

Но есть и ещё один момент. Если в вашем конкретном случае домен прост и тонок, а вся сложность лежит именно в application слое, то всё вышесказанное не про вас и не для вас, а попытки всё равно «делать правильно» будут не более чем карго-культом и той самой невынужденной сложностью, которую просто не нужно создавать.

Поддомены (sub-domains) и связанные (bounded) контексты.

Архитектура - это не совсем про классы
Архитектура - это не совсем про классы

Следующий метод борьбы со сложностью все программисты впитывают вместе с кодом первых pet-проектов – сильная зацеплённость, слабая связанность. Мы группируем вместе то, что кажется тесно связанным между собой с точки зрения логики и чётко разграничиваем то, что кажется относительно независимым. Таким образом, мы боремся с угрозой комбинаторного взрыва, возникающего, когда что угодно может зависеть от чего угодно произвольным образом.

Мы делаем это на уровне методов, мы делаем это на уровне классов, мы делаем это на уровне библиотек и целых сервисов – это универсальный подход. Чем выделяется DDD? Тем, что выносит эту практику на беспрецедентно высокий уровень – на уровень анализа предметной области. Ещё раз – не на уровень архитектуры ПО, а на уровень анализа самой задачи.

На самом деле, само по себе это не было революционной идеей, бизнес использует эту технику очень давно, выделяя в составе предприятия отделы, отвечающие за тот или иной аспект его работы. Относительно новой стала идея того, что этот шаг (по сути – бизнес-анализа, но с нюансами) должен обязательно предшествовать проектированию, причём проектирование на стратегическом уровне должно результаты этого анализа буквально отражать (в виде bounded contexts, т.е. связанных контекстов или, если уж совсем точно, «контекстов с чётко очерченными границами» ), а не просто учитывать, как один из источников информации при принятии решения.

Почему? Потому что умные люди не спорят с объективной реальностью. Если в реальной логике домена какие-то аспекты тесно связаны друг-с-другом, то пытаясь разделить их в программной модели, т.е. доменной логике, вы прямо напрашиваетесь на неприятности.

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

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

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

Поэтому намерения и планы не имеют особого значения, важен только потенциал развития. В противном случае вместо критического успеха у нас может случиться критическая неудача – поддомен в результате окажется слишком сложным и в лучшем случае нас ждёт болезненное перепроектирование с перераспределением ответственности между командами и компонентами, а в худшем – провал проекта. 

Это – Харибда архитектурного проектирования, конец игры. В роли Сциллы же выступают связи между поддоменами. Они неизбежны, как смерть и налоги, ведь если бы поддомены не имели связей между собой, то не образовывали бы единого домена и не требовали бы создания единой системы. Поэтому всё, что вы можете сделать – минимизировать ущерб.

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

Что можно сделать, чтобы облегчить ситуацию, когда она выглядит угрожающе?

Во-первых, вы можете попытаться слить два разделённых поддомена обратно в один.

Во-вторых, вы можете напротив, выделить тесную связь между двумя поддоменами в третий, если это уменьшит связанность. Классический пример этой техники – вынесение логики генерации отчётов, требующих совместного использования данных из нескольких подсистем, в отдельную подсистему. Как вариант – перераспределение ответственности между поддоменами может носить и более сложный характер, затрагивая и другие поддомены тоже (а что делать?).

В-третьих, вы должны отдавать предпочтение менее жёстким связям – асинхронной коммуникации на основе событий, а не вызовов (по мере возможности), не бояться затаскивать в свой контекст кусочки данных (но не логики!) смежных контекстов (раз уж логическая связь между ними всё равно существует по факту).

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

Много чего можно сделать, но что делать, если ничего из этого не помогает? Как это обычно бывает в таких ситуациях, первый и самый главный шаг – признание проблемы. Нужно честно сказать себе и непосредственному начальству – мы в заднице. Эта жёсткая связанность между поддоменами и отвечающими за них командами будет нашей болевой точкой. Это риск, которым нужно управлять и как можно раньше, ни в коем случае нельзя поддаваться искушению просто не обращать на него внимание в надежде, что всё как-то само образуется.

Ваша главная задача – как можно раньше уменьшить волатильность. Пусть ваши лучшие аналитики первым делом займутся прояснением деталей именно этой истории. Ваши лучшие архитекторы должны первым делом детально проработать именно эти интерфейсы взаимодействия. Ваш лучший Project должен сосредоточиться на налаживании и поддержании как можно менее неэффективных коммуникаций между невольно связанными этой зависимостью командами. Короче – минимизируйте ущерб и как можно раньше, так, как делали бы, если бы все сроки уже горели и Заказчик уже требовал бы от вас результата, потом будет уже поздно пить боржоми.

И пара слов о микросервисах

И этого парня вы знаете тоже
И этого парня вы знаете тоже

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

Разбиение системы на микросервисы без учёта всего вышесказанного – прямая дорога в ад распределённого монолита, который объединяет все худшие качества монолита со всеми худшими качествами микросервисов. С другой стороны, правил без исключений не бывает и технические микросервисы имеют право на существование – просто это именно тот случай, когда следует отмерить все семь раз перед тем, как что-то резать.

Однако, есть ещё и такой феномен, как «наносервисная архитектура», когда в отдельный микросервис выделяется буквально каждый мелкий чих. На мой взгляд, у этого подхода недостатков куда больше, чем достоинств, но я не могу не отметить одно действительно важное его преимущество – когда ваш микросервис становится действительно маленьким и простым, его внутренняя структура перестаёт иметь какое-либо значение и может себе позволить быть настолько простой, насколько это вообще возможно, без потери качества.

Разбираемся в сортах поддоменов

Создатель DDD предложил поделить выявляемые поддомены (и, соответственно, контексты) на три категории:

  • Core – ядро домена, причина разработки системы

  • Generic – типовые поддомены, то, что встречается постоянно в разных доменах и разных системах

  • Supporting – вспомогательные поддомены, помогающие работать ядру

И, по моему мнению, тем самым подложил своей концепции изрядную свинью. Нет, по сути всё правильно, но вот типичное понимание этой классификации – дело совсем другое.

Начнём с самого простого – generic поддомены это то, что вы, вполне вероятно, делали в своём предыдущем проекте и, вполне вероятно, будете делать в следующем. По сути, речь о том, что отличает опытных архитекторов от начинающих – умение видеть то, что шахматисты называют типовыми (а футболисты - стандартными) позициями. CRM, нотификации, SSO, платежи по кредитным картам и т.д. и т.п. Отказ от создания велосипедов на стратегическом уровне, причём речь идёт не только и не столько о реализации, сколько о проработанных концепциях.

А вот с core получилось достаточно трагикомично. Во-первых, на удивление много читателей почему-то решили, что речь идёт о shared core, разделяемом между контекстами ядре системы. Трудно себе представить что-то более далёкое от истины, но, к счастью, таких всё-таки меньшинство.

А вот тех, кто постоянно мучает себя вопросами в духе «а может ли система иметь несколько core поддоменов» или увлечённо холиварит на предмет, что можно считать истинным core поддоменом, а что нет, едва ли не большинство.

Я бы предложил (вслед за Верноном) смотреть на это иначе – отсортируйте свои поддомены по убыванию совокупности когнитивной сложности и важности для бизнеса, а свои команды разработки по убыванию профессионализма и слаженности, а затем сопоставьте эти списки друг-другу именно в таком порядке. Дело не в том, что можно назвать core, а что нельзя, а в том, что самыми сложными и ответственными участками работ должны заниматься ваши самые лучшие люди.

Если вы – условный Netflix и ваша политика найма требует, чтобы ваш потенциальной сотрудник в типичной компании считался бы местной звездой, то шаг сортировки можно и пропустить. Но если вы и есть та самая типичная компания, лучшие кадры из которой постоянно утекают в какой-нибудь Netflix, то вам волей-неволей нужно уметь идти на компромиссы и наступать на горло своему чувству прекрасного.

Разработчики не рождаются крутыми senior-ами, а специалистам среднего уровня сложно применять на практике сложные паттерны проектирования, поэтому им приходится писать на Go использовать схемы попроще, чтобы оставаться эффективными. А простые поддомены, в свою очередь (какое приятное совпадение!), в силу своей относительной простоты не создают той самой когнитивной сложности, которая настоятельно требует относиться к своей организации с предельной серьёзностью и жестоко карает за вольное с собой обращение.

Трёхслойная архитектура? Прекрасный, надёжный подход! Реализация контроллера, DTO и обращения к базе в одном файле (aka simple architecture)? Почему бы и не да, если простота задачи позволяет?

Задача со временем может усложнится и «всё придётся переделывать»? It’s a numbers game. Нет ничего страшного в том, чтобы иногда что-то переделать, если как правило переделывать не приходится, а упрощение подхода позволяет сэкономить много времени для других задач.

Единый (ubiquitous) язык

Строители Вавилонской башни вошли в историю не благодаря своему исключительному профессионализму
Строители Вавилонской башни вошли в историю не благодаря своему исключительному профессионализму

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

В двух словах, речь о том, что вы должны использовать одни и те же слова (существительные и глаголы) в по меньшей мере трёх контекстах:

  • При обсуждении сценариев работы предметной области как внутри команды, так и с доменными экспертами

  • На всех диаграммах, иллюстрирующих работу вашего доменного слоя

  • Во всех (почти) классах вашего доменного (sic!) слоя

Англоязычным командам, конечно, проще, но остальные просто могут применять в беседах термины на родном языке в качестве взаимозаменяемых с английскими.

Звучит же достаточно просто, верно? А применяется достаточно редко. А почему, собственно? Я знаю две причины.

А зачем нам эксперт? Нам эксперт не нужен!

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

Жванецкий

Во-первых, эксперты предметной области не хотят использовать «этот ваш птичий язык». Что, в свою очередь, вскрывает широкий пласт куда более сложной проблемы – ограниченного доступа команды к знаниям этих самых экспертов, их физической недоступности или эмоциональной невовлечённости. И это одна из самых нелепых проблем в IT.

Вы представляете, сколько стоит разработка ПО? Я как-то пытался прикинуть порядок сумм и у меня получилось, что полгода работы одной кроссфункциональная команды небольшого размера обойдутся работодателю (с учётом налогов) примерно в стоимость квартиры в Москве. IT это очень и очень дорого! Что может за полгода сделать одна команда? Ничего такого, для чего мог бы понадобится DDD.

Как вообще можно реализовать проект в рамках сложной предметной области без нормального доступа к знаниям доменных экспертов? Божьей милостью? Или за 100 500 итераций? Или, быть может, кто-то вкачал Удачу до капа и не парится?

Во что обойдётся заказчику этого проекта полугодовая задержка в нормальной реализации требований? А во что обойдётся законченный продукт, который делает то, что заказывали, но совсем не то, что было на самом деле нужно? А может, идея посадить дорогостоящего и важного специалиста в одну комнату с командой разработки на полный рабочий день и приковать к батарее – не такая уж плохая и страшная? В принципе, agile, который мы все так любим, прямо требует именно этого в качестве критерия успеха.

Но в подобном экстриме обычно нет нужды – часа в день на созвоны и доступности в messenger-е и почте в остальное время может быть вполне достаточно, это даже не пол-ставки. Причём выделять для этой цели руководителей отделов и нобелевских лауреатов совершенно необязательно, достаточно крепкого середнячка, у которого есть возможность обращаться к старшим коллегам за консультацией по сложным вопросам.

А может, мы как-нибудь так?

В моей жизни я не раз оказывался на перепутье. И я всегда знал, какой путь будет правильным. Я всегда знал это, без исключений. Но я никогда не выбирал его. Знаете, почему? Потому что это было чертовски сложно.

Полковник Слэйд (Аль Пачино).

Как-нибудь так
Как-нибудь так

Правила использования единого языка имеют одну примечательную тонкость. Когда (не если, а именно когда) в результате углубления вашего понимания предметной области вы замечаете, что для обсуждения её сценариев вам приходится использовать термины, который в вашей доменной модели нет, вы должны изменить реализацию своей доменной модели, переписать свой код, чтобы вернуть однозначное соответствие между ними.

Как же так? Ведь ранее я говорил, что доменную модель нужно создавать именно для того, чтобы не пришлось ничего менять, что она – фундамент нашего приложения? Что же это за фундамент такой, если его нужно постоянно переделывать? А ведь у нас уже фичи в продакшене и схема базы данных согласована с девопсами, безопасниками, лидом Data Platform, девочкой с ресепшена и курьером из пиццерии, как мы это всё менять будем и зачем? А диаграммы что, тоже перерисовывать? Может, мы как-нибудь так?

Всё верно, доменная модель это фундамент вашего приложения. И вы только что выяснили, что он у вас кривой и местами трещинами пошёл. Вы абсолютно уверены, что хотите продолжать строить на нём здание, стелить полы, ставить дорогую сантехнику и селить в нём своих детей?

Вот почему этот паттерн – важнейший. То, что на первый взгляд кажется практически абстрактной философией, на практике оборачивается жёсткими и регулярными прогонами acceptance тестов вашей модели. Именно единый язык прибивает вашу фантазию гвоздями к реальности, раз за разом повторяя – нет, ты опять ошибся, переделывай. Это больно. Вот только любая альтернатива – ещё хуже.

Избегать этой боли – примерно то же, что избегать посещения стоматолога. В конечном итоге выйдет и больнее и дороже. Технический долг называется долгом совсем не просто так, это даже не метафора, а простая констатация факта.

Конец

Как «конец»? А где же конкретика? Где примеры кода? А как же этот, как его, Value Object с агрегатами?

А нету. Конкретно эта история – не про это. Во-первых, она и так слишком длинная. Во-вторых, тактические паттерны DDD соотносятся со стратегическими примерно так же, как дизайн интерьера соотносится со строительством – и то и другое - важно и сложно, но в далеко не одинаковой степени.

Если вы практикуете всё вышесказанное – значит вы практикуете DDD. Если нет, то скрупулёзное следование тактическим паттернам DDD это не DDD и даже не Чистая Архитектура – это карго культ и потеря эффективности в угоду неверно понятым абстрактным принципам.
 
Тратить значительные усилия на качественное, системное и строгое написание кода предметной области следует не «потому что так правильно», а потому что этот код имеет огромное, ключевое значение для вашей системы, а его ожидаемое время жизни измеряется годами. Но если вы пренебрегли стратегическими паттернами, то скорее всего никакой особенной ценности ваш код попросту не представляет.

Почему Repository – самый неверно понимаемый паттерн в истории программирования? Почему Aggregate – центральный тактический паттерн DDD и ваш лучший друг? Почему Address – ужасный пример применения паттерна Value Object? Как DDD помогает не мешает вам писать высокоэффективный код? Об этом, и о многом другом, вы узнаете в совершенно другой истории.

Спасибо за внимание!

Комментарии (3)


  1. SolidSnack
    18.07.2025 20:07

    Очень много текста конечно, но как по мне вы в кучу скидываете понятия хоть чучуть пересекающиеся и из-за этого качество изложенного страдает (моё мнение). DDD никак не протеворечет/запрещает паттерны ООП или какие либо ещё. Домен не является неизменяемым фундаментом на всем цикле жизни приложения, домен нужен для строгой инверсии зависимости и изоляции кода который отвечает за бизнес логику от кода который помогает этой бизнес логике работать с технической точки зрения.


  1. onets
    18.07.2025 20:07

    Подскажите такую вещь - вот я сделал сущность, у нее свойства приватные (чтобы не нарушать ее состояние), и можно либо через конструктор значения передать, либо на каждое свойство реализовать метод UpdateXXX(value).

    Далее у нас есть контроллер, там DTO, допустим там UpdateDTO и все те же поля для простоты. Как это все подружить на ООП языке, не добавляя 100500 параметров в конструктор или методов UpdateXXX?