1 of 88

DDD: ТАКТИЧЕСКИЙ ДИЗАЙН. ЧАСТЬ 1

1

2 of 88

ОБУЧЕНИЕ DDD: СОДЕРЖАНИЕ

2

  • Обзор тактического моделирования DDD

3

  • Сущности

8

  • Значения объектов

53

  • Агрегаты

59

  • Фабрики

82

3 of 88

ТАКТИЧЕСКОЕ МОДЕЛИРОВАНИЕ DDD

3

4 of 88

ОБЗОР ТАКТИЧЕСКОГО МОДЕЛИРОВАНИЯ

4

  • Тактическое моделирование применимо в ограниченном контексте.
  • Тактические моделирование сильно полагается на универсальный язык.
  • Агрегаты – центральный тактический шаблон.
  • Общность не состоит в связи с организацией или же группа сущности и ценность объекта должны оставаться последовательно транзакционными на протяжении всей жизни агрегата.
  • Совокупность является стойкой в общении с использованием репозитория.

5 of 88

ОБЗОР ТАКТИЧЕСКОГО МОДЕЛИРОВАНИЯ

5

  • Домен сервиса - бизнес-операции внутри модели домена, не помещающиеся в одной операции одного объекта в организации.
  • Домен события - модель значительных событий в домене.
  • Модули - логическая группировка объектов (Java-пакеты, пространства имен).

6 of 88

МИР DDD

6

7 of 88

МЫ ОСТАЕМСЯ С ГИБКОЙ СИСТЕМОЙ ПРОЕКТА

7

8 of 88

СУЩНОСТИ

8

9 of 88

ОБЗОР

9

"Когда объект выдается по идентичности, быстрее чем его атрибуты, в начале делать определение в модели. Держите определение класса простым и сосредоточенным на непрерывности жизненного цикла и идентичности. Определите средство из отличия, каждый объект на его форме или же история". (Эванс*)

10 of 88

ОБЗОР

10

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

11 of 88

УНИКАЛЬНАЯ ИДЕНТИФИКАЦИЯ

11

12 of 88

УНИКАЛЬНАЯ ИДЕНТИФИКАЦИЯ

12

  • Уникальная идентификация - наиболее неотъемлемая характеристика, особенно те, которые ее идентифицируют или используют в поиске соответствующего объекта.
  • Ценность объектов в способности держать идентификацию. Они находятся неизменными в фокусе поведения вокруг идентификации и отсутствии связи.

13 of 88

ПОКОЛЕНИЕ СТРАТЕГИЙ

13

  • Задано пользователем
  • Создано объявление
  • Генерируется механизмом устойчивости
  • Создание ограниченного контекста
  • Суррогатная идентификация

14 of 88

УНИКАЛЬНАЯ ИДЕНТИФИКАЦИЯ: ПРЕДОСТАВЛЕННАЯ ПОЛЬЗОВАТЕЛЕМ

14

15 of 88

УНИКАЛЬНАЯ ИДЕНТИФИКАЦИЯ: СОЗДАНА ПРИЛОЖЕНИЕМ

15

  • Преимущества – просто, чип
  • Недостаток – простой, чип?

Что, если уникальная идентификация не является достоверной (на форме заголовок написан с ошибкой)? Потребности пользователя могут дать путь к правильной идентификации.

16 of 88

УНИКАЛЬНАЯ ИДЕНТИФИКАЦИЯ: СОЗДАНА ПРИЛОЖЕНИЕМ

16

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

17 of 88

УНИКАЛЬНАЯ ИДЕНТИФИКАЦИЯ: СОЗДАНА ПРИЛОЖЕНИЕМ

17

Пример:

-> c8e6cf04-4bf8-11e6-beb8-9e71128cae77

  • Настроить добавление информации:

-> АПМ-S-2016-07-17-C8E6CF04

  • Сворачивать ценность объекта:

String rawId = java.util.UUID.randomUUID.toString();

  • Создать UUID:

String rawId = "APM-S-2016-07-17-C8E6CF04";

SprintId sprintId = new SprintId(rawId);

Date sprintCreationDate = sprintId.creationDate();

18 of 88

УНИКАЛЬНАЯ ИДЕНТИФИКАЦИЯ: СОЗДАНА ПРИЛОЖЕНИЕМ

18

public class Sprint extends Entity { private SprintId sprintId;

public Date creationDate() {

return this.sprintId().creationDate();

}

...

}

public class HibernateSprintRepository implements

