由于“贫血领域模型”是反模式,为什么有这么多系统实现它?
我认为有几个原因
1.系统的复杂性
在一个简单的系统中(几乎是您在互联网上找到的所有示例和示例代码)如果我想实现:
将产品添加到订单
我把这个功能放在订单上
public void Order.AddOrderLine(Product product)
{
OrderLines.Add(new OrderLine(product));
}
很好而且超级面向对象。
现在假设我需要确保我需要验证产品是否存在于库存中,如果不存在则抛出异常。
我不能再将其放在订单上,因为我不希望我的订单依赖于库存,所以现在需要将其放在服务上
public void OrderService.AddOrderLine(Order order, Product product)
{
if (!InventoryService.Has(product)
throw new AddProductException
order.AddOrderLine(product);
}
我还可以将 IInventoryService 传递给 Order.AddOrderLine,这是另一种选择,但这仍然使 Order 依赖于 InventoryService。
Order.AddOrderLine 中仍然有一些功能,但通常仅限于订单范围,而根据我的经验,有更多的业务逻辑超出订单范围。
当系统不仅仅是基本的 CRUD 时,您最终将在 OrderService 中得到大部分逻辑,而在 Order 中得到很少的逻辑。
2. 开发者对OOP的看法
互联网上有很多关于实体应该采用哪种逻辑的热烈讨论。
就像是
订单.保存
秩序是否应该知道如何拯救自己?假设我们有相应的存储库。
现在订单可以添加订单行吗?如果我尝试用简单的英语来理解它,它也没有真正的意义。用户将产品添加到订单中,那么我们应该执行 User.AddOrderLineToOrder() 吗?这似乎太过分了。
OrderService.AddOrderLine() 怎么样。现在有点道理了!
我对 OOP 的理解是,为了封装,您将函数放在类上,该函数需要访问类的内部状态。如果我需要访问 Order.OrderLines 集合,我会将 Order.AddOrderLine() 放在 Order 上。这样类的内部状态就不会暴露。
3. IoC容器
使用 IoC 容器的系统通常都很贫乏。
这是因为您可以测试具有接口的服务/存储库,但无法(轻松)测试域对象,除非您将接口放在所有这些对象上。
由于“IoC”目前被誉为解决所有编程问题的解决方案,因此很多人盲目遵循它,这种方式最终导致贫血领域模型。
4. 面向对象编程很难,过程化很容易
我有一点“知识的诅咒“关于这一点,但我发现
对于新开发人员来说,拥有 DTO 和服务比 Rich Domain 容易得多。
可能是因为使用 Rich Domain,更难知道将逻辑放在哪些类上。什么时候创建新类?使用哪些模式? ETC..
对于无状态服务,您只需将其放入名称最接近的服务中即可。