1

Having seen Jimmy Bogard's excellent video on crafting wicked domains, I tried to apply the same principles to one of my existing projects to evaluate how well i have grasped the concept. I have my queries and doubts listed below.

Domain Background: Admin can view a list of companies. He then approves a company. The database is supposed to update a Boolean field to true and store the id of the user who approved the company.

Initially, I had the following code written in my service layer. It passes the request to the repository which updates the appropriate fields in db and then sends a mail notification.

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

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

Re factoring to create rich domains and encapsulate logic within the domain class, I created the below.

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;
        }
    }

Queries:

  1. Is what I have done completely correct/partially correct?
  2. From a performance viewpoint is fetching the two objects just to create the proper class instances going to be a problem in the future?
  3. Does the mail notification belong to the service layer?
  4. How would my company repository look like to handle the property related to the user who approves the company? Should my company repository have a reference to the user repository (i think that is wrong).

Alternatively, I could write the service layer as below, but I don't think that is correct either.

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);
    }
}
4

1 回答 1

2

这类问题几乎不可能正确回答,但我可以从我所看到的情况告诉你:

  1. 这有点正确,但我通常倾向于将行为放在操作所针对的 AR 上,而不是执行它的演员。在这种情况下,恕我直言,双重调度不会带来任何有用的东西。因此,我将简化为company.Approve(adminUser). 您可能会说这adminUser.approve(Company)更好地反映了“管理员用户批准公司”之类的用例,但您可以反过来说“公司由管理员用户批准”。另请注意,company.SetIsApproved您使用的方法非常面向 CRUD,当然不能很好地反映您无处不在的语言。

  2. AR应该设计得尽可能小。只要您不创建不必要的大型集群聚合,我认为这不会成为问题。我强烈建议您阅读Vaughn Vernon 的《有效聚合设计》

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

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

于 2015-05-21T16:00:08.250 回答