逻辑何时属于业务对象/实体,何时属于服务?

Cas*_*ins 5 .net entity domain-driven-design business-logic

在尝试理解领域驱动设计时,我一直回到一个我似乎无法明确回答的问题.

您如何确定哪个逻辑属于Domain实体,哪些逻辑属于域服务?

示例:我们有一个在线商店的Order类.此类是实体和聚合根(它包含OrderItems).

Public Class Order:IOrder
{
    Private List<IOrderItem> OrderItems

    Public Order(List<IOrderItem>)
    {
        OrderItems = List<IOrderItem>
    }

    Public Decimal CalculateTotalItemWeight()
    //This logic seems to belong in the entity.
    {
        Decimal TotalWeight = 0
        foreach(IOrderItem OrderItem in OrderItems)
        {
            TotalWeight += OrderItem.Weight
        }
        return TotalWeight

    }
}
Run Code Online (Sandbox Code Playgroud)

我想大多数人会同意CalculateTotalItemWeight属于实体.但是,在某些时候我们必须将此订单发送给客户.要做到这一点,我们需要做两件事:

1)确定发运此订单所需的邮资费率.

2)确定邮资费率后打印运输标签.

这两个操作都需要在Order实体之外的依赖关系,例如外部Web服务来检索邮资费率.我们该如何完成这两件事?我看到几个选项:

1)直接在域实体中编码逻辑,如CalculateTotalItemWeight.然后我们打电话:

Order.GetPostageRate
Order.PrintLabel
Run Code Online (Sandbox Code Playgroud)

2)将逻辑放在接受IOrder的服务中.然后我们打电话:

PostageService.GetPostageRate(Order)
PrintService.PrintLabel(Order)
Run Code Online (Sandbox Code Playgroud)

3)创建为对订单进行操作的每个操作的一类,并通过构造器注入传递类的订单的一个实例(这是选项1的变形,但是允许RateRetriever和LabelPrinter类的重用):

 Public Class Order:IOrder
{
    Private List<IOrderItem> OrderItems
    Private RateRetriever _Retriever
    Private LabelPrinter _Printer

    Public Order(List<IOrderItem>, RateRetriever Retriever, LabelPrinter Printer)
    {
        OrderItems = List<IOrderItem>
        _Retriever = Retriever
        _Printer = Printer
    }

    Public Decimal GetPostageRate
    {
        _Retriever.GetPostageRate(this)
    }

     Public void PrintLabel
    {
        _Printer.PrintLabel(this)
    }
}
Run Code Online (Sandbox Code Playgroud)

如果有的话,您为这种逻辑选择了哪一种方法?您选择的理由是什么?最重要的是,是否有一套指导方针可以帮助您做出选择?

Dav*_*vid 1

我倾向于让外部服务确定运费。对我来说,这是应用程序逻辑,而不是特定于订单的逻辑。例如,您可能决定在一段时间内为超过一定规模的订单或特定的忠实客户群提供免费送货服务。对我来说,逻辑往往会独立于订单的构建方式而变化。

我很可能会将负责下订单的代码(某种订单处理服务,在应用程序层或命令处理程序中)移交给服务以获取运费,然后将该费率传递到顺序,所以我猜是选项2。

为了打印运输标签,我倾向于让域按照http://www.udidahan.com/2009/06/14/domain-events-salvation/的方式引发事件。然后,一个单独的处理程序将处理标签的打印。同样,其逻辑是打印标签的方式可能会随着构建订单的方式而变化,因此将其分开是有意义的。使用域事件似乎是确保在正确的时间打印标签的最干净的方法,而不需要订单(或者实际上是订单处理器)了解打印逻辑。