Skip to main content

Changing the mindset - part 4 / 4 - Subtle difference

This is the last article out of the series about changing the mindset. At the beginning we took the Classic approach, and then we introduced three important concepts (in the order):
  1. Domain Driven Design (Modeling the Domain)
  2. Command Query Responsibility Segregation (Segregation of Responsibility)
  3. Event Sourcing (Segregation of Responsibility)
In the previous part we separated our Read and Write Models. We also applied Event Sourcing as a persistence mechanism for Aggregates. We also equipped the Aggregate with behavioral methods only, and got rid of getters.
Everything looks really nice. The Model is clean, verbose, and everything is explicit (as for such a small example). We have one data store for the Write side, and we don’t have to care about distributed transactions. We can even easily provide historical data.

However, if you paste the last piece of code from the previous part to your IDE, you will see that there are many warnings on the fields – "never read locally". You can ignore them and proceed, but you can stay for a moment and think why they appear.

The "aha" moment!



It dawned on me the one day when I was really sleepy. I was making some coffee, while my brain was in the nothing box. And somehow the idea came to my mind - why do I need all those fields?


It looks like we had them on the very beginning, when we were mapping them directly to SQL columns. We needed them to be persisted and we were loading them with each query, but their only purpose was to be shown. They were not used by any business logic. I shall repeat that: they were not used by any business logic...

It was not clear until now…

Actually we don’t need them in our model! We need to keep only the properties that will be used in making business decisions. The data that they are holding is stored – in serialized Events (and later in the Read Model), but we don’t need them in the Write Model any more.

So finally our code may look like this:

public class Match extends AggregateRoot {
  private UUID id;

  private Date matchDate;
  private boolean finished;

  public Match(UUID id, Team homeTeam, Team awayTeam) {
    apply(new MatchCreatedEvent(id, homeTeam.getId(), awayTeam.getId());
  }

  private void handle(MatchCreatedEvent event) {
    this.id = event.getId();
    this.finished = false; 
  }
  
  public setupMatchDate(Date matchDate) throws MatchAlreadyFinishedException {
    if (finished) {
      throw new MatchAlreadyFinishedException();
    }
    apply(new MatchDateSetUpEvent(id, matchDate);
  }

  private void handle(MatchDateSetUpEvent event) {
    this.matchDate = event.getMatchDate();
  }
  
  public finishWithScore(Score score, Date finishDate)
      throws MatchFinishedBeforeStartException, MatchAlreadyFinishedExcepion {
    if (matchDate == null || finishDate.before(matchDate)) {
      throw new MatchFinishedBeforeStartException();
    }
    if (finished) {
      throw new MatchAlreadyFinishedException();
    }
    apply(new MatchFinishedEvent(
      id, finshDate, score.getHomeGoals(), score.getAwayGoals());
  }

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

The Questions

So, how our mind has changed during introducing new concepts to our code?

We started with Classic Approach, when we modeled the simplest anemic classes and mapped them with Hibernate. We didn't care about the Language or any actions that can be taken on the objects. Actually what we created, was a Database Schema and we mapped it one-to-one to the Classes. Although I would call them Records (yup, like in Pascal). We cared about Nouns only, and at this moment we were not even aware of that at all. To create the model, we were asking mainly: "What?"

The next step we took was to take care of Verbs. We started to think what we can do with our model. We looked at the Language that is used in the Domain and explicitly modeled it. We started to use this Language as a part of the code itself. Objects started to expose behavior described by Domain Experts. In addition to "What?" we started to use "Why? What it does? What is the responsibility of this term?".

However, there were some aspects that we didn't like - we had leaky abstraction, since next to those behavioral methods we had internals-revealing getters. So we decided to separate Model to Read and Write. Thanks for that we were able to think about Model behavior and data presentation in other places.  We even separated data stores to optimize their schemas for their purposes. We introduced next questions: "What we want to show? Where we want to show it?".

Then we introduced the concept of an Event. The Write side was publishing them as a result of actions. So after each behavioral method we were able to say what kind of Events should be raised. We started to think more about processes. We started to think more about business questions: "What changes given behavior should imply? How to name them? How the business process looks like?".

We started to dig into the business Domain deeply and express it directly in the code. Without paper documentation (do I hear "Agile"?). After all we finished with explicitly defined Business Processes, and something more: cleaner code. Extremely cleaner!

Do You see it?

Of course there can be many other ways to model this simple Domain, but I wanted to show how the code is changed when introducing new concepts to it.

Take a step back dear Reader, and look at the code from previous parts. Compare each version. Can you see some more differences? ;)

And what do you think about this mindset change? Did you experience it yet? If yes, then in what way? In similar, or different order? Or maybe completely another way?

Or maybe you are still at the very first step and you start designing the new functionality of your highly-complicated-and-extremely-expensive-new-cool-application from designing some Pascal records?



Changing the mindset series:
  1. Changing the mindset - part 1 / 4 - Classic approach
  2. Changing the mindset - part 2 / 4 - Modeling the Domain
  3. Changing the mindset - part 3 / 4 - Segregation of Responsibility
  4. Changing the mindset - part 4 / 4 - Subtle difference

Comments

  1. Thanks this series has helped with my quest for learning CQRS and Domain Driven Design.

    ReplyDelete

Post a Comment