12.23M
Category: programmingprogramming

Тактические паттерны DDD - Lite

1.

Тактические паттерны
DDD-Lite

2.

3.

Сущности
(Entities)

4.

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

5.

Сущности чаще всего выражены существительными
Физические или
материальные объекты
Места размещения
объектов (Places)
Операции (Transactions)
Роли (например
Потребитель, Продавец)
Контейнеры концепций
(Containers for other
Concepts)
Внешние системы
(например Удаленная база
данных)
Абстрактные понятия (Abstract
Nouns) (например Жажда)
Организации
События (например Авария)
Правила/Политики
Записи/Протоколы

6.

У сущностей есть жизненный цикл
Изменение состояния со временем
Сущности идентифицируемы, и могут быть
отслежены по идентификаторам
При проектировании сущности больше
внимания уделяется их идентифицируемости
(вопрос «Кто») чем их свойствам (вопрос
«Какие»)
Не должны слепо отображать сущности
реального мира (“Model the Real‐World”)

7.

An entity is anything that
has continuity through a
life cycle and has
distinctions independent
of attributes ...
Eric Evans

8.

1.
2.
3.
Выделяем идентификатор
Включаем атрибуты по которым
осуществляется запрос (возможные ключи)
Добавляем поведение которое связано с
этими атрибутами
Все остальное можно вынести в поведение
атрибутов (Value Object) или в бизнес сервисы
(Business Services)
4.
5.
Определяем роли и ответственности
Вводим механизм валидации

9.

10.

Природа
Бизнес идентификаторы (Natural Keys)
Генерируемые идентификаторы
▪ Инкрементальные
▪ Глобально уникальные (GUIDs/UUIDs)
▪ Строковые
Происхождение
Введенные пользователем
Генерируемые доменной моделью
Генерируемые базой данных
Генерируемые внешним контекстом

11.

GUID
f36ab21c-67dc-5274-c642-1de2f4d5e72a
содержит
Время создания в миллисекундах
Адрес сервера (IP)
Идентификатор процесса, виртуальной машины (JVM)
Случайное число
Крайне неудобен для отображения и
пользовательского ввода
Может быть преобразован в читаемую форму
APM-P-08-14-2012-F36AB21C

12.

Чаще всего реализуется соответствующим репозиторием
Момент идентификации
При сохранении
При создании объекта

13.

Поведение – изменение состояния объекта,
реализуется в ООП посредством методов
класса
Поведение чаще всего приписывается
сущности
Если нет подходящих сущностей, то пробел
заполняю порождая (выделяя) новые сущности
Думайте о сущностях скорее как о единицах
поведения, нежели как о единицах данных
(“Tell Don’t Ask”)

14.

Реализация поведения с использованием методов set,
get свидетельствует о слабом коде
Если все поведение сводится к смене состояния, лучше
использовать метод с осмысленным именем
Например вместо setActive(boolean) используем методы
activate() / deactivate()
(Intention Revealing Interface)
Если смена состояния сопровождается дополнительной
логикой (например проверкой допустимости операции), то
использовании методов установки (set) зачастую является
симптомом запаха «Класс данных» (Data Class),
всю работу класса вынуждены выполнять клиенты

15.

Исключив обращение к методам get и set
из бизнес логики мы зачастую не можем
убрать их из сущности, так как они
используются механизмами других слоев
приложения (стандарт JavaBean)
В частности, слоем сохранения
Можем использовать паттерн Хранитель
(GoF Memento) для решения этой
проблемы

16.

Побочный эффект это изменение
функцией какого-либо состояния, или
взаимодействие с объектами внешнего
мира
Побочные эффекты выносятся из сущности
в бизнес службы
Пишем функции свободные от
неожиданных побочных эффектов
(Hidden‐Side‐Effect‐Free Functions)
Замкнутые операторы

17.

Доступ к службам может быть реализован
следующим образом
Двойная диспетчеризация
Передача службы в параметрах метода
Команда (GoF Command Pattern)
Для создания сущности не нужен контейнер
инверсии управления (IOC)
Использовать службу обнаружения для
разрешения внешнего объекта внутри метода
(Service Locator Pattern)

18.

19.

20.

Ответственность (responsibility) класса выражена
его интерфейсом
Распределение ответственностей между классами
регламентируется принципами SOLID (от Роберт C.
Мартина) и паттернами GRASP (Крейга Лармана)
Если в различных контекстах с различными
клиентами класс обладает различными
ответственностями, говорим о различии ролей
класса
Согласно принципу разделения интерфейса (ISP)
каждая роль должна быть представлена отдельным
интерфейсом
Иногда конкретная роль связана с состоянием класса

21.

