2017-03-16 9 views
0

より複雑な集約ルート構造とイベントソーシングに問題があります。私は、コードのこの部分を考えてみましょう:イベントソーシング - ARからentitesへのイベントの受け渡し

class AggregateRoot{ 
    Entity entity1; 
    Entity entity2; 

    //this is returned to service layer to be persisted to db in event stream 
    AggregateRootUpdatedState execCommand(int command){ 
     entity1 = new Entity(); 
     EntityUpdatedStateEvent event1 = this.entity1.changeState(command); 

     EntityUpdatedStateEvent event2 = null; 
     if(entity1.state==1) { //since we already set sub entity - we can check this 
      entity2 = new Entity(); 
      event2 = this.entity2.changeState(command); 
     } 

     AggregateRootUpdatedState parentEvent = new AggregateRootUpdatedState(event1, event2); 
     //when(parentEvent); //??? WE ALREADY CHANGED STATE IN TWO LINES ABOVE 
     return parentEvent; 
    } 

    void when(AggregateRootUpdatedState event){ //needed for re-hydrating the event state 
     entity1 = new Entity(); 
     entity1.when(event.event1); 
     if(event.event2!=null) { 
      entity2 = new Entity(); 
      entity2.when(event.event2); 
     } 
    } 
} 

class Entity{ 
    int state; 

    EntityUpdatedStateEvent changeState(int state){ 
     EntityUpdatedStateEvent event = new EntityUpdatedStateEvent(state); 
     when(event); 
     return event; 
    } 

    void when(EntityUpdatedStateEvent event){ 
     this.state = event.state; 
    } 
} 

class EntityUpdatedStateEvent{ 
    int state; 

    EntityUpdatedStateEvent(int state) { 
     this.state = state; 
    } 
} 

class AggregateRootUpdatedState{ 
    EntityUpdatedStateEvent event1; //here we are nesting events AR->Entity 
    EntityUpdatedStateEvent event2; 

    AggregateRootUpdatedState(EntityUpdatedStateEvent event1, EntityUpdatedStateEvent event2) { 
     this.event1 = event1; 
     this.event2 = event2; 
    } 
} 

あなたが見ることができるように、2サブエンティティentity1entity2を持って1つの集約ルートAggregateRootがあります。 ARがコマンド(この場合は単純なint command)を受け取ると、ARはサブエンティティのいくつかのメソッドを呼び出して、それらの状態を変更する必要があります。これに対する反応として、彼らはwhenメソッドを呼び出すことによって、エンティティの内部で自動的に適用されるEntityUpdatedStateEventを起動します。現時点でイベントを適用すると、エンティティが戻ったときに、集約ルートが正しい状態になり、総計ルートで比較テストif(entity1.state==1)を実行できるようになります。このテストによっては、他のエンティティの状態も更新します。両方のイベントは、イベントストアに保存されるAggregateRootUpdatedStateイベントに構成されます。

今、私の質問は - 私はwhenメソッドを初めて呼び出すことはありませんAggregateRootUpdatedState(ARの再水和時のみ)。 ARの状態もwhenメソッドを呼び出すことによって変更する必要があることがわかったので、これは正しいアプローチですか?

イベントをAR階層に渡す別の方法はありますか?第一の例では、私の本当の問題へのより多くの非現実的であったため

class AggregateRoot{ 
    List<SubEntityLevel1> subEntityLevel1s; 
    int rootNum; 

    void command(int x){ 
     rootNum = x*x; 
     for(int i=0; i<x; i++){ 
      SubEntityLevel1 subEntityLevel1 = new SubEntityLevel1(); 
      subEntityLevel1.modify1(i, rootNum); 
      subEntityLevel1s.add(subEntityLevel1); 
     } 
    } 

    void when(AggregateRootModifiedEvent event){ 
     //HOW TO REFACTOR METHOD ABOVE TO EVENT? 
    } 
} 

class SubEntityLevel1{ 
    int id; 
    List<SubEntityLevel2> subEntityLevel2s; 
    int sum = 0; 

    void modify1(int id, int rootNum){ 
     //HOW TO MAKE EVENT FROM THIS AND THEN APPLY IN WHEN() METHOD? 
     this.id = id; 
     this.sum = rootNum; 
     for(int i=0; i<id; i++){ 
      if(subEntityLevel2s.stream().noneMatch(se2 -> "0".equals(se2.id))) { 
       SubEntityLevel2 subEntityLevel2 = new SubEntityLevel2(); 
       subEntityLevel2.modify2(i); 
       subEntityLevel2s.add(subEntityLevel2); 
       sum++; 
      } 
     } 
    } 

