Domain-Driven Design, или предметно-ориентированное проектирование, одна из самых умных идей в истории разработки программ. И одна из самых опасных. Я говорю это как человек, который сам много лет применяет её принципы и считает их фундаментальными. Просто острый нож хорош в руках повара и опасен в руках ребёнка, который решил, что им удобно открывать консервы.
Если коротко, DDD ставит во главу угла бизнес-логику. Не базу данных, не фреймворк, не красивые технологии, а саму предметную область: как устроен бизнес, какие в нём есть понятия, правила и процессы. Код строится как модель этой предметной области, и программисты говорят на том же языке, что и бизнес. Звучит идеально. Так в чём же опасность?
Что в DDD действительно ценно
Начну с хорошего, потому что без этого статья получится несправедливой. Самая важная идея DDD, на мой взгляд, это единый язык. Когда в бизнесе говорят «заявка», «сделка», «отгрузка», то и в коде должны быть «заявка», «сделка», «отгрузка». Не «order_item_v2», не «entity_type_3», не что-то, что программист придумал в пятницу вечером.
Я писал об этом в статье про нефункциональные требования: соответствие кода языку предметной области это требование, которое кажется мелочью, а потом определяет, сможет ли новый разработчик понять систему без двухнедельного погружения. Если язык совпадает, бизнес и программисты понимают друг друга. Если нет, каждая задача начинается с перевода, и в переводе неизбежно что-то теряется.
Вторая ценная идея: границы контекстов. В большой компании слово «клиент» может означать разное в отделе продаж, в бухгалтерии и в службе доставки. DDD предлагает не пытаться создать одного универсального «клиента» на всю компанию, а честно признать, что у каждого контекста своя модель. Это спасает от монстров, в которых сто полей и ни одно не понятно до конца.
Ресторанный агрегат для яичницы
Теперь об опасности. DDD создавался для сложных предметных областей: банковские системы, логистика, страхование, где бизнес-правил сотни, они переплетаются и постоянно меняются. Для этого в DDD есть целый арсенал: агрегаты, сущности, объекты-значения, доменные события, репозитории, сервисы предметной области, слои и абстракции.
И вот здесь начинается беда. Программист прочитал книгу, вдохновился и применяет весь арсенал к простому проекту. Справочник товаров, форма заявки, список клиентов. Это то, что называется CRUD: создать, прочитать, обновить, удалить. Бизнес-логики там почти нет. А в коде появляются пять слоёв абстракции, десятки классов на каждую сущность, и чтобы добавить одно поле в форму, нужно изменить восемь файлов.
Это как поставить на кухне огромный профессиональный ресторанный агрегат с манипуляторами и колбами, чтобы пожарить одно яйцо. Агрегат прекрасен. В ресторане на двести посадочных мест он незаменим. Но на домашней кухне он занимает всё пространство, требует обслуживания и сложной настройки, а яичница на нём получается не лучше, чем на обычной сковородке. Только дольше и дороже.
Этот эффект называют переусложнением, по-английски overengineering. И его стоимость реальна: разработка медленнее, новых людей сложнее вводить в проект, ошибок больше, потому что больше мест, где они могут спрятаться.
Когда DDD опасен
В простых проектах. Если система в основном хранит и показывает данные, а правил в ней мало, тактические паттерны DDD создают ненужные слои. Единый язык при этом остаётся полезным всегда, а вот агрегаты и доменные события стоит приберечь.
Без доступа к экспертам предметной области. DDD строится на постоянном диалоге с людьми, которые знают бизнес. Если такого диалога нет, программисты начинают моделировать предметную область по своим представлениям, и получается сложная, тщательно спроектированная модель того, чего в реальности не существует. Это хуже, чем простой код без всякой модели, потому что его сложнее переделать.
В командах с низким уровнем подготовки. DDD требует зрелости. Чтобы правильно провести границы контекстов и выделить агрегаты, нужен опыт. Начинающая команда, которая применяет DDD по книге, часто создаёт не модель предметной области, а лабиринт, в котором сама же потом теряется.
На ранней стадии стартапа. Когда предметная область ещё не понятна и меняется каждую неделю, тщательно выстроенная доменная модель превращается в балласт. Её приходится перестраивать при каждом развороте бизнеса.
Мой подход: язык всегда, тяжёлая артиллерия по необходимости
Я разделяю DDD на две части. Стратегическая часть, то есть единый язык, понимание предметной области, границы контекстов, полезна практически всегда, даже в маленьком проекте. Она не добавляет кода, она добавляет ясности. Тактическая часть, то есть агрегаты, репозитории, доменные события и прочие паттерны, это тяжёлая артиллерия, и её нужно применять там, где действительно сложная бизнес-логика.
Часто в одной системе есть и то и другое. Например, в учётной системе справочники и отчёты простые, а расчёт себестоимости или логика резервирования товара на складе действительно сложные. Значит, для справочников подойдёт простой код, а для расчётов уже стоит выстраивать полноценную модель. Не нужно стричь всю систему под одну гребёнку.
У нас в Ingello единый язык зашит в саму технологию. В Fractal схема данных на SFL описывает сущности предметной области их собственными именами, и генератор строит из неё код, базу и интерфейсы. Названия не теряются по дороге, потому что дороги нет: от описания к коду один шаг. А сложную логику мы добавляем руками там, где она действительно нужна, и только там.
Что сделать, если Вы уже в DDD
Если Ваш проект построен по DDD и разработка идёт медленно, проведите честную ревизию. Посмотрите, в каких частях системы действительно сложная бизнес-логика, а где обычный CRUD, завёрнутый в пять слоёв. Для вторых упростите код. Это не предательство принципов, это их правильное применение.
А если Вы только выбираете подход, начните с единого языка: составьте словарь понятий бизнеса и договоритесь, что код использует именно эти слова. Это даст восемьдесят процентов пользы DDD почти без затрат. Остальные двадцать процентов берите тогда, когда сложность предметной области сама об этом попросит.