SprintRepository {

public SprintId nextIdentity() { return new

SprintId(java.util.UUID.randomUUID().toString().toU pperCase());

}

...

}

19 of 88

УНИКАЛЬНАЯ ИДЕНТИФИКАЦИЯ: МЕХАНИЗМ СТОЙКОСТИ

19

  • Всегда уникальный.
  • Две стратегии может быть применено здесь: рано (до сохранения объекта) и поздно (пока объект не сохранился).

20 of 88

УНИКАЛЬНАЯ ИДЕНТИФИКАЦИЯ: МЕХАНИЗМ СТОЙКОСТИ

20

Поздно:

  • Сгенерировано, когда сохраняется.
  • Легко настраивается в ORM & Oracle.

<id name="id" type="long" column="sprint_id">

<generator class="sequence">

<param name="sequence">sprint_seq</param>

</generator>

</id>

  • Пример:

21 of 88

УНИКАЛЬНАЯ ИДЕНТИФИКАЦИЯ: МЕХАНИЗМ СТОЙКОСТИ

21

Рано:

  • Генерируется перед сущностью, является сохранением.
  • Требуется дополнительный код в Oracle.

public SprintId nextIdentity() {

Long rawSprintId = (Long) this.session()

.createSQLQuery(

"select sprint_seq.nextval as sprint_id from dual")

.addScalar("sprint_id", Hibernate.LONG)

.uniqueResult();

return new SprintId(rawSprintId);

}

  • Пример:

22 of 88

УНИКАЛЬНАЯ ИДЕНТИФИКАЦИЯ: СОЗДАНО ДРУГИМ ОГРАНИЧЕННЫМ КОНТЕКСТОМ

22

  • Когда другой ограниченный контекст генерирует идентификацию, нам необходимо найти сопоставление и назначить идентификацию в локальный ограниченный контекст.
  • Характеристики другой сущности можно скопировать в локальном ограниченном контексте, что недопустимо.
  • Самая сложная стратегия, из -за расходов на техническое обслуживание. Сила организации изменяет другой ограниченный контекст, необходимо уведомить о завершении. Здесь может помочь ЭДА.

23 of 88

23

УНИКАЛЬНАЯ ИДЕНТИФИКАЦИЯ: СОЗДАНО ДРУГИМ ОГРАНИЧЕННЫМ КОНТЕКСТОМ

Пример – Выбор спринта id из местного контекста и ещё одного из ограниченного контекста.

24 of 88

КОГДА СЛЕДУЕТ СОЗДАВАТЬ ИДЕНТИФИКАЦИЮ?

24

  • Ранняя роль идентификации – идентификация генерируется до сущности и сохраняется, как часть построения.
  • Поздняя роль идентификации – идентификация генерируется после сущности и является стойкой.

25 of 88

РАННЕЕ СОЗДАНИЕ ИДЕНТИФИКАЦИИ

25

26 of 88

ПОЗДНЕЕ СОЗДАНИЕ ИДЕНТИФИКАЦИИ

26

27 of 88

ПОЗДНЕЕ СОЗДАНИЕ ИДЕНТИФИКАЦИИ: ПОТЕНЦИАЛЬНЫЕ ПРОБЛЕМЫ

27

  • Сила идентификации требуется до сущности и сохраняется. Пример: сущность генерирует событие домена на усмотрение. Что событие предоставляет ссылку идентификатора созданной организации.
  • Равенство имеет значение. Если новые сущности добавлены к установкам до существования стойкости, равенство проверки не может полагаться при проверке идентификации, потому что это является нулевым.

28 of 88

ПОЗДНЕЕ СОЗДАНИЕ ИДЕНТИФИКАЦИИ: ПРОВЕРКА ВРЕМЕНИ

28

Сравнивать атрибуты сущности:

public class Sprint extends Entity {

...

@Override

public boolean equals(Object anObject) {

boolean equalObjects = false;

if (anObject != null && this.getClass() == anObject.getClass()) {

Sprint anotherSprint = (Sprint) anObject;

equalObjects = this.productId().equals(anotherSprint.productId()) && this.tenantId().equals(anotherSprint.tenantId()) && this.begins().equals(anotherSprint.begins()) && this.ends().equals(anotherSprint.ends()) && this.name().equals(anotherSprint.name());

}

return equalObjects;

}

@Override

public int hashCode() { int hashCode =

+ (...) + this.productId().hashCode()

+ this.tenantId().hashCode() + this.begins().hashCode()

+ this.ends().hashCode() + this.name().hashCode();

return hashCode;

}

...

}

29 of 88