Основная ответственность сущностей
идентифицируемость
Поведение как изменение состояния не
ключевых атрибутов должно быть
перемещено в объекты значения или с
бизнес сервисы
При перемещении в объекты значения
зачастую происходит нарушение закона
Деметры (The Law of Demeter (LoD))
игнорируем этот факт

22.

Dealing with Roles
[Martin Fowler]

23.

Избегаем паттерна
состояние (State)
используем явные
модели состояний

24.

Виды валидации
Программирование по контракту (DoC,
защищенное программирование) требует
соблюдение определенных предусловий и
поддержки инвариантов
Поддержка бизнес правил и целостности,
зачастую требуют более существенных,
дорогих проверок в которых могут участвовать
несколько сущностей

25.

Само-валидирующиеся (Self‐Validating)
сущности
Сущность никогда не должна быть в не
валидном состоянии

26.

Внутренняя инкапсуляция
self-encapsulation
предохранители
guards

27.

Имеет большее отношение к реализации
агрегата
Паттерны
Спецификации (Specifications)
[Evans & Fowler, Spec]
Стратегии (GoF Strategy)
[Gamma et al.]
Отложенная валидация (Deferred Validation)
Checks pattern language, Ward Cunningham
[Cunningham, Checks]

28.

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

29.

30.

31.

Объекты-значения
(Value Objects)

32.

Объекты-значения — это
дескрипторы или свойства, важные в
той предметной области, которую вы
моделируете
Представляют данные измерений,
количества или описания объектов
Представляют состояние объекта но
не предполагают идентичности
(набор атрибутов без
идентификатора)

33.

... elements of the design that
we care about only for what
they are, not for who or
which they are
Eric Evans

34.

Можно реализовать атрибут как простой тип
Запах Одержимость простыми типами (Primitive Obsession)
Объекты-значения описывают свойства сущностей гораздо
более изящным и объявляющим намерения способом
Заметив их в интерфейсе или методе сущности, вы сразу
поймете, с чем имеете дело
Начинаем моделирование с объектов значений, и если они не
получают поведение (Запах «Ленивый класс» (Lazy Class))
расформировывать
Можно реализовать атрибут как сущность
нужно придумать идентификаторы которые потом могут не
понадобится
пострадает производительность (уникальность)
Flyweight pattern GoF
Предпочитаем объекты значения сущностям

35.

36.

37.

Деньги, популярный объект-значение,
гораздо лучше выглядит как параметр
функции API передачи средств, чем просто
десятичное число

38.

39.

Неидентифицируемы
Определяются как эквивалентные по эквивалентности
всех атрибутов
Могут быть предоставлены в совместное использование
(share)
Желательно неизменяемые (Immutable)
Поведение реализуется как функции без побочных
эффектов (Side-Effect-Free Behavior)
При изменении бизнес логики полностью заменяемы
Описывают концептуально целостные объекты
Должны поддерживать высокую сцепленность (Cohesive)
Могут быть скомпонованы в единый объект (Combinable)
Само-валидирующиеся (Self‐Validating)
Никогда не должны быть в не валидном состоянии

40.

Устраняем сложность вносимую
идентификацией
Неприятие коллекций (Collection Aversion)
Считается, что не стоит создавать коллекции
объектов значений (тем самым формируя подобие
идентификатора)
Вместо этого предполагается именовать все
элементы в базовой сущности
IEnumerable<PhoneNumber> PhoneNumber
PhoneNumber HomeNumber; PhoneNumber WorkNumber;

41.

Entity
Value Object

42.

С объектами-значениями мы хотим получить
функции, свободные от побочных эффектов
Добавляя 20 рублей к 20 рублям, вы
изменяете 20 рублей?
Нет, вы создаете новый денежный
дескриптор 40 рублей
Неизменяемость проверяется модульным
тестом
Объект клонируют, выполняют операцию и
сравнивают с эталоном

43.

функция без побочных эффектов (Side-Effect-Free Behavior)

44.

Атрибут часто меняется
Удаление и создание слишком дорого
Замена объекта дорого обходится
Объектов не так уж и много
При выявлении необходимости
модифицировать объект нужно
рассмотреть вариант преобразования его
к сущности

45.

Нет смысла изменить объект, когда можно просто
заменить его
Используя функции без побочных эффектов можно
производить замены объектов-значений даже при
изменениях в бизнес логике

46.

Элементы объекта значения
представляются как одно целое, и не
имеют смысла друг без друга
Паттерн «Целое значение» Whole Value
[Cunningham, Whole Value aka Value Object]
Цена имеет два атрибута 50 $, по
отдельности эти атрибуты либо ничего не
значат, либо имеют другое значение

47.

Зачем ?

48.

