#oop #domain-driven-design #repository-pattern #ddd-repositories #aggregateroot
#ооп #дизайн, управляемый доменом #репозиторий-шаблон #ddd-репозитории #aggregateroot
Вопрос:
Допустим, я не хочу обновлять псевдоним объекта Person с правилом, что этот псевдоним используется до 5 другими лицами. Как я должен это сделать?
Должен ли я выполнять проверку в службе домена перед вызовом обновления?
Или лучше передать PersonRepository в метод PersonEntity.update(…) и выполнить поиск и проверку внутри метода обновления сущности?
Дополнительное разъяснение:
@plalx идея создания дополнительной сущности для выполнения этого правила умна.
Когда я читаю о DDD, я часто читаю, что передача репозитория в сущность не рекомендуется (если это не зло). Вот почему я пытаюсь найти пример, где нам нужен репозиторий или какой-либо другой сервис в сущности). На самом деле я не знаю, является ли плохим только передача репозитория, а служба в порядке, или передача службы в сущность аналогично не рекомендуется.
Давайте предположим, что это правило не такое сложное и важное бизнес-правило, и у нас может быть несколько похожих правил (так много, что создание дополнительной сущности для каждого правила для каждого атрибута, который мы хотим проверить, немного перегружено). Давайте предположим, что мы можем разрешить 6 или 7 одинаковых псевдонимов в случае одновременного обновления (мы только хотим ограничить их достаточно небольшим числом) и проверить
personRepository.usageOfNickname(this/person.nickname) < 5
достаточно. В таком случае, какой дизайн лучше с точки зрения DDD?
-
передача PersonRepository в объект Person
class Person {
...
boolean changeNickname(newNickname, nicknameUsageService) {
if (personRepository.usageOfNickname(this.nickname) < 5) {
this.nickname = newNickname;
return true;
} else {
return false; //or throw
}
}
}мне это кажется наиболее очевидным и прямым, преимущество заключается в том, что логика заключена в сущности, но что меня беспокоит, так это жалкая передача репозитория в сущность и это ощущение, что Эванс не одобряет это
-
вместо передачи PersonRepository в объект Person, передающий PersonService в объект Person (аналогично примеру @plalx) — передает ли служба в объект в любом случае лучше, чем репозиторий?
- выполнение проверки в сервисе, аналогичной в примере @plalx
changePersonNickname(personId, newNickname){...}
но в примере @plalx использование сервиса кажется оправданным, поскольку оно работает с двумя объектами, здесь у нас есть только один объект, и я боюсь, что если бы эта логика была в сервисе, а не в объекте, о котором идет речь, это не привело бы к анемичной модели домена и выходу из DDD.
Комментарии:
1. Недостатком внедрения сервисов является то, что это делает тестирование более утомительным (требуется больше макетов / заглушек). Причина, по которой вы не передаете весь репозиторий, заключается в соблюдении принципа разделения интерфейсов (ISP); предпочтение интерфейсам, зависящим от конкретной задачи (например,
NicknameUsageProvider
которые могут быть реализованы классом репозитория). Наконец, что вы также можете сделать, так это извлечь данные из службы приложений и передать их методу AR.nicknameUsage = …; person.changeNickname(newNickname, nicknameUsage)
.
Ответ №1:
Или лучше передать PersonRepository в метод PersonEntity.update(…) и выполнить поиск и проверку внутри метода обновления сущности?
Однако это не помешало бы нарушению правила из-за параллелизма, поскольку, как только вы проверили personRepo.usageCountOfNickname(nickname) <= 5
, оно могло измениться сразу после.
Если вы хотите строгую согласованность, вы могли бы ввести NicknameUsage
aggregate root для обеспечения соблюдения этой политики. Вы бы изменили более 1 AR в транзакции, но это, вероятно, не имеет большого значения, поскольку маловероятно, что в любом случае будет много споров по одним и тем же псевдонимам, верно?
Например.
changePersonNickname(personId, newNickname) {
transaction {
person = personRepository.personOfId(personId);
currentNicknameUsage = nicknameUsageRepository.usageOfNickname(person.nickname);
currentNicknameUsage.release();
newNicknameUsage = nicknameUsageRepository.usageOfNickname(newNickname);
nicknameUsage.reserve(); //throws if 6 already
person.changeNickname(newNickname);
}
}
Вы могли бы также инкапсулировать логику управления использованием псевдонима в службе домена, которая затем внедряется в changeNickname
работу AR.
Например.
class Person {
...
void changeNickname(newNickname, nicknameUsageService) {
nicknameUsageService.reserveAndRelease(newNickname, this.nickname);
this.nickname = newNickname;
}
}
Если вы хотите устранить все риски NicknameUsage
нарушения синхронизации с User-Nickname
отношениями, вы можете спроектировать так, чтобы NicknameUsage
это был единственный объект, отслеживающий отношения между пользователями и их псевдонимами (псевдоним вообще не является частью User
AR).
Наконец, у меня нет большого опыта с возможной согласованностью, и, надеюсь, кто-то еще прольет некоторый свет на то, что было бы правильным подходом, но если вы не хотите изменять много ARS за транзакцию, то я думаю, что есть несколько стратегий, которые вы могли бы использовать.
Например, вы можете разрешить > 6 пользователям использовать один и тот же псевдоним, но затем запустить процесс, который обнаруживает нарушения и помечает таких пользователей нарушением политики псевдонимов, где у них есть льготный период для изменения своего псевдонима, иначе он будет автоматически установлен на что-то другое (или любое другое компенсирующее действие). Обратите внимание, что у вас все равно будет проверка с использованием службы домена, чтобы ограничить количество нарушений.
Если вы хотите предотвратить нарушения, вы также можете использовать какую-нибудь сагу, где сначала резервируется новый ник, затем освобождается старый и, наконец, меняется ник пользователя. Будет короткий период времени, когда у человека фактически будет зарезервировано 2 псевдонима, но никогда не будет времени, когда псевдонимы используются более 6 раз.
Комментарии:
1. Спасибо за отличный ответ. Я добавил больше разъяснений в свой вопрос. Пожалуйста, взгляните. Сага и процесс обнаружения нарушений также являются хорошей идеей, но я хотел бы знать, достаточно ли этого, чтобы мы могли передать репозиторий в сущность, или нам следует избегать этого всеми средствами, даже путем добавления дополнительного объекта Nickname Usage,