СУРРОГАТНАЯ ИДЕНТИФИКАЦИЯ

29

  • Сила инструментов ОРМ требует собственную идентификацию, где спроектирована.
  • В этом кейсе, можно использовать обе идентификации.

30 of 88

ПРИМЕР СУРРОГАТНОЙ ИДЕНТИФИКАЦИИ – ЧЛЕН

30

Применение слоя супертипа (Фаулер*), спрятать шаблон суррогатной идентификации в высший класс

public class IdentifiedDomainObject {

private static final long serialVersionUID = 1L; private long id;

protected IdentifiedDomainObject() { super();

this.setId(-1);

}

protected long id() {

return this.id;

}

private void setId(long anId) { this.id = anId;

}

}

31 of 88

ПРИМЕР СУРРОГАТНОЙ ИДЕНТИФИКАЦИИ – ЧЛЕН

31

Участник группы является частью значения-объекта:

Группа общности. Группа имеет члены группы экземпляров:

Суррогат ID используется в отдельной базе данных.

public class GroupMember extends IdentifiedDomainObject {

...

public GroupMember(TenantId aTenantId, String aName, GroupMemberType aType) {

this();

this.setName(aName); this.setTenantId(aTenantId); this.setType(aType);

}

@Override

public boolean equals(Object anObject) {

boolean equalObjects = false;

if (anObject != null && this.getClass() == anObject.getClass()) {

GroupMember typedObject = (GroupMember) anObject;

equalObjects = this.tenantId().equals(typedObject.tenantId()) && this.name().equals(typedObject.name()) && this.type().equals(typedObject.type());

}

return equalObjects;

}

}

public class Group extends ConcurrencySafeEntity { private String description;

private Set<GroupMember> groupMembers; private String name;

private TenantId ;

...

}

32 of 88

ПРИМЕР СУРРОГАТНОЙ ИДЕНТИЧНОСТИ

32

Настройка спящего режима в созданном пространстве

<hibernate-mapping>

<class

name="com.saasovation.identityaccess.domain.model. identity.GroupMember"

table="tbl_group_member" lazy="true">

<id name="id" type="long" column="id" unsaved-value="-1">

<generator class="native"/>

</id>

<property name="name" column="name" type="java.lang.String" update="true"

insert="true" lazy="false"

/>

...

</class>

</hibernate-mapping>

CREATE TABLE `tbl_group_member` (

`id` int(11) NOT NULL

auto_increment,

`name` varchar(100) NOT NULL,

...

PRIMARY KEY (`id`)

) ENGINE=InnoDB;

33 of 88

ОТКРЫТИЕ СУБЪЕКТОВ

33

34 of 88

ПУТИ ОТКРЫТИЯ СУБЪЕКТОВ

34

  • Начинать с таблицы базы данных (БД) - неправильный путь, результат в относительной модели кода, самый вероятный путь к анемичной модели.
  • Слушать язык (универсальный язык).

35 of 88

РОЛИ И ОБЯЗАННОСТИ

35

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

36 of 88

ПРИМЕР: ОБНАРУЖЕНИЕ АТАК В БЕЗОПАСНОМ КОНТЕКСТЕ

36

Рассмотрим следующее возможные случаи:

  • Пользователи существуют в группе и под контролем аренды.
  • Пользователи должны быть аутентифицированы в системе.
  • Пользователи обладают личной информацией, в том числе именем и контактной информацией.
  • Личная информация пользователя может быть изменена сама по себе или руководителем.
  • Безопасность пользователя: реквизиты для входа (пароли) могут быть изменены.

37 of 88

ПРИМЕР: ОБНАРУЖЕНИЕ АТАК В БЕЗОПАСНОМ КОНТЕКСТЕ

37

Аренда?

Копать глубже:

  • Арендаторы разрешают регистрация многим пользователям по приглашению.
  • Арендаторы могут быть активны или деактивированы.
  • Пользователи системы должны быть аутентифицированы, но становятся аутентифицированы только если в хозяин проявляет активность.

38 of 88

ПРИМЕР: ОБНАРУЖЕНИЕ АТАК В БЕЗОПАСНОМ КОНТЕКСТЕ

38

Идентификация объектов:

  • Арендатор – идентификатор арендатора, значение объекта. С использованием вместо этого отдельного типа из нити, или же целого числа, или же имеет большие преимущества.
  • Пользователь - идентифицирован по имени. Пароль – не часть идентификации, потому что его можно изменить.

39 of 88

ПРИМЕР: ОБНАРУЖЕНИЕ АТАК В БЕЗОПАСНОМ КОНТЕКСТЕ

