Где я должен поставить уникальную проверку в DDD? - PullRequest
12 голосов
/ 16 июня 2009

Я работаю над своим первым проектом DDD и думаю, что понимаю основные роли сущностей, объектов доступа к данным и их взаимосвязи. У меня есть базовая реализация валидации, в которой каждое правило валидации хранится вместе с соответствующей сущностью. Это хорошо работает для правил, которые применяются только к текущему объекту, но распадаются, когда нужны другие данные. Например, если у меня есть ограничение на то, что имя пользователя должно быть уникальным, я бы хотел, чтобы вызов IsValid () возвращал false, когда существует существующий пользователь с текущим именем.

Однако я не нахожу никакого чистого способа сохранить это правило проверки на самой сущности. Я хотел бы иметь функцию IsNameUnique на объекте, но большинство решений для этого потребовали бы, чтобы я внедрил объект доступа к данным пользователя. Должна ли эта логика быть во внешней службе? Если да, то как мне сохранить логику с самой сущностью? Или это то, что должно быть за пределами пользовательского объекта?

Спасибо!

Ответы [ 3 ]

3 голосов
/ 21 июня 2009

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

public interface ISpecification<T>
{
  bool IsSatisfiedBy(TEntity entity);
}

public class UniqueUsernameSpecification : ISpecification<User>
{
  private readonly IUserRepository _userRepository;

  public UniqueUsernameSpecification(IUserRepository userRepository)
  {
    _userRepository = userRepository;
  }

  public bool IsSatisfiedBy(User user)
  {
    User foundUser = _userRepository.FindUserByUsername(user.Username);
    return foundUser == null;
  }
}

//App code    
User newUser;

// ... registration stuff...

var userRepository = new UserRepository();
var uniqueUserSpec = new UniqueUsernameSpecification(userRepository);
if (uniqueUserSpec.IsSatisfiedBy(newUser))
{
  // proceed
}
2 голосов
/ 16 июня 2009

В DDD существует понятие, называемое агрегат . Это в основном отвечает за согласованность в приложении.

ИМХО, в данном случае, в частности, я предполагаю, что CustomerRepository будет внутри чего-то вроде «агрегата клиентов», а класс Customer будет корнем агрегата.

Корень тогда будет ответственен за выполнение всего этого, и никто другой не сможет получить доступ к параметрам CustomerRepository. И есть несколько возможностей:

  • CustomerRepository может выдать исключение, если имя не является уникальным (и Клиент может перехватить и вернуть ошибку или что-то в этом роде)
  • CustomerRepository может иметь IsValidUserName (), которую Клиент будет вызывать, прежде чем делать что-либо еще
  • Любой другой вариант, который вы можете придумать
1 голос
/ 12 декабря 2017

Я скажу, что это просто выходит за рамки DDD.

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

Какую модель согласованности вы используете?

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

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

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

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

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

...