    void when(SubEntityLevel1Modified event){ 
     this.id = event.id; 
     this.sum = event.sum; 
     for(SubEntityLevel2Modified subEvent : event.subEntity2Events){ 
      when(subEvent); 
     } 
    } 

    void when(SubEntityLevel2Modified event){ 
     SubEntityLevel2 subEntityLevel2 = new SubEntityLevel2(); 
     subEntityLevel2.when(event); 
     subEntityLevel2s.add(subEntityLevel2); 
    } 

    void when(SubEntityLevel2Created event){ 
     //?????? 
    } 
} 

class SubEntityLevel2{ 
    String id; 

    SubEntityLevel2Modified modify2(int x){ 
     SubEntityLevel2Modified event = new SubEntityLevel2Modified(String.valueOf(x)); 
     when(event); 
     return event; 
    } 

    void when(SubEntityLevel2Modified event){ 
     this.id = event.id; 
    } 
} 

//----- EVENTS 

class AggregateRootModifiedEvent{ 
    int rootNum; 
    List<SubEntityLevel1Modified> subEntity1Events; 

    public AggregateRootModifiedEvent(int rootNum, List<SubEntityLevel1Modified> subEntity1Events) { 
     this.rootNum = rootNum; 
     this.subEntity1Events = subEntity1Events; 
    } 
} 

class SubEntityLevel1Modified{ 
    int id; 
    List<SubEntityLevel2Modified> subEntity2Events; 
    int sum; 

    public SubEntityLevel1Modified(int id, List<SubEntityLevel2Modified> subEntity2Events, int sum) { 
     this.id = id; 
     this.subEntity2Events = subEntity2Events; 
     this.sum = sum; 
    } 
} 

class SubEntityLevel2Created{} 

class SubEntityLevel2Modified{ 
    String id; 

    SubEntityLevel2Modified(String id){ 
     this.id = id; 
    } 
} 

UPDATEは、私が実際にネストの3つのレベルを持っているこの新しいものとし、それらといくつかの追加の両方でリストに質問を更新しました論理。私は基本的に、このメソッドをリファクタリングする方法(例えば、SubEntityLevel1modify1のメソッド)を使って、イベントソースを使用してイベントを作成し、それを適用することに苦労しています。

UPDATE 2

OK、私はいくつかの解決策を持っているが、はるかに複雑な場所で調達イベントがない場合よりも思えるように両方のイベントは、その後AggregateRootUpdatedStateに構成されてい

class AggregateRoot{ 
    List<SubEntityLevel1> subEntityLevel1s = new ArrayList<>(); 
    int rootNum; 

    List<Object> command(int x){ 
     List<Object> tempEvents = new ArrayList<>(); 
     //rootNum = x*x; 
     int tempRootNum = x*x; 
     for(int i=0; i<x; i++){ 
      SubEntityLevel1 subEntityLevel1 = new SubEntityLevel1(); 
      List<Object> subEvents = subEntityLevel1.modify1(i, tempRootNum); 
      //subEntityLevel1s.add(subEntityLevel1); 
      SubEntityLevel1Added event = new SubEntityLevel1Added(subEvents); 
      when(event); 
      tempEvents.add(event); 
     } 
     AggregateRootModifiedEvent event = new AggregateRootModifiedEvent(tempRootNum); 
     when(event); 
     tempEvents.add(event); 
     return tempEvents; 
    } 

    void when(AggregateRootModifiedEvent event){ 
     this.rootNum = event.rootNum; 
    } 

    void when(SubEntityLevel1Added event){ 
     SubEntityLevel1 subEntityLevel1 = new SubEntityLevel1(); 
     for(Object subEvent : event.events){ 
      subEntityLevel1.when(subEvent); //list of SubEntityLevel2Added AND SubEntityLevel1Initialized 
     } 
     subEntityLevel1s.add(subEntityLevel1); 
    } 
} 

class SubEntityLevel1{ 
    int id; 
    List<SubEntityLevel2> subEntityLevel2s = new ArrayList<>(); 
    int sum = 0; 

    List<Object> modify1(int id, int rootNum){ 
     //this.id = id; 
     //this.sum = rootNum; 
     int tempSum = rootNum; 
     List<Object> tempEvents = new ArrayList<>(); 
     for(int i=0; i<id; i++){ 
      if(subEntityLevel2s.stream().noneMatch(se2 -> "0".equals(se2.id))) { 

       SubEntityLevel2 subEntityLevel2 = new SubEntityLevel2(); 
       SubEntityLevel2Initialized event = subEntityLevel2.initialize(i); 
       //subEntityLevel2s.add(subEntityLevel2); 
       SubEntityLevel2Added event2 = new SubEntityLevel2Added(event); 
       when(event2); 
       tempEvents.add(event2); 

       //sum++; 
       tempSum++; 
      } 
     } 
     SubEntityLevel1Initialized event3 = new SubEntityLevel1Initialized(id, tempSum); 
     when(event3); 
     tempEvents.add(event3); 
     return tempEvents; 
    } 

