DDD, использующий репозиторий в сущности для проверки перед обновлением

#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,