Организации DDD, использующие Услуги

У меня есть приложение, которое я пытаюсь построить по крайней мере с номинальной моделью домена типа DDD, и я борюсь с определенной частью.

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

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

public class Ticket
{
    public Ticket(int id, ConstantRates constantRates, FinancialCalculationService f, RateCalculationService r)
    {
        Id = id;
        ConstantRates = constantRates;
        FinancialCalculator = f;
        RateCalculator = r;
    }

    private FinancialCalculationService FinancialCalculator { get; set; }

    private RateCalculationService RateCalculator { get; set; }

    private ConstantRates ConstantRates { get; set; }

    public int Id { get; private set; }

    public double ProjectedCosts { get; set; }

    public double ProjectedBenefits { get; set; }

    public double CalculateFinancialGain()
    {
        var discountRate = RateCalculator.CalculateDiscountRate(ConstantRates.Rate1, ConstantRates.Rate2,
                                                                ConstantRates.Rate3);

        return FinancialCalculator.CalculateNetPresentValue(discountRate,
                                                            new[] {ProjectedCosts*-1, ProjectedBenefits});
    }
}


public class ConstantRates
{
    public double Rate1 { get; set; }
    public double Rate2 { get; set; }
    public double Rate3 { get; set; }
}

public class RateCalculationService
{
    public double CalculateDiscountRate(double rate1, double rate2, double rate3 )
    {
        //do some jibba jabba
        return 8.0;
    }
}

public class FinancialCalculationService
{
    public double CalculateNetPresentValue(double rate, params double[] values)
    {
        return Microsoft.VisualBasic.Financial.NPV(rate, ref values);
    }

}

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

Прочитав "Синюю книгу", но на самом деле ничего такого в этом стиле не построил, я ищу руководство.

РЕДАКТИРОВАТЬ

Спасибо всем за отзывы! Исходя из того, что я слышу, похоже, что моя модель должна выглядеть следующим образом. Это выглядит лучше?

public class Ticket
{
    public Ticket(int id)
    {
        Id = id;
    }

    private ConstantRates ConstantRates { get; set; }

    public int Id { get; private set; }

    public double ProjectedCosts { get; set; }

    public double ProjectedBenefits { get; set; }

    public double FinancialGain { get; set; }
}



public class ConstantRates
{
    public double Rate1 { get; set; }
    public double Rate2 { get; set; }
    public double Rate3 { get; set; }
}

public class FinancialGainCalculationService
{
    public FinancialGainCalculationService(RateCalculationService rateCalculator, 
        FinancialCalculationService financialCalculator,
        ConstantRateFactory rateFactory)
    {
        RateCalculator = rateCalculator;
        FinancialCalculator = financialCalculator;
        RateFactory = rateFactory;
    }

    private RateCalculationService RateCalculator { get; set; }
    private FinancialCalculationService FinancialCalculator { get; set; }
    private ConstantRateFactory RateFactory { get; set; }

    public void CalculateFinancialGainFor(Ticket ticket)
    {
        var constantRates = RateFactory.Create();
        var discountRate = RateCalculator.CalculateDiscountRate(constantRates.Rate1, constantRates.Rate2,
                                                                constantRates.Rate3);

        ticket.FinancialGain = FinancialCalculator.CalculateNetPresentValue(discountRate,
                                                            new[] {ticket.ProjectedCosts*-1, ticket.ProjectedBenefits});
    }
}

public class ConstantRateFactory
{
    public ConstantRates Create()
    {
        return new ConstantRates();
    }
}

public class RateCalculationService
{
    public double CalculateDiscountRate(double rate1, double rate2, double rate3 )
    {
        //do some jibba jabba
        return 8.0;
    }
}

public class FinancialCalculationService
{
    public double CalculateNetPresentValue(double rate, params double[] values)
    {
        return Microsoft.VisualBasic.Financial.NPV(rate, ref values);
    }

}

Модель предметной области в конечном итоге оказывается довольно анемичной, но, поскольку я добавляю функции, возможно, она будет иметь к ней больше.

РЕДАКТИРОВАТЬ 2