Когда имеет смысл выделять один атрибут в
объект значение ?
Со значением связано определенное
поведение
Мы хотим прояснить концепцию, через
формирование информативного
интерфейса
Микро-типы (Micro Types, Tiny Types)
Стандартные типы (Standard Types)

49.

Клиент использует переданное значение
предварительно преобразовав его (например
переписав заглавными буквами)
Исключая дублирование логики и увеличивая
сцепленность кода выделяем объектзначение

50.

Обертка над примитивным типом,
сделанная с целью прояснения концепта

51.

Стандартный тип –
объект описывающий
тип другого объекта
(Паттерн проектирования
«Power Types»)

52.

Между объектами-значениями не может
быть двунаправленных ассоциаций,
так как нет возможности ссылки если оба
объекта не идентифицированы
Если такая ассоциация возникает один из
объектов становится сущностью

53.

Статический метод создания (Static Factory
Methods)
Микро тип (Micro Types (Also Known as Tiny
Types))
Например Hours

54.

NoSQL
SQL
Плоская денормализация (Flat Denormalization)
▪ Создание формата хранения (asString, toJson)
▪ Преобразование на сохранение
(Persisting Values on Save)
▪ Разбор на загрузке (Parsing Values on Load)
Нормализация
(Normalizing into Separate Tables)

55.

56.

57.

В этом случае объект-значение сохраняется
в отдельной таблице и идентифицируется
суррогатным ключом
Схема данных и доменная модель приходят
к разногласию по поводу
идентифицируемости концепта

58.

59.

Ассоциации

60.

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

61.

Двунаправленные
Отношение многое ко многому
Ассоциации с ограничениями

62.

Избавляемся от двунаправленных
ассоциаций
2. Избавляемся от связей многое на многое
3. Удаляем второстепенные ассоциации
(Модель это очищенное знание)
1.
P.S. Любое упрощение требует
дополнительного исследования домена

63.

• Стремимся устранить все двунаправленные ассоциации

64.

65.

• Снижаем мощность ассоциации усложняя структуру классов

66.

67.

68.

Заменяем прямую ссылку на идентификатор

69.

Агрегаты и сводные корни

70.

Для упрощения сложной доменной
модели концепты объединяют в
кластера по агрегированию
Правила агрегирования
Корень агрегации должен иметь
глобальный идентификатор
Внешние сущности ссылаются
только на корни
Удаление корня удаляет все
объекты в агрегате

71.

72.

Сводный корень — специальная
сущность, к которой напрямую
обращаются потребители
Определение сводных корней
позволяет избегать чрезмерного
соединения объектов, составляющих
модель
Следует отметить, что сводные корни
отчаянно стерегут свои подсущности

73.

Сводные корни — единственный вид
сущностей, на который может ссылаться
программа
Это позволяет избежать «Большого кома
грязи»,
потому что теперь у вас есть ограничение,
не дающее создавать тесно связанные
системы, где все сопряжено со всем

74.

75.

Есть сущность под названием полис
(Policy)
Полисы продлеваются раз в год,
поэтому, вероятно, есть и сущность под
названием Period
Так как Period не может существовать без
Policy, а на Period можно воздействовать
через Policy, Policy является сводным
корнем, а Period — дочерней сущностью

76.

Policy.CurrentPeriod().Renew();
— нарушение закона Деметры
«смерть от тысячи порезов» — хорошее
описание кошмара, которым может
стать поддержание слишком связанной
системы

77.

Policy.Renew();
Cводный корень все делает сам
В своих внутренних сущностях он может
найти текущий период, определить,
наступило ли уже время продления, и
сделать все, что потребуется

78.

Агрегаты строятся по потребностям
пользовательского интерфейса
Агрегаты это коллекции или контейнеры
объектов
Фокусируемся на связях Has-a
Агрегаты слишком большие
Проблемы с производительностью (большие
объемы загрузки, сложные запросы и т.д.)
Конкуренция (коллизии на транзакциях)
Слабая масштабируемость
Агрегаты слишком маленькие (пустые)
Нет должной защиты инвариантов

79.

Сколько запросов
понадобится для
вытягивания его в
память, какова
сложность этих
запросов
Сколько места в
памяти он займет
Какова вероятность
одновременных
запросов на
модификацию этого
агрегата

80.

Агрегаты высший уровень абстракции
предметной области
Обращаемся ко всем элементам агрегата как к
единому целому
Предпочитаем небольшие агрегаты
Идеально – одна сущность и несколько объектов
значений
Допускается пара дочерних сущностей
Агрегаты основа поддержки инвариантов
предметной области

81.

Инвариант – это бизнес-правило которое
всегда сохраняет свою непротиворечивость
(мгновенная и атомарная транзакционная
согласованность)
Например:
Инвариант
English     Русский Rules