DDD CQRS EventSourcing example source: football Match

 

  Classic programming mode such as JavaEE or spring+hibernate or .NET only thinking about data not about business language. in classic mode the domain model is only a data package, not a object, domain mode is kidnapped by technology.

  In Eric Evans on How Technology Influences DDD, Eric Evans say:"But the objects have been crippled for a long time by being tied to heavy frameworks like J2EE, but also and maybe more insidiously to the necessity of mapping to relational databases. ".

  Now it is time to change our mindset, we must change this abnormal mode. Changing the mindset - more object-oriented view at the business domain modeling has given us a good answer. in this talk, show a complete business analysis and design process by a football match case. and now I accomplished the match sample with open source jdonframework. deploy myweb.war of this match sample to tomcat, you can see the run result.

  Match sample souce code : https://github.com/banq/jdonframework/tree/master/example/cqrs%2Bes/match

  Match is a aggregate root, its source:

/**
* aggregate root
*
* @author banq
*
*/
@Model
public class Match {

private String id;

private Date matchDate;

private Team teams[] = new Team[2];

private boolean finished;

@Inject
public EventSourcing es;

public void handle(MatchCreatedEvent matchCreatedEvent) {
this.id = matchCreatedEvent.getMatchId();
this.teams[0] = new Team(matchCreatedEvent.getMatchTeamName1());
this.teams[1] = new Team(matchCreatedEvent.getMatchTeamName2());
this.finished = false;
}

public void startMatch(Date matchDate) {
if (this.matchDate != null)
System.err.print("the match has started");
es.started(new MatchStartedEvent(this.id, matchDate));
}

public void handle(MatchStartedEvent event) {
this.matchDate = event.getMatchDate();
}

public void finishWithScore(Score score, Date matchDate) {
if (this.matchDate == null)
System.err.print("the match has not started");
if (finished)
System.err.print("the match has finished");
es.finished(new MatchFinishedEvent(this.id, matchDate, score.getHomeGoals(), score.getAwayGoals()));
}

public void handle(MatchFinishedEvent event) {
this.finished = true;
}

public String getId() {
return id;
}

public String getHomeTeamName() {
return teams[0].getName();
}

public String getAwayTeamName() {
return teams[1].getName();
}

public boolean isFinished() {
return finished;
}

}

 

  the source is different from the ppt in here:

 

@Inject
public EventSourcing es;

  EventSourcing class will be injected into Match by jdonframework. EventSourcing class code:

@Introduce("message")
public class EventSourcing {

   @Send("created")
   public DomainMessage created(MatchCreatedEvent matchCreatedEvent) {
    return new DomainMessage(matchCreatedEvent);
   }

   @Send("started")
   public DomainMessage started(MatchStartedEvent matchStartedEvent) {
   return new DomainMessage(matchStartedEvent);
   }

   @Send("finished")
   public DomainMessage finished(MatchFinishedEvent matchFinishedEvent) {
   return new DomainMessage(matchFinishedEvent);
   }
}

 

 
                        

 

@Introduce("message") will introduce async message into this class, when its methods such as "created" was called from Match aggregate root, a message will be send by "@Send("created")", so this class is a message publisher too.

a message named "created" will be send to its consumers/subsribers. MatchCreatedEventImpl is one of consumers:

 

@Consumer("created")
public class MatchCreatedEventImpl implements DomainEventHandler {

   private final MatchRepository matchRepository;

   public MatchCreatedEventImpl(MatchRepository matchRepository) {
   super();
   this.matchRepository = matchRepository;
   }

   @Override
   public void onEvent(EventDisruptor event, boolean arg1) throws Exception {
   MatchCreatedEvent matchCreatedEvent = (MatchCreatedEvent) event.getDomainMessage().getEventSource();

   Match match = new Match();
   match.handle(matchCreatedEvent);
   matchRepository.save(match);
   }

}

  in MatchCreatedEventImpl, a Match aggregate objects was created, and saved into repository.

  MatchCreatedSavemeEventBusSubscriber is another one of "created" message consumers :

@Consumer("created")
public class MatchCreatedSavemeEventBusSubscriber implements DomainEventHandler {

private EventBusHandler eventBusHandler;

public MatchCreatedSavemeEventBusSubscriber(MatchListQuery matchListQuery) {
this.eventBusHandler = new SavemeEventBusHandler(matchListQuery);
}

@Override
public void onEvent(EventDisruptor event, boolean arg1) throws Exception {
MatchCreatedEvent matchCreatedEvent = (MatchCreatedEvent) event.getDomainMessage().getEventSource();
// todo send to JMS Server or EventBus Server;
eventBusHandler.refresh(matchCreatedEvent.getMatchId());
}

}

 

 
                        

  MatchCreatedSavemeEventBusSubscriber: in a CQRS system, a notify message should be sent to Event bus, so that the query system will know the Match data has been modified, and then load the newest data. data consistent between different systems can refer CAP or BASE theory .

  execution order of many consuners is by alphabetical order of their class name.

  Other events : MatchStartedEvent.java MatchFinishedEvent.java are same as MatchCreatedEvent. these events can be saved into database, these events can be play back, so the match will be back at history point. no need transaction, no need any lock or mointor.

 

download all samples

JdonFramework DDD + CQRS + EventSourcing examples

CES:Context Event and State

DDD DCI and Domain Events example