Domain-driven design

Мы будем использовать некоторые идеи из методологии Domain-driven design. Впервые она описана в одноименной книге. В основном мы будем использовать информацию из глав 5 и 6.

Объект-значение

Языки программирования содержат различные примитивные типы, вроде чисел, строк, символов. Их еще называют значениями. Они неизменяемы. Значения могут быть равны, даже если хранятся в разных областях памяти.

Объект-значение подобен примитивным типам, но моделирует понятие предметной области. Примерами могут быть цвет или деньги. Цвета равны, если равны их RGB компоненты. Мы не можем поменять компоненту R зеленому цвету, т.к. это будет уже другой цвет.

Т.е. объект-значение неизменен и полностью идентифицируется своими атрибутами.

Сущность

Напротив, сущность определяется только своим идентификатором и может изменяться.

Например, есть публикация в интернете. Ее URL есть ее идентификатор. При этом в разные моменты времени ее содержимое может быть разным.

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

Ссылки / Ассоциации

Возьмем публикации в интернете или в журналах. Публикации могут ссылаться друг на друга. Но связь эта однонаправленная. Например, публикации А и Б ссылаются на публикацию Г, при этом Г не знает, кто на нее ссылается. Очевидно, что и Г может ссылаться на А и Б, но А и Б не будут знать этого. Конечно, можно построить некий индекс и определить кто ссылается на публикацию, но это внешний по отношению к публикациям механизм.

Поддержка двунаправленных связей дорога, по этому при моделировании выбирают наиболее важное направление. Например, есть Автор и Публикация. Автор может создать несколько публикаций. Представите, что у вас есть карточки в картотеке. Можно указать автора на карточке публикации. Но тогда узнать о всех публикациях автора можно будет только по стороннему индексу. Практичнее записывать ссылки на публикации в карточке автора. Имея карточку автора можно понять были ли у него публикации, сколько их.

Агрегаты

Большое количество связей между сущностями затрудняет их понимание. Поэтому стремятся сократить количество ассоциаций.

Агрегаты - подход к организации сущностей, чтобы сократить количество связей между ними.

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

Подробнее на сайте Мартина Фаулера.

Службы(сервисы)

Представим, что есть автомобиль и авто-мойка. Автомобиль моется в авто-мойке? Или авто-мойка моет машину?

Служба не содержит состояния и просто моделирует некое действие.

Комментарии

Entity, Value object, Service - ортогональные понятия относительно Clean Architecture. Да, верхний слой в Clean Architecture называется Entities, и это вносит путаницу. Слои Entities и Use Cases могут содержать свои Сущности, Объекты-значения и Службы.


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

Допустим, есть агргегата с корнем Пост и вложенными Комментариями. Эти сущности имеют простые, не составные идентификаторы. В этом случае http приложение не может иметь такой ресурс /posts/:post-id/comments/:comment-id, т.к. ни у кого не должно быть возможности получить постоянную сслыку на внутреннюю сущность. Но приложение вполне может иметь ресурс /posts/:post-id/comments. В случае с html страницами можно использовать якоря для ссылки на внутреннюю сущность: /posts/:post-id/#comment-id.

Однако, комментарий, как корень другого агрегата, может иметь составной идентификатор [post-id, post-comment-id]. При этом post-comment-id должен быть уникальным только в контексте поста. И в этом случае ресурс /posts/:post-id/comments/:comment-id корректен.

Разумеется, URL - это всего лишь деталь способа доставки приложения пользователю, и он должен зависеть от типа идентификатора, а не наоборот.


Из-за отсутствия постоянных ссылок на внутренние сущности их удобно хранить в векторах, где идентификатором сущности выступает ее индекс в векторе.