Изменение поведения агрегата в течение его жизни - PullRequest
0 голосов
/ 25 мая 2018

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

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

  • Трейдер создает сделку , информирующую о контракте и его цене.
  • Менеджер по рискам проверяет сделку, указывая причину этого.
  • 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 }

И проблема изящно исчезнет.Но чтобы сделать это в ОО, я должен был бы сделать свой агрегат неизменным и, возможно, создать какую-то иерархию (в которой мне пришлось бы скрывать базовые методы! Ой!).

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

Ответы [ 2 ]

0 голосов
/ 30 мая 2018

Вы можете иметь один класс на штат вместо одного класса.См. Этот пост Грега Янга: http://codebetter.com/gregyoung/2010/03/09/state-pattern-misuse/

Обычная проблема с моделью состояния - это трение с проблемами постоянства и особенно с ORM.Вам решать, стоит ли забота о большей надежности и безопасности типов.

0 голосов
/ 25 мая 2018

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

public Interface ITrade
{
    Guid Id { get; }
    Contract Contract { get; }
    decimal Price { get; }
}

public class Trade : ITrade
{
    public Trade(Guid id, Contract contract, decimal price)
    {
        Id = id;
        Contract = contract;
        Price = price;
    }

    Guid Id { get; }
    Contract Contract { get; }
    decimal Price { get; }

    public ValidatedTrade Validate(string reason)
    {
        return new ValidatedTrade(this, reason);
    }
}

public class ValidatedTrade : ITrade
{
    private ITrade trade;
    private string validationReason;

    public ValidatedTrade(Trade trade, string validationReason)
    {
        this.trade = trade;
        this.validationReason = validationReason;
    }

    Guid Id { get { return trade.Id; } }
    Contract Contract { get { return trade.Contract ; } }
    decimal Price { get { return trade.Price ; } }

    public string GetValidationReason()
    {
        return validationReason;
    }

    public SubmittedTrade SubmitToLedger(AccountingInfo accountingInfo)
    {
        return new SubmittedTrade(this, accountingInfo);
    }
}

public class SubmittedTrade : ITrade
{
    private ITrade trade;
    private AccountingInfo accountingInfo;

    public SubmittedTrade(ValidatedTrade trade, AccountingInfo accountingInfo)
    {
        this.trade = trade;
        this.accountingInfo = accountingInfo;
    }

    Guid Id { get { return trade.Id; } }
    Contract Contract { get { return trade.Contract ; } }
    decimal Price { get { return trade.Price ; } }

    public AccountingInfo GetAccountingInfo() { .. }
}
...