2010-05-18 70 views
67

我想了解贫血域模型以及为什么它们被认为是反模式。避免贫血域模型 - 一个真实的例子

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

我有一个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."); 
} 

我真的不遇到这种做法许多问题,但据说这是我应该运行的东西尖叫,因为它是一个贫血域模型

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

对于轮岗服务需要的所有注入仓库如何?他们将如何注入员工,考虑到与员工打交道的大部分时间我们不需要任何这些仓库?

+0

+1很好的问题! – mdma 2010-05-18 05:53:20

+0

那么,如果不明显的话,ILead是什么样子的呢?selectEmployee()呢? – 2010-05-18 05:54:13

+0

那么在这种情况下,铅是一个网上查询,所以它会有一个评论属性等,但我们也有电话查询,应用程序,报价等,这些都是略有不同。 ILead接口将具有诸如LocationOfLead,TimeOfLead等属性 – cbp 2010-05-18 06:19:09

回答

49

在这种情况下,这并不构成一个贫血域模型。贫血域模型是specifically about validating and transforming the objects。所以一个例子是如果一个外部函数实际改变了员工的状态或者更新了他们的详细信息。

在这种情况下发生了什么是您正在采取所有的员工,并根据他们的信息选择其中之一。拥有一个单独的对象来检查其他对象并就其所发现的事物做出决定是很好的。有一个对象用于将对象从一个状态转换到另一个状态是不好的。

你的情况贫血域模型的一个例子是有一个外部方法

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

接受一个Employee对象和更新其工作时间为一周,确保标志提出,如果小时数超过一定的限制。这个问题是,如果你只有Employee对象,那么你不知道如何在正确的约束内修改他们的小时。在这种情况下,处理它的方法是将updateHours方法移入Employee类。这是贫血域模式反模式的关键。

+0

但是,如果Employee是数据库的持久对象呢。为什么我应该在里面放一个方法?同样的问题对于DTO没有放置方法是有效的。那么你会把updateHours方法放在哪里? – Pascal 2016-05-22 15:27:35

+0

'updateHours'属于Employee类。您应该通过任何必要的数据来更新小时,例如已完成的任务。协作者对象也很好,但最好不要服务。 – MauganRa 2017-08-06 16:15:39

29

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

作为一个经验法则,与域对象固有关联的行为完全由该域对象实例定义,可以包含在域对象中。否则,为了保持责任清晰,最好将其外部放在合作伙伴/服务中,就像您所做的那样。

+5

。这是一个真正的外部模块(LeadQueueManager或其他),有很多内部逻辑 - 这绝对不是一个贫血域模型。员工对呼叫队列调度的了解是什么?没有;) – TomTom 2010-05-18 06:26:24

13

这一切都在你的脑海 - 考虑轮换服务成为领域模型的一部分,问题就会消失。

轮换需要保留关于许多员工的信息,因此它既不属于潜在客户,也不属于任何单个员工对象。它确实值得自己成为一个域对象。

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

0

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

从你的问题,你打破我的头两个域分析建模规则: -

  1. 行为建模为(记录)活动是在心脏的领域模型。首先添加它们。
  2. 模型域活动作为类,而不是方法。

我会添加一个活动“查询”模型。有了它,模型就具有行为,并且可以组合并作为对象组工作,而无需外部控制器或脚本。

EnquiryHandlerModel