2017-03-16 2 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; 
    } 
} 

는, 하나의 집계 루트이 하위 기관 entity1entity2을 가지고 AggregateRoot이 : 나는이 코드 조각이 있다고 할 수 있습니다. AR이 명령 (이 경우 단순한 int command)을 받으면 상태를 수정하기 위해 하위 엔티티의 일부 메소드를 호출해야합니다. 이에 대한 반작용으로 when 메서드를 호출하여 엔티티 내부에 자동으로 적용되는 EntityUpdatedStateEvent을 발생시킵니다. 이 시점에 이벤트를 적용하면 엔티티가 반환 될 때 루트의 집계가 올바른 상태로 유지되며 집계 루트에서 비교 테스트 if(entity1.state==1)을 수행 할 수 있습니다. 이 테스트에 따라 다른 엔티티 상태도 업데이트합니다. 두 이벤트 모두 이벤트 저장 영역에 보존되는 AggregateRootUpdatedState 이벤트로 구성됩니다.

지금 내 질문은 - AR에서 나는 처음으로 메서드를 호출하지 않습니다. AggregateRootUpdatedState (AR 재 수화에만 해당)이 발생합니다. AR 주소도 when 메서드를 호출하여 수정해야한다는 것을 알았 기 때문에 올바른 접근 방법입니까?

이벤트를 AR 계층 구조로 전달하는 다른 방법이 있습니까? 1 예는 내 진짜 문제에 더 비현실적 이었기 때문에

UPDATE

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; 
    } 
} 

, 나는 실제로 그들과 몇 가지 추가의 모두에서 목록의 중첩과 3 개 수준이이 새로운 하나를 업데이트 질문 논리. 나는 기본적으로이 메소드 (예 : SubEntityLevel1, modify1의 메소드)를 리팩터링하여 이벤트 소싱 (먼저 이벤트를 작성한 후 적용)에 어려움을 겪고있다. 나는 몇 가지 해결책을 가지고 있지만, 장소에 소싱 이벤트가없는 것보다 훨씬 더 복잡한 것처럼

2

OK UPDATE, ... 두 이벤트는 다음 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; 
} 

아칸소 계층 구조 아래로 이벤트를 전달하는 다른 방법이 있습니까?

한 가지 방법은 Meyer의 command query separation 패턴을 사용하고 엔티티 상태를 업데이트하는 논리에서 이벤트를 생성하는 논리를 분리하는 것입니다. 기본 개념은 쿼리에서 자신의 상태를 변경하지 않을 것입니다 (상태 복사본을 만들어 복사본을 변경할 수 있음). 명령에서 변경 사항을 해당 지역의 주에 적용합니다.

상태를 변경할 수없는 값 유형으로 생각하면이 패턴은 자연스럽게 자연스럽게 흐릅니다.

+0

응답 해 주셔서 감사합니다. 내 문제를 좀 더 현실적으로 반영 했으므로 업데이트 된 질문을 확인해주십시오. 나는 아마도이 CreatedEvent를 가져야한다는 것에 동의한다. (하지만 위에 언급 된 새로운 질문에서는 이후에 그것을 참조하는 방법을 모른다.) 또한, 첫 번째 코드 스 니펫에서 답한대로 - 결국 집계 루트는 처음 실행할 때 이벤트를 적용 할 필요가 없습니다. 다시 수화 할 때만 가능합니다. 이벤트 소싱과 관련하여 Meyer의 패턴에 대한 예가 있습니까? – bojanv55

+0

그러나 일반적인 힌트로 살펴 보겠습니다. DDD에 관해 질문 할 때마다 실제 문제에 대해 가능한 한 조심해야합니다. 일반적으로 일반적인 제약 조건을 가진 도메인은 지능적으로 모델을 작성하는 것이 어렵습니다. – VoiceOfUnreason

관련 문제