Bee*_*eep 18 c# architecture dns domain-driven-design anemic-domain-model
我们当前的O/RM工具并不真正允许丰富的域模型,因此我们不得不在各地使用贫血(DTO)实体.这工作正常,但我仍然在努力放置基于对象的基本业务逻辑和计算字段.
当前图层:
我们的存储库层具有大多数基本的提取/验证/保存逻辑,尽管服务层执行了许多更复杂的验证和保存(因为保存操作也执行日志记录,权限检查等).问题是在哪里放置这样的代码:
Decimal CalculateTotal(LineItemEntity li)
{
return li.Quantity * li.Price;
}
Run Code Online (Sandbox Code Playgroud)
要么
Decimal CalculateOrderTotal(OrderEntity order)
{
Decimal orderTotal = 0;
foreach (LineItemEntity li in order.LineItems)
{
orderTotal += CalculateTotal(li);
}
return orderTotal;
}
Run Code Online (Sandbox Code Playgroud)
有什么想法吗?
Vij*_*tel 24
让我们回到基础:
服务有三种形式:域服务,应用服务和基础设施服务
这是您的数据访问和一致性检查的地方.在纯DDD中,您的Aggregate Roots将负责检查一致性(在持久化任何对象之前).在您的情况下,您将使用域服务层中的检查.
建议的解决方案:将现有服务拆分开来
使用新的域服务层来封装DTO的所有逻辑,以及您的一致性检查(使用规范,可能?).
使用Application Service公开必要的fetch方法(FetchOpenOrdersWithLines
),它将请求转发到您的Repository(并使用泛型,如Jeremy建议的那样).您还可以考虑使用" 查询规范"来包装查询.
从你的仓库,使用规格在你的域服务层坚持你的对象之前检查对象的一致性等.
您可以在埃文斯的书中找到支持信息:
Mar*_*ann 11
我很想回答穆,但我想详细说明.总结:不要让您选择的ORM决定您如何定义域模型.
域模型的目的是成为一个丰富的面向对象的API,用于对域进行建模.要遵循真正的域驱动设计,必须定义域模型不受技术限制.
换句话说,域模型首先出现,所有特定于技术的实现随后由映射器解决,映射器在域模型和所讨论的技术之间进行映射.这通常包括两种方式:对ORM可能引入约束的数据访问层,以及UI技术强加额外要求的UI层.
如果实施距离域模型非常远,我们将讨论反腐败层.
在您的情况下,您所谓的贫血领域模型实际上是数据访问层.您最好的办法是定义存储库,以技术中立的方式模拟对您的实体的访问.
例如,让我们看一下您的订单实体.对技术不受约束的建模可能会导致我们这样的事情:
public class Order
{
// constructors and properties
public decimal CalculateTotal()
{
return (from li in this.LineItems
select li.CalculateTotal()).Sum();
}
}
Run Code Online (Sandbox Code Playgroud)
请注意,这是一个普通的旧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
}
Run Code Online (Sandbox Code Playgroud)
您现在可以使用您选择的ORM实现IOrderRepository.但是,某些ORM(例如Microsoft的Entity Framework)要求您从某些基类派生数据类,因此这与域对象作为POCO完全不适合.因此,需要映射.
要认识到的重要一点是,您可能拥有与您的域实体在语义上类似的强类型数据类.但是,这是一个纯粹的实现细节,所以不要对此感到困惑.从例如EntityObject 派生的Order类不是域类 - 它是一个实现细节,因此当您实现IOrderRepository时,您需要将Order Data Class映射到Order Doman类.
这可能是一项繁琐的工作,但您可以使用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);
}
Run Code Online (Sandbox Code Playgroud)
这正是服务层的用途 - 我也看到过称为BusinessLogic层的应用程序.
这些是您希望花费大部分时间进行测试的例程,如果它们位于自己的层中,那么模拟存储库层应该是直截了当的.
存储库层应尽可能通用化,因此它不适合业务逻辑,而是特定类的个体.
归档时间: |
|
查看次数: |
3092 次 |
最近记录: |