1

我遵循领域驱动的设计规则来设计一个系统。我对服务、外部系统和验证有疑问。

聚合必须与其他系统的 Web 服务交互以进行验证并提供信息。我不确定允许聚合访问外部单词是否是个好主意。如果我创建一个服务来访问外部服务,我很难强制执行不变量和验证。如果我将所有逻辑都放在聚合中,这听起来不是一个好主意,但这些问题似乎消失了。

为了使问题更容易理解,假设有一个用户聚合,它必须发送一封电子邮件以确保电子邮件正确(在我的实际问题中,我必须与外部 Web 服务进行通信)

public class User {
    public User (Long id, String name, String email) {...}
    public changeEmail(String newEmail) {...}
    ...
}

public interface EmailValidatorService {
    /**
    * Sends a test email
    */
    public verifyEmail(String email) throws EmailException;
}

我不确定这是否是一个好主意,或者电子邮件验证逻辑是否应该是用户聚合的一部分。也许它可能是一项服务并且用户聚合可以使用它......但这听起来也不是一个好主意。

如果它是用户聚合的一部分,它将承担额外的责任,如果它是一项服务,我看不到执行域规则的简单方法。¿ 如果开发人员使用 changeEmail 而不通过服务验证它怎么办?

4

1 回答 1

3

您有几个选项可以实现此场景。一种是让处理此特定用例的应用程序服务调用验证功能:

class UserService
{
  EmailValidatorService emailValidatorService;
  UserRepository userRepository;

  public void changeUserEmail(string currentEmailAddress, string newEmailAddress)
  {
    var user = this.userRepository.GetByEmail(currentEmailAddress);
    if (user == null)
       throw ...;


    this.emailValidatorService.verifyEmail(newEmailAddress);

    user.changeEmail(newEmailAddress);

    // commit, etc...
  }
}

应用程序服务是注入此类验证规则的方便场所 - 需要调用外部服务或聚合无法轻松访问的服务。更一般地说,应用程序服务可以是一种“后备”机制,用于处理纯 DDD 方法不太适合的情况。此外,无论您使用域模型还是事务脚本之类的东西,应用程序服务都可以存在。

另一种选择是在 User 类的 changeEmail 方法中为聚合提供验证器:

class User
{
  string emailAddress;

  public void changeEmail(string newEmailAddress, EmailValidatorService validator)
  {
    validator.verifyEmail(newEmailAddress);
    this.emailAddress = newEmailAddress;
  }
}

这里的好处是聚合封装了与电子邮件地址更改相关的所有逻辑。因此,客户端代码不能在不提供验证器的情况下更改电子邮件地址——这是应用程序服务方法无法强制执行的。此外,这与通过依赖注入引用验证器服务的用户聚合不同,后者通常不受欢迎。相反,这是一种即时依赖注入。

在此特定场景中要考虑的另一件事是您希望执行的验证的性质。一个特点是,如果一个电子邮件地址在某一时刻有效,那么它将来可能不再有效。这意味着您已经需要制定工作流程来处理具有无效电子邮件地址的现有用户。如果该逻辑已经存在,为什么还要费心确保域中的验证呢?毕竟,即使验证器服务确保电子邮件地址有效且处于活动状态,也不能保证用户会访问它。

于 2012-10-01T18:56:27.413 回答