Зачем нужен Domain-driven design
Domain-driven design (DDD) — это способ проектировать системы так, чтобы код отражал не абстрактные технические требования, а реальные процессы бизнеса. Идея в том, что если разработчики понимают, как работает бизнес, то и система будет работать правильно, а не просто «как написано в ТЗ».
Для инженера DDD — это инструмент, который помогает писать код, который легче менять и поддерживать. Для руководителя — способ снизить затраты на развитие системы и уменьшить количество багов. Но DDD работает не всегда: если бизнес-процессы нестабильны или команда не понимает их до конца, то вместо пользы можно получить лишнюю сложность и замедление разработки.
Как устроен DDD
Предметная область и её границы
Предметная область (domain) — это всё, что происходит в бизнесе: заказы, клиенты, платежи, доставка. DDD предлагает разбить эту область на поддомены, каждый из которых решает свою задачу. Например, в интернет-магазине можно выделить:
- Заказы (создание, оплата, отмена);
- Доставка (расчёт сроков, выбор курьера);
- Возврат товаров (проверка условий, возврат денег).
Каждый поддомен должен быть ограниченным контекстом (bounded context) — это значит, что внутри него действуют свои правила и термины. Например, в поддомене «Заказы» слово «клиент» может означать человека, который делает заказ, а в поддомене «Доставка» — адрес, куда нужно привезти товар. Если эти два значения смешать, система начнёт работать неправильно.
Моделирование реальности
В DDD код пишется так, чтобы он отражал реальные процессы бизнеса. Например, если в магазине есть правило «заказ можно отменить только до отправки курьеру», то в коде это должно быть выражено явно:
class Order:
def cancel(self):
if self.status == "sent_to_courier":
raise CannotCancelOrderError("Заказ уже отправлен курьеру")
self.status = "cancelled"
Если вместо этого написать просто order.status = "cancelled", то рано или поздно кто-то забудет про правило и отменит заказ, который уже нельзя отменить. DDD заставляет разработчика думать о таких правилах на этапе проектирования, а не исправлять ошибки постфактум.
Язык, понятный всем
В DDD есть понятие единого языка (ubiquitous language) — это набор терминов, которые одинаково понимают и разработчики, и бизнес. Например, если в компании говорят «заказ», а не «ордер», то и в коде должно быть Order, а не Transaction. Если бизнес-аналитик говорит «клиент может вернуть товар в течение двух недель», то и в коде должно быть что-то вроде return_period = 14.
Если язык не единый, возникают проблемы:
- Разработчики и бизнес говорят на разных языках и не понимают друг друга;
- В коде появляются термины, которые никто не может объяснить;
- При изменении бизнес-правил приходится менять и код, и документацию.
Архитектура: слои и зависимости
DDD предлагает разделять код на слои:
- Представление (UI, API) — как пользователь взаимодействует с системой;
- Приложение (application layer) — оркестрация бизнес-логики;
- Домен (domain layer) — сама бизнес-логика;
- Инфраструктура (базы данных, внешние сервисы).
Главное правило: домен не должен зависеть от инфраструктуры. Например, если бизнес-логика хранится в базе данных PostgreSQL, то при переходе на MongoDB код домена не должен меняться. Это позволяет менять технические детали, не затрагивая бизнес-правила.
Что даёт DDD инженеру
Код, который легче менять
Если система построена по принципам DDD, то:
- Логика каждого поддомена изолирована, и изменения в одном месте не ломают другое;
- Код отражает реальные бизнес-процессы, поэтому его легче понять;
- Тесты пишутся проще, потому что бизнес-правила выражены явно.
Например, если в магазине меняется правило отмены заказа, то изменения нужно внести только в один класс Order, а не искать все места, где это правило могло быть реализовано.
Меньше багов из-за неявных зависимостей
В системах без DDD часто возникают баги, потому что одно и то же правило реализовано в нескольких местах. Например, правило «заказ можно отменить только до отправки курьеру» может быть:
- В коде отмены заказа;
- В коде отправки заказа курьеру;
- В коде возврата денег.
Если в одном из этих мест правило не учтено, возникает баг. В DDD такое правило реализуется один раз, и все остальные части системы используют его.
Ясность в сложных системах
В больших системах без DDD часто возникает ситуация, когда никто не понимает, как работает та или иная часть кода. DDD помогает разбить систему на понятные части, каждая из которых решает свою задачу. Например, если нужно понять, как работает возврат товаров, можно посмотреть только на поддомен «Возврат», а не разбираться во всей системе.
Что даёт DDD руководителю
Экономия на поддержке и развитии
Системы, построенные по принципам DDD, легче поддерживать и развивать. Это значит:
- Меньше времени тратится на исправление багов, потому что бизнес-правила выражены явно и не дублируются;
- Новые функции добавляются быстрее, потому что не нужно переписывать половину системы;
- Легче вводить новых разработчиков в проект, потому что код отражает реальные бизнес-процессы.
Механизм экономии простой: если бизнес-правила реализованы в одном месте, то при их изменении не нужно искать все места, где они могли быть продублированы. Это сокращает время на разработку и тестирование.
Меньше рисков при изменениях
Если бизнес-процессы меняются, то в системе с DDD изменения затрагивают только один поддомен, а не всю систему. Например, если в магазине меняется правило возврата товаров, то изменения нужно внести только в поддомен «Возврат», а не переписывать всю систему.
Механизм снижения рисков: если логика изолирована, то вероятность сломать что-то другое при изменении минимальна. Это особенно важно в больших системах, где одно изменение может затронуть десятки зависимостей.
Лучшее взаимодействие с бизнесом
DDD заставляет разработчиков и бизнес говорить на одном языке. Это значит:
- Меньше недопонимания между командами, потому что термины понятны всем;
- Быстрее принимаются решения, потому что все понимают, о чём идёт речь;
- Легче документировать систему, потому что термины не нужно переводить с «языка бизнеса» на «язык разработчиков».
Механизм улучшения взаимодействия: если все используют одни и те же термины, то меньше шансов, что кто-то поймёт что-то неправильно. Это сокращает количество ошибок и ускоряет разработку.
Когда DDD не работает
Бизнес-процессы нестабильны
Если бизнес-процессы часто меняются, то DDD может стать обузой. Например, если в стартапе каждый месяц меняются правила работы с заказами, то каждый раз придётся переписывать поддомен «Заказы». В таких случаях лучше использовать более гибкие подходы, например, event sourcing или CQRS.
Механизм проблемы: если бизнес-процессы меняются быстрее, чем успевает адаптироваться код, то DDD превращается в тормоз. Вместо того чтобы ускорять разработку, он начинает её замедлять, потому что каждое изменение требует перепроектирования.
Команда не понимает предметную область
Если разработчики не понимают, как работает бизнес, то DDD превращается в бюрократию. Например, если команда не знает, что такое «возврат товара» и какие у него бывают статусы, то поддомен «Возврат» будет реализован неправильно.
Механизм проблемы: если разработчики не понимают предметную область, то они не могут правильно смоделировать её в коде. В результате система будет работать не так, как нужно бизнесу, а как её поняли разработчики.
Система слишком простая
Если система решает одну простую задачу, то DDD может быть избыточным. Например, если нужно написать скрипт, который раз в день выгружает данные из одной базы в другую, то DDD не нужен.
Механизм проблемы: если система простая, то разделение на поддомены и слои только усложняет её, а не упрощает. В таких случаях лучше использовать простые решения, например, монолит или скрипты.
Тупик: DDD как самоцель
Часто команды начинают применять DDD, потому что «так правильно», а не потому что это нужно для решения конкретной задачи. В результате:
- В коде появляются ненужные абстракции (например,
OrderFactory,OrderRepository), которые усложняют систему, а не упрощают её; - Разработчики тратят время на проектирование, а не на написание работающего кода;
- Система становится сложнее, чем нужно.
Механизм проблемы: если DDD используется без понимания, зачем он нужен, то он превращается в бюрократию. Вместо того чтобы помогать решать задачи, он начинает их усложнять.
Позиция редакции
Domain-driven design — это полезный инструмент, но только в тех случаях, когда:
- Бизнес-процессы стабильны и понятны команде. Если процессы меняются чаще, чем раз в несколько месяцев, то DDD может стать обузой;
- Система достаточно сложная, чтобы оправдать разделение на поддомены. Если система решает одну простую задачу, то DDD не нужен;
- Команда готова тратить время на проектирование, а не только на написание кода. Если команда не понимает предметную область, то DDD не поможет.
Позиция неверна в следующих случаях:
- Если бизнес-процессы нестабильны и часто меняются. В этом случае DDD будет только замедлять разработку;
- Если система слишком простая и не требует сложного проектирования. В этом случае DDD только усложнит её;
- Если команда не понимает предметную область. В этом случае DDD превратится в бюрократию, а не в инструмент решения задач.
Единственное исключение — когда DDD используется как способ навести порядок в уже существующей системе. В этом случае даже частичное применение DDD может помочь разобраться в коде и сделать его более управляемым. Но и здесь важно не переусердствовать: если система простая, то лучше использовать более простые подходы.