39

40 of 88

РЕШЕНО: ОБНАРУЖЕНИЕ АТАК В БЕЗОПАСНОМ КОНТЕКСТЕ

40

Вариант использования — «Клиенты могут быть активными, или быть деактивированы».

Первый (неправильный - нет выраженного поведения) делать (геттер/сеттер):

public class Tenant extends Entity {

...

private boolean active;

...

public boolean getIsActive() {...} public boolean setIsActive() {...}

}

41 of 88

РЕШЕНО: ОБНАРУЖЕНИЕ АТАК В БЕЗОПАСНОМ КОНТЕКСТЕ. ПОВЕДЕНИЕ.

41

Лучший путь:

public class Tenant extends Entity {

...

private boolean active;

...

public boolean isActive() {...}

public boolean activate() {...}

public boolean deactivate() {...}

}

42 of 88

РЕШЕНО: ОБНАРУЖЕНИЕ АТАК В БЕЗОПАСНОМ КОНТЕКСТЕ. ПОВЕДЕНИЕ.

42

Напишите проверку, чувствительную к языку:

public class TenantTest {

public void can_be_activated_and_deactivated() throws Exception {

Tenant = new Tenant(...);

assertTrue(tenant.isActive());

tenant.deactivate(); assertFalse(tenant.isActive());

tenant.activate(); assertTrue(tenant.isActive());

}

}

43 of 88

ПРИМЕР: ОБНАРУЖЕНИЕ АТАК В БЕЗОПАСНОМ КОНТЕКСТЕ

43

Моделирование атаки:

44 of 88

ПРИМЕР: ОБНАРУЖЕНИЕ АТАК В БЕЗОПАСНОМ КОНТЕКСТЕ

44

Моделирование пользователя:

45 of 88

ОРГАНИЗАЦИЯ СБОРКИ

45

Следующая информация должна быть предоставлена во время сборки:

  • Информация идентификации.
  • Атрибуты использования запроса.
  • Вся остальная информация удовлетворяет обратному сущности.

46 of 88

ОРГАНИЗАЦИЯ СБОРКИ

46

Реализация пользовательского конструктора:

public class User extends Entity {

public User(TenantId aTenantId, String aUsername, String aPassword, Person aPerson) {

this();

this.setPerson(aPerson); this.setTenantId(aTenantId); this.setUsername(aUsername); this.protectPassword("", aPassword);

...

DomainEventPublisher

.instance()

.publish(new UserRegistered(

this.tenantId(),

aUsername, aPerson.name(),

aPerson.contactInformation().emailAddress()));

}

...

}

47 of 88

ОРГАНИЗАЦИЯ СБОРКИ

47

Сборка пользователя – метод фабрики на совокупность посетителей:

public class Tenant extends Entity {

public User registerUser(String anInvitationIdentifier, String aUsername, String aPassword, Person aPerson) {

this.assertStateTrue(this.isActive(), "Tenant is not active.");

User = null;

if (this.isRegistrationAvailableThrough(anInvitationIdentifier)) {

aPerson.setTenantId(this.tenantId());

user = new User(

this.tenantId(), aUsername, aPassword, aPerson);

}

return user;

}

...

}

48 of 88

ПРОВЕРКА

48

  • Проверка свойств/атрибутов
  • Проверка всех объектов
  • Проверка кластеров из агрегатов

49 of 88

НАСТРОЙКИ ПРОВЕРКИ (ВАЛИДАЦИИ)

49

Используйте инкапсуляцию в себе:

"Инкапсуляция в себе является спроектированными своими классами так, чтобы любой доступ к данным, даже в пределах тот же класса, идет через методы" (Фаулер*)

50 of 88

НАСТРОЙКИ ВАЛИДАЦИИ

50

Применение самоинкапсуляции:

public final class EmailAddress extends AssertionConcern {

...

public EmailAddress(String anAddress) {

super(); this.setAddress(anAddress);

}

public String address() { return this.address;

}

private void setAddress(String anAddress) {

this.assertArgumentNotEmpty(anAddress, "The email address is required.");

this.assertArgumentLength(anAddress, 1, 100, "Email address must be 100 characters or less."); this.assertArgumentTrue(

Pattern.matches("\\w+([-+.']\\w+)*@\\w+([-.]\\w+)*\\.\\w+([-.]\\w+)*", anAddress), "Email address format is invalid.");

this.address = anAddress;

}

}

51 of 88

ПРОВЕРКА ВСЕГО ОБЪЕКТА

