Снова о разработке на основе предметной области (Domain-Driven Design, DDD)

Original author: Gabriel Schenker
  • Translation

Введение


Слишком много раз я встречал приложения, о которых говорили, что у них есть модель предметной области и приложение было спроектировано на основе это предметной области. Однако в действительности всё, что я видел, было коллекцией сущностей (я бы даже сказал DTO), имеющих кучу свойств без какой бы то ни было реальной логики, связанной с сущностью. Кроме того, я могу найти много сервисов всех видов, которые содержат красочную смесь бизнес-логики и/или инфраструктуры. Если приложение вдобавок использует шину сообщений (как NServiceBus, Mass Transit Bus или Azure Bus), то конечно же заметно, что некие сообщения передаются от одного модуля к другому или нескольким модулям. К сожалению, сообщения часто имеют очень обобщённые названия, содержащие слова “обновить”, “изменить”, “добавить” или “удалить”, и несут большое количество полезной нагрузки — десятки разнообразных свойств. Часто из названия сообщения совершенно не очевидно, является ли оно командой или событием, и чтобы определить это, приходится глубоко зарыться в реализацию.

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

DDD — Что важно?


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

В поисках единого языка


Когда мы начинаем новый проект, то мы должны сначала попробовать по-настоящему понять предметную область бизнеса, для которого мы строим приложение. Нам нужно поговорить с заинтересованными лицами и экспертами в области (да, зачастую это представители клиента, для которого мы создаём программное обеспечение) и попытаться понять их язык. Нам надо внимательно слушать, что они говорят и как они говорят. Пользуются ли они определенными словами, которые понятны и используются таким же образом всеми, работающими в этой предметной области? Является ли смысл этих слов однозначным? Если нет, то мы должны задавать вопросы и требовать от экспертов более подробного описания их терминологии, либо смены формулировок, или даже использовать некие аналогии. Одна из моих любимых «игр» — это спросить экспертов о том, как бы они делали свои задачи в отсутствие компьютера. Что будет действиями, а что объектами, предметами, концепциями или существительными?

Со временем (да, это может занять несколько дней или недель, чтобы сделать это правильно), мы будем иметь общий словарь, понятный всем: заинтересованным сторонам, экспертам в предметной области, бизнес-аналитикам, архитекторам, разработчикам и тестировщикам. Мы будем называть этот словарь или «язык» «единым языком».

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

Разделяя сложную проблему


После того, как мы нашли или определили единый язык предметной области, мы можем начать моделировать домен. Но намного чаще предметная область бизнеса, для которого мы собираемся написать программное обеспечение, является достаточно сложной, чтобы понять все сразу и/или легко упустить что-то из виду. Таким образом, мы должны начать разделять всю сложную область в меньшие части меньшей сложности. Это тот же подход, что и во время еды — мы не в состоянии проглотить весь стейк сразу, но можем разрезать его на куски, а затем съесть один кусок за другим.

Мы можем сделать так с каждой предметной областью. С помощью экспертов необходимо определить под-домены или области, которые мы можем изолировать друг от друга и посмотреть на них в отдельности, чтобы решить их в по-отдельности. Мы называем эти под-домены ограниченными контекстами (прим. пер. — bound contexts). Ограниченный контекст — это часть всей предметной области, имеющая четкие границы, взаимодействующая через четко определенные интерфейсы с другими ограниченными контекстами и которая может быть определена индивидуально.

Определение интерфейсов и контрактов


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

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

Мы должны чётко различать команды и события. Команда всегда имеет только одну цель. Цель команды может либо принять, либо отклонить команду. Результатом выполнения команды, как правило, является изменение в системе. Было сделано что-то, что изменило состояние модели. С другой стороны, событие может иметь от нуля до нескольких подписчиков. Да, это верно, что события могут быть проигнорированы… но события никогда не могут быть отклонены, поскольку они сообщают нам о том, что уже произошло. Чтобы чётко отличать команды от событий, мы должны использовать императивные имена для команд; события же должны называться в прошедшем времени. Имена должны использовать созданный нами единый язык.

Чего мы должны избегать в DDD


Если мы хотим использовать проектирование на основе предметной области при разработке, мы должны избегать нескольких ловушек.

Строить приложение только вокруг данных

В прошлом большинство бизнес-приложений были ориентированы на данные. В первую очередь архитекторы и инженеры создавали схему данных, а всё остальное следовало за ней. Люди говорили"… в конце дня, только данные имеют смысл… Данные, которые мы собираем — наша ценность. Приложения, которые собирают и/или используют данные появляются и исчезают, но сами данные остаются… ". И хотя данные остаются, просто неправильно утверждать, что данные — это всё, что имеет значение! Данные сами по себе не имеют смысла. Отдельно данные лежат мёртвым грузом и только логика придаёт им смысл. И одни и те же данные имеют различное значение в разных контекстах. Таким образом, мы всегда должны начинать с контекста и логики.

