Шпаргалка по основам DDD
Entity & value object
Несмотря на то, что DDD делает акцент на поведении, без объектов нам не обойтись. DDD выделяет два типа объектов: entities и value objects. Их основные свойства можно свести в следующую таблицу:
Свойство | Entity | Value object |
---|---|---|
Идентификация | Определяется полем Id | Определяется совокупностью всех своих свойств (два vo равны только если все их свойства соответсвенно одинаковы) |
Жизненный цикл | Бесконечный. Обладает историей (даже если мы ее не храним) изменений. | Нулевой, поскольку легко заменяемы: одна рублёвая монета может быть заменена другой. Принадлежат одной или нескольким сущностям. |
Изменяемость | Изменяемый | Неизменяемый (иначе жизненный цикл будет не нулевым) |
Ответственность | Идентификация и отслеживание жизненного цикла. Всю остальную логику нужно стараться выносить в Value objects | Любая логика, не изменяющая сам объект |
Как хранятся в БД | В отдельных таблицах | Вкладывается в таблицу entity, которой принадлежит. Если свойств несколько - вкладываются они все. |
Пример | Appointment | DateTimeRange |
Доменный сервис
Доменный сервис должен содержать доменную логику (в отличии от сервиса приложения), которую нельзя отнести ни к одной из entity или value object. Обычно доменный сервис содержит методы, описывающие сложные сценарии, затрагивающие несколько доменных объектов. То есть, доменный сервис дирижирует другими доменными объектами, а не выполняет оперции сам. В противном случае можно “скатиться” к транзакционному программированию и анемичным моделям.
Агрегат
Агрегат - это паттерн в DDD, описывающий то, как нужно работать с несколькими доменными объектами, если они вместе представляют цельное понятие в домене. Примером может служить заказ в магазине: он состоит из отдельных товаров, однако с заказом (вместе с входящими в него заказами) удобнее работать как с единым агрегатом.
Внутри каждого агрегата должен присутствовать корень (aggregate root). Задача корня в том, чтобы поддерживать корректность инвариантов (совокупность возможных значений) всего агрегата. Например, если интернет-магазин не может высылать заказы тяжелее 10 килогамм, то контроль этого требования следует реализовать в корне (заказе).
Для того, чтобы корень мог следить за инвариантами, все взаимодействие с агрегатом должно происходить через его корень. То есть, другие агрегаты не должны иметь доступа до вложенных сущностей в обход корня. Ведь если коллекция товаров станет доступна извне - проверить требование предельного веса будет негде.
Репозиторий
Репозиторий - это паттерн, который используется далеко за пределами DDD. Его цель - инкапсулировать код работы с хранилищем данных (например, БД) в одном месте. Причем так, чтобы для клиентов репозитория доступ к данным осуществлялся также просто, как если бы они обращались к коллекции в памяти.
Для каждого агрегата необходимо создавать отдельный репозиторий. Причем репозиторий должен работать с агрегатом только через его корень.
… и у DDD тоже есть слои
Если наложить основные сущности DDD на onion architecture - то получим такую картину:
Главное, на что стоит обратить внимание - entities, value objects, domain events и агрегаты находятся в самом центре, а соответственно - не должны иметь внешних зависимостей. Если нам удастся изолировать домен таким образом - то его будет проще понимать и тестировать.