jle*_*bke 2 domain-driven-design aggregates
我有一个域Aggregate,称之为"Order",其中包含OrderLines列表.订单会跟踪订单行上的金额总和.客户有一个正在运行的"信用"余额,他们可以从中订购,通过汇总其数据库事务的历史记录来计算.一旦他们用尽了"池"中的所有钱,他们就无法订购更多产品.
因此,每次在订单中添加一行时,我需要检查池中剩余的数量以及订单是否将它们推到了它上面.池中的金额不断变化,因为其他相关客户不断使用它.
问题是,考虑到DDD,我如何得到这个数量,因为我不想用DataContext问题污染我的域层(在这里使用L2S).由于我不能只从域中查询数据库,我如何获取该数据以便验证业务规则?
这是使用域事件的实例吗?
您的订单汇总应完全封装.因此,它需要能够确定添加项目是否有效,即是否超出客户信用.有多种方法可以做到这一点,但它们都依赖于Order存储库返回一个知道如何执行此特定事务的特定聚合.例如,这可能是与您用于满足订单的订单汇总不同的订单汇总.
您必须识别,然后在代码中捕获您希望订单在这种情况下履行特定角色的事实,即添加其他订单项的角色.您可以通过为此角色创建一个接口以及具有该角色内部支持的相应聚合来完成此操作.
然后,您的服务层可以向您的订单存储库询问满足此显式角色接口的订单,因此存储库具有足够的信息,表明您需要能够构建满足该要求的内容.
例如:
public interface IOrder
{
IList<LineItem> LineItems { get; }
// ... other core order "stuff"
}
public interface IAddItemsToOrder: IOrder
{
void AddItem( LineItem item );
}
public interface IOrderRepository
{
T Get<T>( int orderId ) where T: IOrder;
}
Run Code Online (Sandbox Code Playgroud)
现在,您的服务代码如下所示:
public class CartService
{
public void AddItemToOrder( int orderId, LineItem item )
{
var order = orderRepository.Get<IAddItemsToOrder>( orderId );
order.AddItem( item );
}
}
Run Code Online (Sandbox Code Playgroud)
接下来,您实施的Order类IAddItemsToOrder需要一个客户实体,以便它可以检查贷方余额.因此,您只需通过定义特定接口来级联相同的技术.订单存储库可以调用客户存储库以返回履行该角色的客户实体并将其添加到订单聚合中.
因此,您将拥有一个基本ICustomer接口,然后以ICustomerCreditBalance接口形式显示一个显式角色.这些ICustomerCreditBalance行为既可以作为客户存储库的标记接口,也可以告诉客户您需要什么,因此它可以创建适当的客户实体,并且它具有支持特定角色的方法和/或属性.就像是:
public interface ICustomer
{
string Name { get; }
// core customer stuff
}
public interface ICustomerCreditBalance: ICustomer
{
public decimal CreditBalance { get; }
}
public interface ICustomerRepository
{
T Get<T>( int customerId ) where T: ICustomer;
}
Run Code Online (Sandbox Code Playgroud)
显式角色接口为存储库提供了所需的关键信息,以便正确决定从数据库中获取哪些数据,以及是否急切地或懒惰地获取数据.
请注意,在这种情况下我将CreditBalance属性放在ICustomerCreditBalance接口上.但是,它也可以在基本ICustomer接口上,ICustomerCreditBalance然后成为一个空的"标记"接口,让存储库知道您将要查询信用余额.这就是让存储库知道它所返回的实体所需的角色.
正如您在问题中提到的那样,将所有这些结合在一起的最后一部分是域事件.如果超出客户的贷方余额,订单可能会引发故障域事件,以通知服务层订单无效.另一方面,如果客户有足够的信用,它可以更新客户对象的余额或提出域事件以通知系统的其余部分需要减少余额.
我没有将域事件代码添加到CartService类中,因为这个答案已经很长了!如果您想了解更多有关如何执行此操作的建议,建议您针对该特定问题发布另一个问题,我会在那里进行扩展;-)