    void when(SubEntityLevel1Initialized event){ 
     this.id = event.id; 
     this.sum = event.sum; 
    } 

    void when(SubEntityLevel2Added event){ 
     SubEntityLevel2 subEntityLevel2 = new SubEntityLevel2(); 
     subEntityLevel2.when(event.event); 
     subEntityLevel2s.add(subEntityLevel2); 
    } 
} 

class SubEntityLevel2{ 
    String id; 

    SubEntityLevel2Initialized initialize(int x){ 
     SubEntityLevel2Initialized event = new SubEntityLevel2Initialized(String.valueOf(x)); 
     when(event); 
     return event; 
    } 

    void when(SubEntityLevel2Initialized event){ 
     this.id = event.id; 
    } 
} 

//----- EVENTS 

class AggregateRootModifiedEvent{ 
    int rootNum; 

    public AggregateRootModifiedEvent(int rootNum) { 
     this.rootNum = rootNum; 
    } 
} 

class SubEntityLevel1Added{ 
    List<Object> events; 

    public SubEntityLevel1Added(List<Object> events) { 
     this.events = events; 
    } 
} 

class SubEntityLevel1Initialized { 
    int id; 
    int sum; 

    public SubEntityLevel1Initialized(int id, int sum) { 
     this.id = id; 
     this.sum = sum; 
    } 
} 

class SubEntityLevel2Added{ 
    SubEntityLevel2Initialized event; 

    public SubEntityLevel2Added(SubEntityLevel2Initialized event) { 
     this.event = event; 
    } 
} 

class SubEntityLevel2Initialized { 
    String id; 

    SubEntityLevel2Initialized(String id){ 
     this.id = id; 
    } 
} 

答えて

1

...らしいですイベント・ストアに保存されるイベント。

これは私には反パターンのようです。バージョン1のモデルでも動作させることができますが、リファクタリングを開始したり、イベントに応答したりする場合は、余分な作業が必要になります。おそらく、単一のモノリシックイベントではなく、イベントのコレクションを返すという観点から考えるべきです。

あなたの集計コードは、注目すべきもう一つは、特にあなたが子イベントを作成された場合、この例では、あなたがイベントを発生させずにルートエンティティを変更されている、ということである

History execCommand(int command){ 
    entity1 = new Entity(); 
    EntityUpdatedStateEvent event1 = this.entity1.changeState(command); 

    EntityUpdatedStateEvent event2 = null; 
    if(entity1.state==1) { //since we already set sub entity - we can check this 
     entity2 = new Entity(); 
     event2 = this.entity2.changeState(command); 
    } 

    // ALL of the state changes have already happened, so no need to 
    // re-process the events. 
    return History.of(event1, event2) 
} 

ようになり

entity1 = new Entity(); 

エンティティのライフサイクルの開始がモデル内で明示的なものではないのはなぜですか?エンティティの参照(this.entity1、this.entity2)が常に同じ方法で割り当てられることを保証するイベントが必要です。

EntityOneCreatedEvent createEntityOne() { 
    EntityOneCreatedEvent e = new EntityOneCreatedEvent(); 
    when(e); 
    return e; 
} 

AR階層ダウンイベントを渡すためのいくつかの他の方法はありますか?

1つの方法は、Meyerのcommand query separationパターンを使用して、エンティティの状態を更新するロジックからイベントを作成するためのロジックを分離することです。 基本的な考え方は、クエリでは自分の状態を変更しないことです(状態のコピーを作成してコピーを変更する可能性があります)。コマンドでは、ローカルの状態に変更を適用します。

stateを不変の値型と考えると、このパターンは実際に自然に流れ出します。

+0

お返事ありがとうございます。更新された質問を確認してください。現時点で問題が現実的に反映されています。私はおそらくこのCreatedEventを持っている必要があることに同意します(ただし、上の新しい質問では後でそれを参照する方法はわかりません)。また、最初のコードスニペットで返事をしたように - 最後に集計ルートは、初めて実行するときにイベントを適用する必要はありません - 再水和するときだけです。イベントの調達に関するマイヤーのパターンの例はありますか? – bojanv55

+0

しかし、一般的なヒントとして、私は見ていきます.DDDについて質問するときはいつでも、実際の問題にできる限り近づけてください。ジェネリック制約を持つ一般的なドメインは、インテリジェントにモデル化するのが本当に難しいです。 – VoiceOfUnreason

関連する問題