Представьте, что у нас есть Агрегат, у которого есть жизненный цикл, так что он может изменить свое поведение в течение своей жизни.В течение первой части своей жизни он может делать некоторые вещи, а во второй части он может делать другие вещи.
Я хотел бы услышать мнения о том, как мы должны ограничивать то, что агрегат может делать с каждымфаза.Чтобы сделать его немного более осязаемым, давайте возьмем финансовую сделку в качестве совокупного примера.
- Трейдер создает сделку , информирующую о контракте и его цене.
- Менеджер по рискам проверяет сделку, указывая причину этого.
- BackOffice может отправить сделку в бухгалтерскую книгу, предоставив учетную информацию.
- После отправки сделки учетная информацияникогда не может быть изменено.
Торговля явно имеет 3 различных этапа, которые я назову Типизированный , Подтвержден и Отправлен
Моя первая мысль - загрязнить совокупность InvalidOperationExceptions
, что мне действительно не нравится:
public class Trade
{
private enum State { Typed, Validated, Submited }
private State _state = State.Typed;
public Guid Id { get; }
public Contract Contract { get; }
public decimal Price { get; }
public Trade (Guid id, Contract contract, decimal price) { ... }
private string _validationReason = null;
private AccountingInformation _accInfo = null;
public void Validate(string reason) {
if (_state != State.Typed)
throw new InvalidOperationException (..)
...
_validationReason = reason;
_state = State.Validated;
}
public string GetValidationReason() {
if (_state == State.Typed)
throw new InvalidOperationException (..)
return _validationReason;
}
public void SubmitToLedger(AccountingInformation info) {
if ((_state != State.Validated))
throw new InvalidOperationException (..)
...
}
public AccountingInfo GetAccountingInfo() { .. }
}
Я могу сделать что-то вроде Возможно шаблон , чтобы избежать исключений для Get...
методов.Но это не сработало бы для методов поведения (Validate
, SubmitToLedger
и т. Д.)
Как ни странно, если бы я работал над функциональным языком (таким как F #), я бы, вероятно, создал бы другойвведите для каждого состояния.
type TypedTrade = { Id : Guid; Contract: Contract; Price : decimal }
type ValidatedTrade = { Id : Guid;
Contract: Contract;
Price : decimal;
ValidationReason : string}
type SubmittedTrade = { Id : Guid;
Contract: Contract;
Price : decimal;
ValidationReason : string;
AccInfo : AccountingInfo }
// TypedTrade -> string -> ValidatedTrade
let validateTrade typedTrade reason =
...
{ Id = typedTrade.Id; Contract = typedTrade.Contract;
Price = typedTrade.Price; Reason = reason }
// ValidatedTrade -> AccountingInfo -> SubmittedTrade
let submitTrade validatedTrade accInfo =
...
{ Id = validatedTrade.Id;
Contract = validatedTrade.Contract;
Price = validatedTrade.Price;
Reason = validatedTrad.Reason;
AccInfo = accInfo }
И проблема изящно исчезнет.Но чтобы сделать это в ОО, я должен был бы сделать свой агрегат неизменным и, возможно, создать какую-то иерархию (в которой мне пришлось бы скрывать базовые методы! Ой!).
Я просто хотел высказать мнение очто вы, ребята, делаете в этих ситуациях, и если есть лучший способ.