51

  • Если все характеристики действительны, это означает, что в весь объект действителен.
  • Срок действия объекта сильно зависит от контекста.
  • Логика проверки может быть отделена в стратегию (ГоФ) или технические характеристики (Эванс) или в реализацию.

Корень агрегаторов команды метода - подтверждает совокупный объекты.

52 of 88

ПРОВЕРКА КЛАСТЕРОВ АГРЕГАТОВ

52

  • Реализовано в службе домена.

  • Org.company.employment.domain.employee -> Employee.class, ContanctInfo.class, …
  • …domain.model
  • …domain.service

53 of 88

ЗНАЧЕНИЯ ОБЪЕКТОВ

53

54 of 88

ОБЗОР

  • Если вас интересуют только атрибуты и логика элемента модели, классифицируйте его как объект-значение. Сделайте это выражение из атрибутов. Это позволяет связать функциональность.Удовлетворите значение неизменности объекта. Сделать все операции без побочных эффектов функции чтобы не зависеть от любых изменений прав. Не придавайте объекту-значению никакой идентичности и избегайте сложностей проектирования, необходимых для поддержки сущностей. (Эванс*)

54

Взятый из: http://domainlanguage.com/wp-content/uploads/2016/05/DDD_Reference_2015-03.pdf

Общий под творческий Общины Атрибуция 4.0 Международный Лицензия - https://creativecommons.org/licenses/by/4.0/

55 of 88

ОБЗОР

55

  • Жизненно важно при построении блокировать DDD.
  • Мы должны стремиться моделировать с использованием значений объектов, вместо сущностей, где возможно.

56 of 88

ХАРАКТЕРИСТИКИ ЗНАЧЕНИЙ

56

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

57 of 88

ПРИМЕР: ЗНАЧЕНИЕ ОБЪЕКТА

57

public class BusinessPriority extends ValueObject {

public BusinessPriority(BusinessPriorityRatings aBusinessPriorityRatings) {

this();

this.setRatings(aBusinessPriorityRatings);

}

public float priority(BusinessPriorityTotals aTotals) {

float costAndRisk = this.costPercentage(aTotals) + this.riskPercentage(aTotals); return this.valuePercentage(aTotals) / costAndRisk;

}

public float riskPercentage(BusinessPriorityTotals aTotals) {

return (float) 100 * this.ratings().risk() / aTotals.totalRisk();

}

public float totalValue() {

return this.ratings().benefit() + this.ratings().penalty();

}

public float valuePercentage(BusinessPriorityTotals aTotals) { return (float) 100 * this.totalValue() / aTotals.totalValue();

}

@Override

public boolean equals(Object anObject) { ... }

@Override

public int hashCode() { ... }

}

58 of 88

ПОСТОЯННЫЕ ЗНАЧЕНИЯ

58

  • Разработан как ORM-компонент (отношение один к одному).
  • Сохранено в пределах еще одного пространства (один ко многим). Требует технический id, чтобы можно было скрыть из публичного интерфейса (хранилище реляционных данных).
  • Сериализованный в пределах не связанного столбца.

59 of 88

АГРЕГАТЫ

59

60 of 88

ОБЗОР

60

  • DDD агрегат является кластером из объектов домена, что можно лечить один. (Фаулер*)
  • Внешние ссылки ограничены одним элементом AGGREGATE, обозначенным как Root (определен как сущность).
  • Набор из последовательности правил применяется в пределах в границы совокупности.
  • Центральный шаблон в пределах ограниченного контекста.

61 of 88

ПРИМЕР: ПРОЕКТИРОВАНИЕ АГРЕГАТОВ В ПРЕДЕЛАХ ГИБКОГО ПРОЕКТА УПРАВЛЯЕМОГО КОНТЕКСТА (НА ОСНОВЕ НА ВОНА ВЕРНОН - ПРАВИЛА ДЛЯ ДИЗАЙНА АГРЕГАТА*)

61

Рассмотрены следующие варианты использования:

  • Продукты имеют отставание в предметах, релизах, а также спринтах.
  • Новый бэклог продукта предметов запланирован.
  • Новый выпуск товара запланирован.
  • Новые продуктовые спринты - запланированы.
  • Запланированное отставание предмета может быть запланировано для выпуска.
  • Запланированное отставание предмета может быть совершенным на спринте.

62 of 88

ИСХОДНАЯ ПОПЫТКА: БОЛЬШОЙ АГРЕГАТ

62

Основанный на на: Вона Вернон Агрегаты Дизайн Правила сочинение:

