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

我试图理解贫血领域模型,以及为什么它们被认为是反模式。

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

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

这种方法实际上并没有遇到太多问题,但据推测,这是我应该尖叫着逃离的东西,因为它是一个 贫血领域模型

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

轮换服务需要的所有注入的存储库等等怎么办? 考虑到大多数时候我们不需要这些存储库,如何将它们注入到员工中?

10368 次浏览

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

根据经验,与域对象有内在联系并完全根据一个域对象实例定义的行为可以包含在域对象中。否则,为了保持职责清晰,最好像您已经做过的那样,将其放在外部的协作者/服务中。

在这种情况下,这并不构成贫血领域模型。一个贫血领域模型是 特别是关于验证和转换对象。例如,如果一个外部函数实际上更改了 Employes 的状态或者更新了它们的详细信息。

在这种情况下,你需要所有的员工,并根据他们的信息选择其中一个。拥有一个单独的对象来检查其他对象并就其发现的内容做出决定是可以的。拥有一个用于将一个对象从一个状态转换到另一个状态的对象是不可以的。

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

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

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

这一切都在你的头脑中-考虑轮换服务成为领域模型的一部分,问题就解决了。

轮换需要保存许多员工的信息,因此它既不属于领导,也不属于任何一个员工对象。它本身就应该是一个域对象。

只需将“ RotationService”重命名为“ Organization. UserSupportDepartment”即可。

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

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

  1. 作为(记录的)活动建模的行为是领域模型的核心。
  2. 将域活动建模为类,而不是方法。

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

EnquiryHandlerModel