域建模:正确行事

时间:2015-05-20 14:25:54

标签: domain-driven-design solid-principles domain-data-modelling

在看过Jimmy Bogard的优秀video on crafting wicked domains后,我尝试将同样的原则应用于我现有的一个项目,以评估我对这个概念的掌握程度。我对下面列出了疑问和怀疑。

域背景:管理员可以查看公司列表。然后他批准了一家公司。数据库应该将布尔字段更新为true并存储批准公司的用户的ID。

最初,我在服务层中编写了以下代码。它将请求传递给存储库,存储库更新db中的相应字段,然后发送邮件通知。

public void ApproveCompany(int companyId, int userId)
{
    _companyRep.ApproveCompany(companyId, userId);

    //send mail to company representatives on successful approval. 
}

为了创建丰富的域并在域类中封装逻辑,我创建了以下内容。

public void ApproveCompany(int companyId, int userId)
    {
        var user = _userRep.GetById(userId);
        var company = _companyRep.GetById(companyId);
        user.Approve(company);

        _companyRep.Insert(c);

        //send mail to company representatives on successful approval. 
    }

public class AdminUser
    {
        public string Name { get; set; }

        public void Approve(MyApprovedCompany c)
        {
            c.SetIsApproved(this);
        }       
    }

public class Company 
    {
        public bool IsApproved { get; private set; }
        public AdminUser ApprovedBy { get; private set; }

        public void SetIsApproved(AdminUser user)
        {
            if (this.IsApproved)
                throw new Exception("This company has already been approved by user: " + user.Name);

            this.IsApproved = true;
            this.ApprovedBy = user;
        }
    }

查询:

  1. 我所做的完全正确/部分正确吗?
  2. 从性能的角度来看,获取这两个对象只是为了创建一个将来会成为问题的正确类实例?
  3. 邮件通知是否属于服务层?
  4. 我的公司存储库如何处理与批准公司的用户相关的属性?我的公司存储库是否应该引用用户存储库(我认为这是错误的)。
  5. 或者,我可以编写如下的服务层,但我认为这也不正确。

    public void ApproveCompany(int companyId, int userId)
    {
        var user = _userRep.GetById(userId);
        var company = _companyRep.GetById(companyId);
    
        if(company.IsApproved)
        {
            throw new Exception("This company has already been approved by user: " + _userRep.GetById(company.ApprovedUserId).Name);        
        }
        else
        {
            user.Approve(company);
            _companyRep.Insert(c);
        }
    }
    

1 个答案:

答案 0 :(得分:2)

这些问题几乎不可能正确回答,但是我可以从我看到的内容中告诉你:

  1. 这有些正确,但我通常倾向于在操作所针对的AR上放置行为而不是执行它的actor。在这种情况下,双重调度并没有带来任何有用的恕我直言。因此,我会简化为company.Approve(adminUser)。您可能会说adminUser.approve(Company)更好地反映了一个用例,例如"管理员用户批准公司",但您可以转过身来说“#34;公司是由管理员批准的用户&#34 ;.另请注意,您拥有的company.SetIsApproved方法非常注重CRUD,当然也不能很好地反映您无处不在的语言。

  2. AR应设计得尽可能小。只要您不创建不必要的大型群集聚合,我就不会发现这会成为一个问题。我强烈建议您阅读Effective Aggregate Design by Vaughn Vernon

  3. 理想情况下,您应该依靠域事件来实现操作的副作用。有很多关于如何在Web上实现域事件的信息。但是,由于缺少发布/订阅机制,可以在应用程序服务层完成,也可以在AR方法级别注入邮件服务。

  4. 问题是您在另一个AR中引用了AR。 AR通常应通过身份引用其他AR。因此,Company不会保留AdminUser,只能保留用户的ID。通过这样做,您的问题就会消失,并减少AR的大小。