【问题标题】:Where to check user email does not already exist?在哪里检查用户电子邮件尚不存在?
【发布时间】:2018-02-23 02:20:24
【问题描述】:

我有一个这样创建用户的帐户对象;

public class Account
{
    public ICollection<User> Users { get; set; }

    public User CreateUser(string email)
    {
        User user = new User(email);
        user.Account = this;
        Users.Add(user);
    }
}

在我的服务层创建一个新用户时,我调用了这个方法。但是,有一条规则是用户的电子邮件必须是该帐户唯一的,那么这在哪里呢?对我来说,它应该在 CreateUser 方法中添加一个额外的行,用于检查电子邮件是否对帐户是唯一的。

但是,如果要这样做,则需要加载该帐户的所有用户,这对我来说似乎有点开销。最好在数据库中查询用户电子邮件 - 但在方法中这样做需要帐户对象中的存储库,不是吗?也许答案是从存储库加载帐户而不是这样做;

var accountRepository.Get(12);
//instead do
var accountRepository.GetWithUserLoadedOnEmail(12, "someone@example.com");

然后帐户对象仍然可以检查电子邮件的用户集合,如果找到它就会被急切地加载。

这行得通吗?你会怎么做?

我使用 NHibernate 作为 ORM。

【问题讨论】:

    标签: domain-driven-design


    【解决方案1】:

    首先,我认为您不应该使用异常来处理“正常”业务逻辑,例如检查重复的电子邮件地址。这是一个很好的文档反模式,最好避免。保持对数据库的约束并处理任何重复的异常,因为它们无法避免,但请尝试通过检查将它们保持在最低限度。我不建议锁定表格。

    其次,你在这个问题上打了DDD标签,所以我会用DDD的方式回答。在我看来,您需要域服务或工厂。将此代码移入域服务或工厂后,您可以将 UserRepository 注入其中并调用它以查看用户是否已使用该电子邮件地址存在。

    类似这样的:

    public class CreateUserService
    {
    private readonly IUserRepository userRepository;
    
    public CreateUserService(IUserRepository userRepository)
    {
        this.userRepository = userRepository;
    }
    
    public bool CreateUser(Account account, string emailAddress)
    {
        // Check if there is already a user with this email address
        User userWithSameEmailAddress = userRepository.GetUserByEmailAddress(emailAddress);
        if (userWithSameEmailAddress != null)
        {
            return false;
        }
    
        // Create the new user, depending on you aggregates this could be a factory method on Account
        User newUser = new User(emailAddress);
        account.AddUser(newUser);
        return true;
    }
    }
    

    这使您可以稍微分离职责并使用域服务来协调事情。希望对您有所帮助!

    【讨论】:

    • 现在这不是意味着您已经将域逻辑(电子邮件应该对帐户唯一)从域层移到服务层吗?我认为域不会引发异常,另一种有效的方法是将 IsValid() 方法添加到将返回错误结果的帐户类吗?
    • 这个服务在领域层,它不是在服务层。此服务中没有基础设施,它在多个其他域对象之间进行协调。域服务是 DDD 的重要组成部分,它不仅仅是实体。我只是使用一个简单的布尔返回值来检查唯一的电子邮件地址,所以是的,如果需要,请随意使其更具表现力。
    • 在这种情况下,谁会调用域服务?是否会为 Account 对象的 CreateUser 方法注入属性?
    • @user1790300 控制器或其他服务会。将服务注入实体是不好的做法。
    【解决方案2】:

    如果您在 users 表上正确指定了约束,则添加应该抛出异常,告诉您已经存在重复值。您可以在 CreateUser 方法中捕获该异常并返回 null 或一些重复的用户状态代码,或者让它流出并稍后捕获。

    您不想测试它是否存在于您的代码中然后添加,因为在测试和添加之间有一种可能性很小,有人会出现并添加相同的电子邮件会导致异常反正扔了...

    public User CreateUser(string email)
    {
        try
        {
           User user = new User(email);
           user.Account = this;
           user.Insert();
        catch (SqlException e)
        {
          // It would be best to check for the exception code from your db...
          return null;
        }
    }
    

    【讨论】:

    • 问题是我使用 NHibernate 并向帐户添加用户不会更新数据库,直到我在 ISession 上调用刷新。我在数据库中有约束,因此它不会被添加,并且在持久化时会引发异常,但您关于检查然后插入的观点仍然有效。我可以将整个事情包装在一个事务中并在搜索时锁定该行,但这将是一个开销,我认为发生这种情况的可能性很小,因为它将是一个低吞吐量的站点
    • 我不建议在应用程序的“正常”流程中使用异常。这是一个有据可查的反模式。
    • 这实际上是证明规则的例外。因为您正在处理一个单独的系统(通常在单独的机器上),所以依赖异常是处理表现出来的并发问题的唯一方法。
    • 我并不是说你不应该捕获异常,但你应该通过首先检查来避免它。
    【解决方案3】:

    鉴于“用户的电子邮件必须是帐户唯一的规则”,那么最重要的是在数据库架构中指定电子邮件是唯一的,这样如果电子邮件重复,数据库INSERT就会失败.

    您可能无法阻止两个用户几乎同时添加相同的电子邮件,因此接下来的事情是代码应该(优雅地)处理由上述原因导致的 INSERT 失败。

    在您完成上述操作后,在插入之前查看电子邮件是否唯一是可选的。

    【讨论】:

      猜你喜欢
      • 2012-12-27
      • 1970-01-01
      • 2011-09-29
      • 1970-01-01
      • 1970-01-01
      • 2018-07-31
      • 2021-03-25
      • 1970-01-01
      • 2021-03-12
      相关资源
      最近更新 更多