避免贫血领域模型 - 一个真实的例子

时间:2010-05-18 05:49:45

标签: anemic-domain-model

我正在努力了解贫血领域模型以及为什么它们被认为是一种反模式。

这是一个真实世界的例子。

我有一个Employee类,它有很多属性 - 名称,性别,用户名等

public class Employee
{
    public string Name { get; set; }
    public string Gender { get; set; }
    public string Username { get; set; }
    // Etc.. mostly getters and setters
}

接下来,我们有一个系统,涉及在销售人员之间平均轮换来电和网站查询(称为“线索”)。这个系统非常复杂,因为它涉及循环查询,检查假期,员工偏好等。因此,该系统目前分为服务:EmployeeLeadRotationService。

public class EmployeeLeadRotationService : IEmployeeLeadRotationService
{
     private IEmployeeRepository _employeeRepository;
     // ...plus lots of other injected repositories and services

     public void SelectEmployee(ILead lead)
     {
         // Etc. lots of complex logic
     }
}

然后在我们网站查询表格的背面,我们有这样的代码:

public void SubmitForm()
{
    var lead = CreateLeadFromFormInput();

    var selectedEmployee = Kernel.Get<IEmployeeLeadRotationService>()
                                 .SelectEmployee(lead);

    Response.Write(employee.Name + " will handle your enquiry. Thanks.");
}

我并没有真正遇到过这种方法的许多问题,但据说这是我应该尖叫的事情,因为它是贫血领域模型

但对于我来说,不清楚领导轮换服务中的逻辑应该去哪里。它应该领先吗?它应该进入员工吗?

轮换服务需要的所有注入的存储库等 - 如何将它们注入到员工中,因为大部分时间与员工打交道时我们不需要任何这些存储库?

4 个答案:

答案 0 :(得分:53)

在这种情况下,这不构成贫血领域模型。贫困领域模型是specifically about validating and transforming the objects。因此,如果外部函数实际上改变了Employees的状态或更新了它们的详细信息,那么这样做就是一个例子。

在这种情况下发生的事情是,您正在接纳所有员工,并根据他们的信息选择其中一个员工。有一个单独的对象可以检查其他对象并根据它找到的内容做出决定。拥有一个用于将对象从一个状态转换到另一个状态的对象是不可行的。

在你的案例中,一个贫血领域模型的例子是拥有一个外部方法

updateHours(Employee emp) // updates the working hours for the employee

接受一个Employee对象并更新一周工作的小时数,确保在小时数超过某个限制时引发标志。这样做的问题是,如果您只有Employee对象,那么您不知道如何在正确的约束内修改它们的小时数。在这种情况下,处理它的方法是将updateHours方法移动到Employee类中。这是贫血领域模式反模式的关键。

答案 1 :(得分:29)

我认为你的设计很好。如您所知,贫困域模型反模式是对避免在域对象中编码的任何行为的趋势的反对。但相反,它并不意味着与域对象相关的所有行为必须由该对象封装。

根据经验,与域对象内在联系并且完全根据该域对象实例定义的行为可以包含在域对象中。否则,为了明确责任,最好将其外部放在合作者/服务中,就像你所做的那样。

答案 2 :(得分:14)

一切都在您的脑海中 - 考虑轮换服务是域模型的一部分,问题就解决了。

轮换需要保留有关许多员工的信息,因此它既不属于潜在客户,也不属于任何单个员工对象。它确实应该成为域对象本身。

只需将“RotationService”重命名为“Organization.UserSupportDepartment”就可以了。显而易见。

答案 3 :(得分:0)

如果您的域模型仅包含角色和事物,而不是活动作为行为,那么它就是贫血。但是,我谈的是关于模型而不是对象的行为。我在另一个答案中谈到了它们之间的区别...... https://stackoverflow.com/a/31780937/116442

根据您的问题,您打破了我的前两个域分析建模规则: -

  1. 建模为(已记录)的行为活动是域模型的核心。先添加它们。
  2. 将域活动建模为类,而不是方法。
  3. 我会添加一项活动&#34;查询&#34;到模型。有了它,模型就具有行为,并且可以在没有外部控制器或脚本的情况下组合并作为一组对象工作。

    EnquiryHandlerModel