Когда логика принадлежит бизнес-объекту / сущности и когда она принадлежит сервису? - PullRequest
5 голосов
/ 30 января 2011

Пытаясь понять доменно-управляемый дизайн, я продолжаю возвращаться к вопросу, на который не могу дать окончательного ответа.

Как определить, какая логика принадлежит сущности Домена, а какая логика принадлежит Службе домена?

Пример: У нас есть класс 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

    }
}

Я думаю, что большинство людей согласятся, что CalculateTotalItemWeight принадлежит сущности. Однако в какой-то момент мы должны отправить этот заказ клиенту. Для этого нам нужно сделать две вещи:

1) Определите стоимость доставки, необходимую для доставки этого заказа.

2) Распечатайте транспортную этикетку после определения стоимости доставки.

Для обоих этих действий потребуются зависимости, которые находятся за пределами сущности Order, например внешний веб-сервис для извлечения почтовых тарифов. Как мы должны выполнить эти две вещи? Я вижу несколько вариантов:

1) Кодируйте логику непосредственно в объекте домена, например CalculateTotalItemWeight. Затем мы позвоним:

Order.GetPostageRate
Order.PrintLabel

2) Поместите логику в службу, которая принимает IOrder. Затем мы позвоним:

PostageService.GetPostageRate(Order)
PrintService.PrintLabel(Order)

3) Создайте класс для каждого действия, которое работает с Order, и передайте экземпляр этого класса в Order через конструктор Injection (это вариант варианта 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)
    }
}

Какой из этих методов вы выберете для этой логики, если таковые имеются? Какова причина вашего выбора? Самое главное, есть ли набор рекомендаций, которые привели вас к вашему выбору?

Ответы [ 4 ]

1 голос
/ 30 января 2011

Если вы обращаетесь к внешним веб-сервисам для получения Почтовой ставки , лучше создать интерфейс на прикладном уровне, так как сам evan предложил, что если вы хотите общаться с внешними веб-сервисами, вам следует создать интерфейс на прикладном уровне, вы бы внедрили реализацию сервиса в ваш доменный объект.Для печати отгрузочная этикетка , поскольку этикетка печатается только после определения почтовой стоимости, это событие такого рода, как PostageRateConfirmed, и ваш домен может инициировать это событие.

http://danhaywood.com/2010/04/30/accessing-domain-services-from-entities/

1 голос
/ 30 января 2011

Я бы использовал (2).

Это не добавляет дополнительной сложности вашему заказу.

Мне кажется, это естественное использование вспомогательного сервиса.

Обновление: в ответ на комментарий: Страница вики гласит:

Модель анемичного домена: с этим шаблон, логика обычно реализованы в отдельных классах, которые преобразование состояние домена объекты

1 голос
/ 30 января 2011

Я бы хотел, чтобы внешний сервис определял стоимость доставки.Для меня это логика приложения, а не логика конкретного заказа.Например, вы можете на определенный период предложить бесплатную доставку заказов более определенного размера или определенной группе постоянных клиентов.Для меня эта логика будет меняться независимо от того, как устроен заказ.

Скорее всего, у меня был бы код, который отвечает за размещение заказа (какой-то вид службы обработчика заказов, на прикладном уровне или вобработчик команд) передать сервису, чтобы получить тариф доставки, а затем передать этот тариф в заказ, поэтому я предполагаю вариант 2.

Для печати этикетки доставки я бы предпочел иметь доменвызвать событие в соответствии с http://www.udidahan.com/2009/06/14/domain-events-salvation/.. Затем отдельный обработчик будет заниматься печатью этикетки.Опять же, логика для этого заключается в том, что способ печати этикеток, вероятно, будет варьироваться независимо от того, как вы строите заказ, поэтому имеет смысл держать это отдельно.Использование доменного события, кажется, является самым чистым способом гарантировать, что этикетка будет напечатана в нужное время, не требуя, чтобы Заказ (или действительно обработчик заказа) знал о логике печати. ​​

0 голосов
/ 30 января 2011

Моя точка зрения: домен - это то, что содержит логику вашего приложения, без лишних инфраструктур.Логика заключается в том, что после подтверждения заказа печатается этикетка и определяется стоимость доставки.Это должно быть в домене.

Затем инфраструктура выполняет то, что хочет сделать домен.Домен может сообщить инфраструктуре через обмен сообщениями или события.

Таким образом, никакая инфраструктура не проникает в домен, вам требуется только способ транспортировки сообщений из домена.

...