https://vaughnvernon.co/wordpress/wp-content/uploads/2014/10/DDD_COMMUNITY_ESSAY_AGGREGATES_PART_1.pdf Общий под творческий Общины Attribution-NoDerivs 3.0 Непортированный Лицензия - http://creativecommons.org/licenses/by-nd/3.0/

public class Product extends ConcurrencySafeEntity { private ProductId productId;

private String description;

private String name;

private TenantId ;

private Set<BacklogItem> backlogItems; private Set<Release> releases;

private Set<Sprint> sprints;

...

}

63 of 88

ИСХОДНАЯ ПОПЫТКА: БОЛЬШОЙ АГРЕГАТ

63

Основанный на на: Вона Вернон Агрегаты Дизайн Правила сочинение:

https://vaughnvernon.co/wordpress/wp-content/uploads/2014/10/DDD_COMMUNITY_ESSAY_AGGREGATES_PART_1.pdf Общий под творческий Общины Attribution-NoDerivs 3.0 Непортированный Лицензия - http://creativecommons.org/licenses/by-nd/3.0/

public class Product {

...

public void planBacklogItem(

String aSummary, String aCategory,

BacklogItemType aType, StoryPoints aStoryPoints) {

...

}

public void scheduleRelease(

String aName, String aDescription,

Date aBegins, Date anEnds) {

...

}

public void scheduleSprint(

String aName, String aGoals, Date aBegins, Date anEnds) {

...

}

...

}

64 of 88

БОЛЬШОЙ АГРЕГАТ. НЕДОСТАТКИ

64

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

  • Два пользователя, Майк и Джон, посмотрели один и тот же продукт, отмечены в качестве вариант 1 и начинают работать с ним.
  • Майк выполняет планы на новый элемент невыполненной работы. Версия продукта увеличивается до 2.
  • Джон планирует новый релиз и пытается сохранить, но его фиксация не выполняется, потому что она была основана на продукте 1-й версии.

65 of 88

РЕДИЗАЙН НАШЕГО АГРЕГАТА

65

66 of 88

РЕДИЗАЙН НАШЕГО АГРЕГАТА

66

public class Product {

public BacklogItem planBacklogItem(

BacklogItemId aNewBacklogItemId, String aSummary,

String aCategory, BacklogItemType aType, StoryPoints aStoryPoints) {

...

}

public Release scheduleRelease(

ReleaseId aNewReleaseId, String aName,

String aDescription, Date aBegins, Date anEnds) {

...

}

public Sprint scheduleSprint(

SprintId aNewSprintId, String aName, String aGoals, Date aBegins,

Date anEnds) {

...

}

...

}

67 of 88

РЕДИЗАЙН НАШЕГО АГРЕГАТА

67

public class ProductBacklogItemService {

@Transactional

public void planProductBacklogItem(

String aTenantId, String aProductId,

String aSummary, String aCategory,

String aBacklogItemType, String aStoryPoints) {

Product = productRepository.productOfId(

new TenantId(aTenantId), new ProductId(aProductId));

BacklogItem plannedBacklogItem =

product.planBacklogItem(

aSummary, aCategory,

BacklogItemType.valueOf(aBacklogItemType),

StoryPoints.valueOf(aStoryPoints));

backlogItemRepository.add(plannedBacklogItem);

}

...

}

68 of 88

ПРАВИЛА ДИЗАЙНА АГРЕГАТОВ (НА ОСНОВЕ ВОНА ВЕРНОН - ПРАВИЛА АГРЕГАТОВ*)

68

Под лицензией Attribution-NoDerivs 3.0 - http://creativecommons.org/licenses/by-nd/3.0/

  • Правильная модель различна в границах последовательности.
  • Дизайн маленьких агрегатов.
  • Ссылка на другие агрегаты по id.
  • Использовать возможные последовательности вне совокупной границы.
  • Сервис оптимистичного параллелизма.

69 of 88

МОДЕЛЬ ИСТИННЫХ ИНВАРИАНТОВ В ПОСЛЕДОВАТЕЛЬНОСТИ ГРАНИЦ

69

  • Все границы внутри придерживаются определенным правилам.
  • Совокупность в транзакционной последовательности границ.
  • Правильно спроектированной совокупности можно модифицировать в любом направлении. Требуется бизнесу с его взглядами, полностью соответствует внутри одной транзакции.
  • Правильно разработанный ограниченный контекст изменяет только одну совокупность примера сделки во всех случаях.

70 of 88

