我很想回答Mu,但我想详细说明一下。总结:不要让您对 ORM 的选择决定您如何定义域模型。
域模型的目的是成为一个丰富的面向对象的 API,对域进行建模。要遵循真正的领域驱动设计,领域模型必须不受技术约束。
换句话说,领域模型是第一位的,所有特定于技术的实现随后都由映射器解决,该映射器在领域模型和相关技术之间进行映射。这通常包括两种方式:对于 ORM 的选择可能会引入约束的数据访问层,以及对于 UI 技术施加额外要求的 UI 层。
如果实现与领域模型相距甚远,我们将讨论反腐败层。
在您的情况下,您所说的贫血域模型实际上是数据访问层。您最好的办法是定义以技术中立的方式对您的实体的访问进行建模的存储库。
例如,让我们看看您的订单实体。对不受技术约束的订单进行建模可能会导致我们出现这样的情况:
public class Order
{
// constructors and properties
public decimal CalculateTotal()
{
return (from li in this.LineItems
select li.CalculateTotal()).Sum();
}
}
请注意,这是一个普通的旧 CLR 对象 ( POCO ),因此不受技术约束。现在的问题是如何将这些数据输入和输出数据存储?
这应该通过抽象的 IOrderRepository 来完成:
public interface IOrderRepository
{
Order SelectSingle(int id);
void Insert(Order order);
void Update(Order order);
void Delete(int id);
// more, specialized methods can go here if need be
}
您现在可以使用您选择的 ORM 来实现 IOrderRepository。但是,某些 ORM(例如 Microsoft 的实体框架)要求您从某些基类派生数据类,因此这根本不适合作为 POCO 的域对象。因此,需要映射。
要意识到的重要一点是,您可能拥有在语义上类似于您的域实体的强类型数据类。然而,这是一个纯粹的实现细节,所以不要对此感到困惑。派生自例如EntityObject 的 Order 类不是 Domain Class - 它是一个实现细节,因此当您实现 IOrderRepository 时,您需要将 Order Data Class映射到 Order Doman Class。
这可能是一项乏味的工作,但您可以使用AutoMapper为您完成。
下面是 SelectSingle 方法的实现可能的样子:
public Order SelectSinge(int id)
{
var oe = (from o in this.objectContext.Orders
where o.Id == id
select o).First();
return this.mapper.Map<OrderEntity, Order>(oe);
}