Хорошо, я получил еще несколько отзывов о том, что, возможно, мои "расчетные" сервисы больше похожи на объекты стратегии, от которых может зависеть моя сущность. Вот еще один пример с большей логикой в ​​сущности и использованием этих стратегических объектов. Мысли об этом? Есть ли какие-либо проблемы с созданием экземпляров этих помощников непосредственно в организации? Я не думаю, что захочу имитировать их в своих тестах, но ОТО, я также не могу протестировать метод CalculateFinancialGain без тестирования этих объектов стратегии.

public class Ticket
{
    public Ticket(int id, ConstantRates constantRates)
    {
        Id = id;
        ConstantRates = constantRates;
    }

    private ConstantRates ConstantRates { get; set; }

    public int Id { get; private set; }

    public double ProjectedCosts { get; set; }

    public double ProjectedBenefits { get; set; }

    public double CalculateFinancialGain()
    {
        var rateCalculator = new RateCalculator();
        var financeCalculator = new FinanceCalculator();
        var discountRate = rateCalculator.CalculateDiscountRate(ConstantRates.Rate1, ConstantRates.Rate2,
                                                                ConstantRates.Rate3);

        return financeCalculator.CalculateNetPresentValue(discountRate,
                                                            ProjectedCosts*-1, 
                                                            ProjectedBenefits); 
    }
}

public class ConstantRates
{
    public double Rate1 { get; set; }
    public double Rate2 { get; set; }
    public double Rate3 { get; set; }
}

public class RateCalculator
{
    public double CalculateDiscountRate(double rate1, double rate2, double rate3 )
    {
        //do some jibba jabba
        return 8.0;
    }
}

public class FinanceCalculator
{
    public double CalculateNetPresentValue(double rate, params double[] values)
    {
        return Microsoft.VisualBasic.Financial.NPV(rate, ref values);
    }

}

5 ответов

Решение

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

В вашей ситуации я бы потянул FinancialCalculatorService а также RateCalculatorService из вашей сущности и заставить методы каждой службы принимать Ticket сущность как параметр.

Возьмите секунду и прочитайте стр. 105 доменного дизайна Эрик Эванс

Учитывая то, что мы видели в классах, я не думаю, что они действительно являются сервисами в смысле " синей книги", и я бы оставил калькуляторы в Ticket,

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

Если они действительно не имеют зависимостей от сущностей домена, подумайте о них как об "отдельных классах", а не "сервисах" (еще раз, в терминологии синей книги). Это, безусловно, подходит для Ticket зависеть от объектов стратегии (FinancialCalculator а также RateCalculator) которые сами не имеют экзотических зависимостей и сами не изменяют состояние сущностей домена.

Обновление для редактирования 2. Я думаю, что одним из преимуществ создания отдельных классов калькуляторов является то, что вы можете тестировать их независимо от Ticket, Строго говоря, билеты не несут ответственности за выполнение этих вычислений, они ответственны за правильные обращения к этим сотрудничающим классам. Так что я был бы склонен сделать их инъекционными / насмешливыми, как они были в вашем первоначальном примере.

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

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

Мне кажется, что CalculateFinancialGains() - это скорее вызов уровня обслуживания. Это приводит к тому, что Ticket очень анемичен, но я предполагаю, что у него другое поведение? И если это не так, это, вероятно, запах...

Я бы сказал, что службы используют сущности, а не наоборот.

Другое дело, не уверен в вашем домене, но вы уверены, что билет - это сущность, а не объект стоимости?

Этот вопрос на самом деле является примером обсуждения, которое содержится в книге "Чистый код" (стр. 96-97). Основной вопрос заключается в том, использовать ли процедурный подход или объектно-ориентированный подход. Надеюсь, я не нарушаю, повторяя пару частей здесь, но вот что Боб Мартин заявляет для руководства:

Процедурный код (код с использованием структур данных) позволяет легко добавлять новые функции без изменения существующих структур данных. ОО-код, с другой стороны, позволяет легко добавлять новые классы без изменения существующих функций.

Комплимент также верен:

Процедурный код затрудняет добавление новых структур данных, потому что все функции должны измениться. ОО-код затрудняет добавление новых функций, потому что все классы должны измениться.

Я понимаю, что DDD "тип значения" - это то, что Боб Мартин называет структурой данных.

Надеюсь, что это помогает, а не только добавляет шума:)

Другие вопросы по тегам