Проектирование модели предметной области микрослужбы
Совет
Это содержимое является фрагментом из электронной книги, архитектуры микрослужб .NET для контейнерных приложений .NET, доступных в документации .NET или в виде бесплатного скачиваемого PDF-файла, который можно читать в автономном режиме.
Определите одну расширенную модель предметной области для каждой бизнес-микрослужбы или ограниченного контекста.
Ваша цель состоит в создании одной целостной модели предметной области для каждой бизнес-микрослужбы или ограниченного контекста (BC). Однако следует помнить, что BC или бизнес-микрослужба иногда может состоять из нескольких физических служб, совместно использующих одну модель предметной области. Модель предметной области должна определять правила, поведение, бизнес-язык и ограничения для одного ограниченного контекста или бизнес-микрослужбы, которая его представляет.
Шаблон сущности предметной области
Сущности представляют объекты предметной области и в первую очередь определяются их идентификаторами, непрерывностью и сохраняемостью с течением временем, а не только атрибутами, которые их составляют. Как говорит Эрик Эванс, "объект, в первую очередь определенный его удостоверением, называется сущностью". Сущности очень важны в модели домена, так как они являются основой для модели. Следовательно, вы должны идентифицировать и разрабатывать их очень внимательно.
Идентификатор сущности может пересекать несколько микрослужб или ограниченных контекстов.
Один и тот же идентификатор (то есть то же значение Id
, хотя и необязательно та же сущность предметной области) может быть смоделирован в нескольких ограниченных контекстах или микрослужбах. Однако это не означает, что в нескольких ограниченных контекстах будет реализована одна и та же сущность с теми же атрибутами и логикой. Вместо этого атрибуты и поведение сущностей в каждом ограниченном контексте находятся в рамках требований предметной области каждого конкретного ограниченного контекста.
Например, сущность "покупатель" может иметь большинство атрибутов человека, определенных в сущности "пользователь" в микрослужбе профиля или идентификации, включая идентификатор пользователя. Но сущность "покупатель" в микрослужбе заказов может иметь меньше атрибутов, так как только некоторые данные покупателя связаны с обработкой заказа. Контекст каждой микрослужбы или ограниченного контекста влияет на его модель предметной области.
Помимо реализации атрибутов данных, сущности предметной области должны реализовывать поведение.
Сущность предметной области в DDD должна реализовывать логику предметной области или поведение, связанное с данными этой сущности (объекта, доступного в памяти). Например, в рамках класса сущности заказа должна быть задана бизнес-логика и операции, реализованные как методы для таких задач, как добавление позиции заказа, проверка данных и итоговый расчет. Методы сущности обеспечивают инварианты и правила сущности вместо определения этих правил как распределенных на уровне приложения.
На рис. 7-8 показана сущность предметной области, реализующая не только атрибуты данных, но также и операции или методы с логикой, связанной с предметной областью.
Рис. 7-8. Пример структуры сущности предметной области, реализующей данные и поведение
Сущность модели предметной области реализует поведение через методы, то есть это не "слабая" модель. Конечно, иногда у вас могут быть сущности, не реализующие никакую логику в рамках класса сущностей. Это может произойти в дочерних сущностях в агрегате, если дочерняя сущность не имеет никакой специальной логики, так как большая часть логики определяется в корне агрегата. При наличии сложной микрослужбы с логикой, реализуемой в классах службы вместо сущностей предметной области, вы рискуете попасть в слабую модель предметной области, что объясняется в следующем разделе.
Сравнение расширенной и слабой моделей предметной области
В своей записи в блоге AnemicDomainModel Мартин Фаулер (Martin Fowler) описывает слабую модель предметной области следующим образом.
Основным признаком слабой модели предметной области является то, что на первый взгляд она выглядит как что-то реальное. Там есть объекты, многие из которых названы по именам в пространстве предметной области, и эти объекты связаны широкими отношениями и структурой, которую имеют настоящие модели предметной области. Замкнутый круг возникает, когда вы посмотрите на поведение и поймете, что это вряд ли поведение в этих объектах, делающее их чем-то большим, чем контейнеры методов получения и задания.
Конечно, при использовании слабой модели предметной области эти модели данных будут использоваться из набора объектов службы (обычно называемого слоем предметной области), который фиксирует всю бизнес-логику или логику предметной области. Слой предметной области находится вверху модели данных и использует модель данных так же, как данные.
Слабая модель предметной области — это просто структура процедурного стиля. Слабые объекты сущности не являются реальными объектами из-за отсутствия поведения (методов). Они лишь содержат свойства данных, и, таким образом, это не объектно-ориентированный проект. Помещая все поведение в объекты служб (слой предметной области), вы, в сущности, получаете плохо структурированный код или сценарии транзакции, таким образом теряя преимущества, которые предоставляет модель предметной области.
Как бы там ни было, если вы имеете очень простой ограниченный контекст или микрослужбу (службу CRUD), слабая модель предметной области в форме объектов сущности только со свойствами данных может быть достаточно хороша, и, возможно, не имеет смысла реализовывать более сложные шаблоны DDD. В таком случае это будет просто модель сохраняемости, так как вы намеренно создали сущность только с данными для целей CRUD.
Именно поэтому архитектуры микрослужб прекрасно сочетаются с мультиархитектурным подходом, учитывающим каждый ограниченный контекст. Например, в приложении eShopOnContainers микрослужба заказов реализует шаблоны DDD, а микрослужба каталога, представляющая собой простую службу CRUD, — нет.
Некоторые говорят, что слабая модель предметной области является антишаблоном. В действительности это зависит от того, что вы реализуете. Если вы создаете довольно простую микрослужбу (например, службу CRUD), то слабая модель предметной области не является антишаблоном. Однако если вам предстоит иметь дело со сложной предметной областью микрослужбы, имеющей множество постоянно меняющихся бизнес-правил, слабая модель предметной области может быть антишаблоном для этой микрослужбы или ограниченного контекста. В этом случае проектирование в соответствии с расширенной моделью предметной области с сущностями, содержащими данные и поведение, а также с реализацией дополнительных шаблонов DDD (агрегатов, объектов значений и т. п.) может обеспечить огромные преимущества для долгосрочного успешного существования такой микрослужбы.
Дополнительные ресурсы
DevIQ. Сущность предметной области
https://deviq.com/entity/Мартин Фоулер (Martin Fowler). Модель предметной области
https://martinfowler.com/eaaCatalog/domainModel.htmlМартин Фоулер (Martin Fowler). Слабая модель предметной области
https://martinfowler.com/bliki/AnemicDomainModel.html
Шаблон объекта значения
Как заметил Эрик Эванс (Eric Evans), "многие объекты не имеют концептуальной идентификации, эти объекты описывают определенные характеристики предмета".
Сущности требуется идентификатор, но в системе имеется множество объектов, у которых он отсутствует, например шаблон объекта значения. Объект значения — это объект без концептуального идентификатора, который описывает аспект предметной области. Это объекты, создаваемые для представления элементов структуры, которые вы считаете временными. Вас интересует, что они собой представляют, а не чем они являются. В качестве примеров можно привести числа и строки, но это также могут быть высокоуровневые понятия, такие как группы атрибутов.
То, что является сущностью в микрослужбе, может не быть сущностью в другой микрослужбе, поскольку во втором случае ограниченный контекст может иметь другой смысл. Например, адрес в приложении электронной коммерции может вообще не иметь идентификатора, так как он может лишь представлять группу атрибутов профиля клиента — человека или организации. В этом случае адрес должен классифицироваться как объект значения. Однако в приложении для компании, обслуживающей электроэнергетическую систему общего пользования, адрес клиента может быть важен для предметной области бизнеса. Следовательно, адрес должен иметь идентификатор, чтобы систему выставления счетов можно было привязать непосредственно к адресу. В этом случае адрес должен классифицироваться как сущность предметной области.
Человек с именем и фамилией обычно является сущностью, так как он имеет идентификатор, даже если имя и фамилия совпадают с другим набором значений, например, если такие имя и фамилия также связаны и с другим человеком.
Объектами значений трудно управлять в реляционных базах данных и моделях ORM, таких как Entity Framework (EF), тогда как в документоориентированных базах данных их гораздо проще реализовать и использовать.
В EF Core 2.0 и более поздних версий представлена функция Принадлежащие сущности, которая упрощает обработку объектов-значений, как мы увидим во всех подробностях позже.
Дополнительные ресурсы
Мартин Фоулер (Martin Fowler). Шаблон объекта значения
https://martinfowler.com/bliki/ValueObject.htmlОбъект значения
https://deviq.com/value-object/Объекты значений при разработке на основе тестирования
https://leanpub.com/tdd-ebook/read#leanpub-auto-value-objectsЭрик Эванс (Eric Evans). Предметно-ориентированное проектирование (DDD). Структуризация сложных программных систем. (Книга, в которой рассматриваются объекты значений)
https://www.amazon.com/Domain-Driven-Design-Tackling-Complexity-Software/dp/0321125215/
Шаблон агрегата
Модель предметной области содержит кластеры разных сущностей данных и процессы, которые могут управлять значительными областями функций, такими как выполнение заказов или инвентаризация. Более мелкой единицей DDD является агрегат, описывающий кластер или группу сущностей и поведений, которые могут рассматриваться как единый блок.
Агрегат обычно определяется на основе необходимых транзакций. Классический пример — заказ, который также содержит список позиций заказа. Позиция заказа обычно является сущностью. Но это будет дочерняя сущность в агрегате заказа, который также будет содержать сущность "заказ" как корневую сущность, обычно называемую корнем агрегата.
Идентифицировать агрегаты может быть довольно трудно. Агрегат — это группа объектов, которые должны быть согласованы друг с другом, но нельзя просто выбрать группу объектов и пометить их как агрегат. Вы должны начать с концепции предметной области и обдумать сущности, которые используются в наиболее распространенных транзакциях, связанных с этой концепцией. Именно такие сущности, которые должны быть согласованными на уровне транзакций, и формируют агрегат. Обдумывание транзакционных операций, скорее всего, является лучшим способом определения агрегатов.
Шаблон корня агрегата или корневой сущности
В агрегате содержится по крайней мере одна сущность — корень агрегата, также называемый корневой или основной сущностью. Кроме того, в агрегате может находиться несколько дочерних сущностей и объектов значений, причем все сущности и объекты работают совместно для реализации необходимого поведения и транзакций.
Цель корня агрегата заключается в обеспечении согласованности агрегата; он должен быть единственной точкой входа для обновлений агрегата с помощью методов или операций в классе корня агрегата. Изменения сущностей в агрегате должны происходить только через корень агрегата. Это защитник согласованности агрегата, учитывающий все инварианты и правила целостности, соблюдение которых может требоваться в агрегате. Если изменить дочернюю сущность или объект значения независимо, корень агрегата не сможет обеспечить правильное состояние агрегата. Это было бы похоже на стол с оторванной ножкой. Обеспечение согласованности является главной задачей корня агрегата.
На рис. 7-9 показаны примеры агрегатов, например агрегат "покупатель", содержащий одну сущность (корень агрегации Buyer). Агрегат "заказ" содержит несколько сущностей и объект значения.
Рис. 7-9. Пример агрегатов с одной или несколькими сущностями
Модель предметной области DDD состоит из агрегатов, агрегат может иметь только одну сущность или несколько сущностей, а также включать объекты значений. Обратите внимание, что агрегат Buyer может иметь дополнительные дочерние сущности в зависимости от предметной области, как, например, в микрослужбе заказов в эталонном приложении eShopOnContainers. На рис. 7-9 просто показан случай, в котором покупатель состоит из одной сущности, в качестве примера агрегата, содержащего только корень агрегации.
Для поддержки разделения агрегатов и сохранения четких границ между ними рекомендуется в модели предметной области DDD запретить прямой переход между агрегатами и иметь только поле внешнего ключа (FK), как реализовано в модели предметной области микрослужбы заказов в приложении eShopOnContainers. Сущность Order имеет только поле внешнего ключа для покупателя, и в ней отсутствует свойство навигации EF Core, как показано в следующем коде:
public class Order : Entity, IAggregateRoot
{
private DateTime _orderDate;
public Address Address { get; private set; }
private int? _buyerId; // FK pointing to a different aggregate root
public OrderStatus OrderStatus { get; private set; }
private readonly List<OrderItem> _orderItems;
public IReadOnlyCollection<OrderItem> OrderItems => _orderItems;
// ... Additional code
}
Для идентификации агрегатов и работы с ними требуется опыт и проведение исследования. Дополнительные сведения см. в следующем списке дополнительных ресурсов.
Дополнительные ресурсы
Вон Вернон (Vaughn Vernon). Эффективное агрегатное проектирование. Часть I. Моделирование одного агрегата (из https://dddcommunity.org/)
https://dddcommunity.org/wp-content/uploads/files/pdf_articles/Vernon_2011_1.pdfВон Вернон (Vaughn Vernon). Эффективное агрегатное проектирование. Часть II. Организация совместной работы агрегатов (из https://dddcommunity.org/)
https://dddcommunity.org/wp-content/uploads/files/pdf_articles/Vernon_2011_2.pdfВон Вернон (Vaughn Vernon). Эффективное агрегатное проектирование. Часть III. Получение аналитических сведений путем обнаружения (из https://dddcommunity.org/)
https://dddcommunity.org/wp-content/uploads/files/pdf_articles/Vernon_2011_3.pdfСергей Грибняк (Sergey Grybniak). Тактические конструктивные шаблоны DDD
https://www.codeproject.com/Articles/1164363/Domain-Driven-Design-Tactical-Design-Patterns-PartКрис Ричардсон (Chris Richardson). Разработка транзакционных микрослужб с помощью агрегатов
https://www.infoq.com/articles/microservices-aggregates-events-cqrs-part-1-richardsonDevIQ. Шаблон агрегата
https://deviq.com/aggregate-pattern/