ССЫЛКА НА ДРУГИЕ АГРЕГАТЫ ПО ИДЕНТИФИКАЦИИ

70

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

71 of 88

ДИЗАЙН МАЛЕНЬКОГО АГРЕГАТА

71

  • Пределы большого кластера масштабируются и производительность.
  • Ограничение в общности к совокупности корня и нескольких напечатанных значений (предпочтение ценности объектов над сущностями). Атрибуты или необходимы любые другие, как минимум. Необходимые средства – те атрибуты, которые должны быть последовательны с другими.
  • Не будьте сбиты с толку отношениями.

72 of 88

ИСПОЛЬЗОВАТЬ ВОЗМОЖНЫЕ ПОСЛЕДОВАТЕЛЬНОСТИ ВНЕ НАШИХ ГРАНИЦ

72

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

73 of 88

ИСПОЛЬЗОВАТЬ ВОЗМОЖНЫЕ ПОСЛЕДОВАТЕЛЬНОСТИ ВНЕ НАШИХ ГРАНИЦ

73

Пример основан на книге Вона Вернон - Правила дизайна агрегатов:

https://vaughnvernon.co/wordpress/wp-content/uploads/2014/10/DDD_COMMUNITY_ESSAY_AGGREGATES_PART_2.pdf

Под лицензией - http://creativecommons.org/licenses/by-nd/3.0/

Например, совершение и отставание предмета для спринта, требует обновления двух агрегатов: спринт и элемент невыполненной работы. Первый обновлятся BackLogItem. BackLogItem поднимает соответствующее событие:

public class BacklogItem extends ConcurrencySafeEntity {

...

public void commitTo(Sprint aSprint) {

...

DomainEventPublisher

.instance()

.publish(new BacklogItemCommitted( this.tenantId(), this.backlogItemId(), this.sprintId()));

}

...

}

public class BackLogItemApplicationService {

...

public void commitBacklogItemToSprint(

String aTenantId, String aBacklogItemId, String aSprintId) {

TenantId tenantId = new TenantId(aTenantId); BacklogItem backlogItem =

backlogItemRepository().backlogItemOfId( tenantId, new BacklogItemId(aBacklogItemId));

Sprint sprint = sprintRepository().sprintOfId( tenantId, new SprintId(aSprintId));

backlogItem.commitTo(sprint); this.backlogItemRepository().save(backlogItem);

...

}

}

74 of 88

ИСПОЛЬЗОВАТЬ ВОЗМОЖНЫЕ ПОСЛЕДОВАТЕЛЬНОСТИ ЗА ПРЕДЕЛАМИ ГРАНИЦЫ (ПРОДОЛЖЕНИЕ НАШЕГО ПРИМЕРА)

74

Пример основан на книге Вона Вернон - Правила дизайна агрегатов:

https://vaughnvernon.co/wordpress/wp-content/uploads/2014/10/DDD_COMMUNITY_ESSAY_AGGREGATES_PART_2.pdf Лицензия - http://creativecommons.org/licenses/by-nd/3.0/

Затем, событие BackLogItemCommitted обрабатывается и обновляет агрегат спринта:

public class Sprint extends ConcurrencySafeEntity {

...

public void commit(BacklogItem aBacklogItem) {

...

int ordering = this.backlogItems().size() + 1;

CommittedBacklogItem committedBacklogItem =

new CommittedBacklogItem(

this.tenantId(), this.sprintId(), aBacklogItem.backlogItemId(), ordering);

this.backlogItems().add(committedBacklogItem);

}

...

}

public class SprintApplicationService {

...

public void handle(BacklogItemCommitted backLogCommittedEvent) {

BacklogItem backlogItem =

backlogItemRepository().backlogItemOfId(

backLogCommittedEvent.tenantId(), backLogCommittedEvent.backlogItemId());

Sprint sprint =

sprintRepository().sprintOfId(

backLogCommittedEvent.tenantId(), backLogCommittedEvent.committedToSprintId());

sprint.commit(backlogItem);

this.sprintRepository().save(sprint);

}

...

}

75 of 88

ОПЦИИ ВОЗМОЖНЫХ ПОСЛЕДОВАТЕЛЬНОСТЕЙ

75

  • Синхронный
    • Обновление сделки второй совокупности правильно начинается после первого обновления.
    • Преимущества
      • Маленький непоследовательный период

      • Не требуется делать специфические инструменты

    • Недостаток: решение имеет проблемы, когда реализуется отказоустойчивый сценарий.

76 of 88

