我对更复杂的聚合根结构和事件源有疑问。假设我有这段代码:
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;
}
}
如您所见,有一个聚合根 AggregateRoot
,它有 2 个子实体 entity1
和 entity2
。当 AR 接收命令(在本例中为简单的 int 命令
)时,它必须调用子实体上的一些方法,以便它们修改其状态。作为对此的 react ,它们触发 EntityUpdatedStateEvent
,该事件通过调用 when
方法自动应用于实体内部。此时应用事件可以保证当实体返回时,聚合根将具有正确的状态集,并且我将能够在聚合根中进行比较测试 if(entity1.state==1)
。根据此测试,我还更新其他实体状态。然后,这两个事件都会组合成 AggregateRootUpdatedState
事件,并保存到事件存储中。
现在,我的问题是 - 在 AR 中,第一次发生 AggregateRootUpdatedState
时,我根本不会调用 when
方法(仅在重新水合 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;
}
}
由于第一个示例对我的实际问题来说更加不真实,因此我用这个新的问题更新了问题,其中我实际上有 3 层嵌套,并且其中都有列表和一些附加逻辑。我基本上正在努力如何重构此方法(例如 SubEntityLevel1
、modify1
中的方法)以使用事件源(首先创建事件,然后应用它)。
更新2
好吧,似乎我有一些解决方案,但似乎比没有事件源要复杂得多......
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;
}
}
最佳答案
Both events are then composed into AggregateRootUpdatedState event that is preserved to event store.
在我看来,这就像一个反模式;您可以使其适用于模型的版本 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;
}
Is there some other way to pass events down the AR hierarchy?
一种方法是使用 Meyer 的 command query separation模式,并将创建事件的逻辑与更新实体状态的逻辑分开。 基本思想是,在查询中,您永远不会更改自己的状态(您可能会复制您的状态,然后更改该副本);在命令中,您将更改应用于本地状态。
如果您将状态视为不可变的值类型,那么这种模式确实自然而然地从中流出。
关于java - 事件溯源 - 将事件从 AR 传递到实体,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/42839254/