Точка зрения, что данные — это главная ценность — приводит к тому, что базы данных часто используются в качестве точки интеграции. Многие различные приложения используют одну и ту же БД. Это приводит к множеству проблем в долгосрочной перспективе. Чтобы дать вам хорошую аналогию, того, о чем я говорю, давайте представим, что у меня есть кошелек с деньгами в кармане. Теперь мой друг поиздержался и хочет позаимствовать 10 долларов. Как наш мир работает в этом случае? а) друг вежливо просит меня одолжить 10 баксов, и я открываю свой бумажник, чтобы передать ему деньги или б) друг достаёт мой бумажник из моего кармана (я не замечаю этого), и забирает 10 $ купюру из него. Таким образом: никогда не используйте базу данных, как точку интеграции в бизнес-приложениях.

Когда я разрабатываю приложение, ERD (ER-модель данных — прим.пер.) является одной из наименее важных вещей для меня. Я не позволяю себе и модели моей предметной области быть подконтрольными хранилищу данных или модели данных. Для меня бизнес-приложение не означает автоматически, что я должен использовать СУБД, и что моя модель данных должна быть в 3-й нормальной форме!

Говорить о деталях реализации

Главная идея DDD не в сущностях, сервисах, применении фабрики либо репозитория. Это все детали реализации. Они не имеют никакого реального смысла пока мы не сделали домашнее задание и определили единый язык, ограниченные контексты, а также интерфейсы и контракты. Если мы начнем заниматься реализацией слишком рано, то в результате получим анемичную модель, состоящую из коллекции DTOs, окруженных огромным числом сервисов и размазанной повсюду бизнес-логики.
Использовать технический шум

В нашем приложении мы никогда не должны использовать понятия или слова вроде save, update, insert, delete, handle, manage. Это либо очень технические термины либо абстрактные понятия, не имеющие конкретного смысла. Если у меня есть сохранение, то одна из первых вещей, которые я делаю — это убираю кнопку «Сохранить» из пользовательского интерфейса приложения. Сохранение — не бизнес-концепция. Представьте себе мир без компьютеров — используют ли реальные люди высказывания вроде "..., позвольте мне сохранить это ..." (одно исключение — это сохранение денег на банковском счету). Кроме того, в реальном бизнесе, мы никогда не применяем слова «Удалить» или «Добавить» или «Обновить» к чему-либо. Разве мы удаляем сотрудника, когда он больше не требуется компании?

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

Транзакции БД

Хотя транзакции БД — это хорошая вещь, не стоит злоупотреблять ими. Бизнес-транзакции являются гораздо более важными, нежели транзакции БД. Несмотря на то, что по определению, транзакции БД полностью целостны (и выполняются быстро), бизнес-транзакции таковыми не являются. Пример бизнес-транзакции, с которой вы все вероятно хорошо знакомы — то, что происходит в Starbucks, когда утром вы заказываете любимый кофе. Это «долгий» процесс со многими возможно «противоречивыми» промежуточными состояниями и асинхронными задачами. Тем не менее, всё это работает, масштабируется и широко принято всеми.

Таким образом, при моделировании вашей предметной области с помощью DDD, не думайте о транзакциях БД (или еще хуже «распределенных транзакциях») вообще. Думайте о возможных действиях, их результатах и о сценарии отмены действия в случае неудачи. И вы увидите, что вы будете решать главным образом бизнес-проблемы, а не бороться с техническими проблемами.

Вывод


Чтобы успешно использовать DDD надо сделать несколько вещей очень хорошо с самого начала: определить общий словарь (единый язык), разделить общую сложную проблемную область в меньшие под-домены, называемые ограниченными контекстами, и тщательно определить границы и контракты, используемые между отдельными ограниченными контекстами. С другой стороны, мы должны избегать определенных практик, которые слишком часто используются разработчиками программного обеспечения: строить приложение только вокруг данных при моделировании предметной области только вокруг данных (без логики); сосредоточения внимания на деталях реализации (сущностях, сервисах и т.д.) взамен основных концепций, рассмотренных выше; использования обобщенных и специфических для разработчиков терминов и понятий при реализации приложения; и, наконец, переоценке ценности транзакций баз данных взамен акцентирования на бизнес-процессах или транзакциях.
  • +12
  • 47.6k
  • 4
Share post

Comments 4

    –2
    Пришлось перейти в исходную статью, чтобы понять, что речь о проблемно-ориентированном проектировании (Domain-driven design), а не о Data Display Debugger, например. К счастью, там-то расшифровка дана неоднократно. В переводе на текущий момент такая расшифровка вообще не наблюдается. А где-нибудь в начале она бы очень не помешала.
      +2
      Добавил, действительно, упустил этот момент из виду.
        +1
        Очень надеюсь не слышать термин DDD как «проблемно-ориентированном проектировании», предпочитаю использовать «предметно».
        А то всегда всплывает в голове книга Нильссона в русском варианте — яркий пример как не стоит переводить техническую литературу.

        Еще бы оставил оригинальные термины в переводе (хоть в скобках) — единый язык (ubiquitous language).
          +1
          Да, сверился с гуглом, «предметно-ориентированное» более уместно. Меня ввела в заблуждение Википедия, каюсь. Мое замечание больше касалось именно отсутствия любых пояснений о том, что же такое DDD: как оригинальной расшифровки, так и перевода.

      Only users with full accounts can post comments. Log in, please.