DDD применяет агрегированные инварианты, когда дочерний объект изменяет состояние

DDD применяет агрегированные инварианты, когда дочерний объект изменяет состояние

У меня есть Contractобъект, который имеет DateRangeсвойство (dateFrom, dateTo) и Salesколлекцию.

Каждый Saleтакже имеет DateRangeсвойство , которое должно быть в границах Contract«с DateRange.

Каков правильный способ принудительного применения вышеуказанного инварианта при изменении Saleдаты?

public class Contract : Entity
{
    public DateRange Dates { get; private set; }
    public ICollection<Sale> Sales { get; private set; }
}

public class Sale : Entity
{
    public DateRange Dates { get; private set; }

    public void ChangeDates(DateRange dates)
    {
        Dates = dates;
    }
}

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

В Contractдату можно изменить в любое время, поэтому каждый Saleдолжен быть соответствующим образом изменен.

c#,domain-driven-design,

1

Ответов: 1


3 принят

Основываясь на ваших текущих требованиях

Интерпретация ваших требований, Contractявляется совокупным корнем и Saleявляется сущностью в Contractсовокупности. По мере того как требование заключается в том , что любые даты договора , должны лежать в пределах набора общественной ничтожной ChangeSaleDate ( длинный SaleID , DateRange дата ) { если ( это . Дата . Объемная ( даты )) { вар продажа = это . Продажи . Сначала ( s => s . Id == SaleId ); продажа . ChangeDates ( даты ); } else { throw new ArgumentException ( «Новые даты продажи должны быть между ...» , «датами» ); } } , любое изменение даты SaleId должно управляться Surround, поэтому сначала можно проверить даты DateRange.

Для этого у вас будет метод ChangeDates, например:

Sale

Это предполагает, что у вас есть internalили какой-либо другой способ определения Контракта в рамках контракта, и что вы внедрили Saleметод ICollection<Sale>поддержки такого типа проверки.

В зависимости от структуры проекта, можно также отметить Contractметод на ContractIdтак Sale, чтобы вы случайно не вызвать его из ваших прикладных услуг.

Из вашего комментария, это правда, этот механизм может привести к большому количеству методов для aggregate root ( Contract), поскольку он применяет инварианты, которые применяются ко всем «ContractRepositorys» в контракте. В результате подобные ситуации могут быть побуждением оспаривать требования ...

Борьба с требованиями

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

Альтернативной реализацией было бы создание Saleсобственной совокупности. В этом случае у вас не будет Contractсобственности на Contract- скорее, у вас будет только SaleRepositoryсвойство Sale, и каждая продажа получит свой глобальный уникальный идентификатор.

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

Чтобы изменить даты продажи, вы должны использовать, Saleчтобы получить a Contract, а затем Saleполучить a Saleи, возможно, передать контракт методу изменения даты на Sale:

public void ChangeDate(Contract contract, DateRange dates)
{
    if (contract.Id != this.ContractId)
        throw new ArgumentException("wrong contract", "contract");

    if (!contract.AreSaleDatesValid(dates))
        throw new ArgumentException("wrong dates", "dates");

    this.Dates = dates;
}

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

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

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

Но именно здесь могут помочь события домена. Если ваш Sale.ChangeDateметод опубликовал событие SaleDatesChangedи вы обрабатываете событие асинхронно в новой транзакции, тогда обработчик может проверить, действительны ли даты продажи для контракта.

Что будет дальше, зависит от ваших бизнес-требований - предупреждения для ручного просмотра или автоматического изменения даты продажи, чтобы соответствовать новым датам контракта?

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

Это «конечная последовательность» из требования DDD - ваше правило, что все продажи должны быть в сроки контракта, будет удовлетворено ... в конце концов.

И именно поэтому я сказал «оспаривать» требования - если было бы лучше позволить датам продаж выходить за рамки даты контракта в этих случаях и обрабатывать их надлежащим образом для бизнеса, тогда вы оспариваете свои требования и разработали более глубокое понимание домена.

C #, домен приводом дизайн,
Похожие вопросы