ОПЦИИ ВОЗМОЖНЫХ ПОСЛЕДОВАТЕЛЬНОСТЕЙ

76

  • Асинхронный
    • Обновление сделки второй общности начинается после периода зависимости на механизм обмена сообщениями
    • Преимущество: несколько готовых решений (как NServiceBus)
    • Недостаток: долгий непоследовательный период

77 of 88

ОПЦИИ ВОЗМОЖНЫХ ПОСЛЕДОВАТЕЛЬНОСТЕЙ

77

  • Сценарии отказоустойчивости:
    • Повторить попытку обработки события для второго агрегата.
    • Разрешить управление (основано на варианте использования), вмешаться и исправить непоследовательное владение.
    • Дизайн автоматизированной компенсирующей процедуры к откату изменений на уже модифицированной совокупности.

78 of 88

СЕРВИС ОПТИМИСТИЧЕСКОЙ СОВМЕСТНОСТИ (НЕТ БЛОКИРОВКИ СТРАТЕГИИ)

78

  • Определять версию атрибута на общности уровня корня.
  • Обновить каждое время команды, являющееся выполненной в совокупности.
  • При сохранении – выполнять проверку версии и валидацию.

79 of 88

ДРУГИЕ РЕКОМЕНДАЦИИ

79

  • Значение части объекта – значения объектов, требующие меньше усилий обслуживания, чем сущности.
  • Рассказывать используя, не спрашивать принцип:
    • Объект клиента должен не спрашивать объект сервер за содержащиеся части, а сделать решение, основанное на праве, то есть, а потом попросить объект сервера сделать что-то.

    • Вместо этого, клиент должен "рассказывать", а сервер делать, с использованием команды на сервере и публичного интерфейса.

80 of 88

НАРУШЕНИЕ ПРАВИЛ

80

  • Правила не должны быть нарушены

81 of 88

НАРУШЕНИЕ ПРАВИЛ

81

Если в самом деле, в самом деле нужно – здесь есть причины:

  • Пакетные обновления на такой же тип агрегатов. Эти должно быть безопасно.
  • Технические ограничения (ранее: не доступный механизм обмена сообщениями).
  • Производительность запросов. Там сила в производительности агрегатов retrieval, если данные агрегаты ссылаются на ещё одну общность напрямую (не через id).

82 of 88

ФАБРИКИ

82

83 of 88

ОБЗОР

  • Сдвиг ответственности за создание экземпляров из сложных объектов и АГРЕГАТОВ как отдельный объект, который может сам не иметь модели домена, но все еще часть домена из дизайна. Предоставленный интерфейс содержит в себе все сложности сборки и делает ссылки на клиент в конкретные классы объектов, где создается экземпляр. Создавать все АГРЕГАТЫ в качестве части обеспечения их возможностей. (Эванс*)

83

84 of 88

РАСПОЛОЖЕНИЕ МЕТОДА ФАБРИКИ

84

  • На общий корень
  • На сервис
  • В пределах отдельного фабричного объекта

85 of 88

АГРЕГАТ, КАК ФАБРИКА

85

  • Агрегаты довольно часто содержат достаточно информации для создания еще одной общности.
  • Фабричные методы не должны обязательно именоваться как Create()*

86 of 88

АГРЕГАТ, КАК ФАБРИКА

86

public class Forum extends Entity {

...

public Discussion startDiscussionFor( String aSubject,

Author anAuthor,

ForumIdentityService aForumIdentityService) {

if (this.isClosed()) {

throw new IllegalStateException("Forum is closed.");

}

Discussion =

new Discussion(

this.tenant(), this.forumId(),

aForumIdentityService.nextDiscussionId(), anAuthor,

aSubject);

return discussion;

}

...

}

87 of 88

МЕТОД ФАБРИКИ НА ТЕХНИЧЕСКИЙ ДОМЕН СЕРВИСА

87

public class TranslatingCollaboratorService implements CollaboratorService {

...

@Override

public Author authorFrom(Tenant aTenant, String anIdentity) {

Author author =

this.userInRoleAdapter()

.toCollaborator(

aTenant,

anIdentity, "Author", Author.class);

return author;

}

...

}

88 of 88

ВЫВОДЫ

88

  • Тактическое моделирование DDD нужно внутри ограниченного контекста.
  • Общность является центральным тактическим шаблоном.
  • Несколько правил нужно соблюдать, когда проектирование в совокупности.
  • Совокупность создана из сущности и целостности объекта.
  • Событие важности противоречит сущности.
  • Агрегаты и сервис домена могут действовать в качестве фабрики.