Domain-Driven Design: Tackling Complexity in the Heart of Software Эванса — лучшая книга о проектировании действительно больших enterprise-приложений, что я читал. Видимо это мнение разделяют многие другие разработчики и проектировщики, потому что Entity и ValueObject, Repository и Specification встречаются почти в каждой большой кодовой базе. Но вот незадача, Ubiquitous Language (единый язык) и Bounded Context (контекст предметной области) в чужом коде я не видел ни разу. И здесь зарыта очень большая собака.
Зачем нужен единый язык?
Порой, в нашей профессии, сформулировать требование к продукту бывает сложнее, чем реализовать его. Разные предметные области сложны по своему. В перевозках вы столкнетесь с концепцией овербукинга, в продажах со «специальными предложениями», в трейдинге — со «стоп-лосс трейлинг ордерами».
Единый язык — это базовая концепция борьбы со сложностью. Если предметная область итак требует изучения, то давайте не будем делать вещи еще сложнее и заставлять людей разбираться еще и в вашей модели предметной области. Будем называть вещи одинаково как в спецификации, так и в коде. Пример ниже наглядно иллюстрирует использование этой концепции:
Единый язык on
var ticket = cashdesk.Sale(seat, count); // Это может понять даже не технический специалист
Единый язык off
ticket.State = TicketState.Sold; // А это нет
ticketRepository.Update(ticket);
Проблемы с отсутствием единой терминологии я ощутил на собственной шкуре. Комманда разрабатывала систему для продажи билетов на мероприятия онлайн. Новая версия продукта была призвана заменить старую, расширить функционал и исправить ошибки проектирования первой версии. Один из крупных косяков первой системы состоял в допущении, что можно продать только один билет на место. Но оказалось, что существуют «танцевальный партер» и «столик на 5 человек», которые удобно представлять одним местом в зале.
Так у программистов появилось абстрактное понятие «продаваемое место», связывающее место в зале, с количеством билетов, которое можно продать на это место. Для обычных залов с рядами и местами оно было равно единице, а для танцевального партера — ограничивалось вместимостью зала.
Заметили как сложно воспринимается предыдущий абзац?
С технической точки зрения проблема была решена идеально, но каждому следующему программисту приходилось объяснять, что такое продаваемое место, чем отличается от квоты и почему в UI это называется билет. В итоге эта путаница вылилась в очень неудачный промах при оценке сложности спринта и спринт был провален.
Зачем нужны контексты?
Проблема №1
К сожалению, преимущества единого языка мы не получаем бесплатно. Единый язык требует кропотливого анализа требований и консультаций с экспертами предметной области. Зачастую, так называемые эксперты озвучивают противоположные точки зрения о бизнес-процессах и терминах. Разрабатывая приложения в рамках «единого языка» вы инвестируете много времни в создание внутренней базы знаний клиента. Безусловно это хорошо. Проблема в том, что почти наверняка эта работа не заложена в бюджет на разработку приложения. Разработка по всем канонам DDD и с применением единого языка — дорогое и не быстрое удовольствие.
Проблема №2
«Единый язык» на самом деле не является «единым» для всего приложения и живет только в рамках контекста. И Эванс прямо об этом пишет. К сожалению, в книге сей факт формируется мягко, скорее как рекомендация, а не правило. Я бы набрал параграф красным КАПСОМ и поместил в рамку. Попытка создания единой модели предметной области обречена на провал. Один и тот же термин может значить разные вещи в разных контекстах. Я могу сходу назвать несколько примеров из разных предметных областей, но все они требуют специального объяснения, поэтому ограничусь синтетическим примером: салат рекурсивный — помидоры, огурцы, салат. Ну вы поняли…
Проблема №3
Никому не нужна модель предметной области целиком. Она скорее всего слишком сложна даже для крутых стратегов и аналитиков. Эти ребята попросят представить им данные в совершенно ином, понятном им, виде и разрезе. Контексты реализуют принцип «разделяй и властвуй», что помогает быстрее обучать пользователей и подключать новых членов в комманду разработки.
В качестве бонуса
Bounded Context — это реализация принципа слабой связанности на более высоком уровне — на уровне подсистемы (модуля). Такая сегментация позволит вам более гибко управлять разработкой, вплоть до исключения или переписывания с нуля целых модулей, возможно, что даже с полной заменой комманды и/или технологического стека модуля.
Автор: marshinov