Skip to main content

Changing the mindset - part 2 / 4 - Modeling the Domain

In the previous part we created Anemic Entity Match which was connected to another Anemic Entity Team, and they both were ruled by a Service MatchService. We had a bunch of getters and setters in them, which could be executed from any place that we can imagine. We didn't have any behavior in our domain objects.

As I previously said, it can be a very good approach... Of course, if you are writing your code in C or Pascal... ;) After all - doesn't those, so called, Entities look like structures or records? And please do not tell me, that getters and setters are encapsulating the state...

If we consider ourselves to be Object Oriented Programmers, than we have to start thinking differently. It is not enough to make our objects out of data. We have to teach them how to act.

Where to start?

OK, so we want to start changing the way we think to more Object Oriented, but where should we begin? If we cannot just define the data and put it into structures, where to search for the clues?

The answer is closer than we think. We use it day by day. It is our language. And more specifically - so called Ubiquitous Language.


We have to examine the words that we are using to describe Processes that takes place in Modeled Domain. We should talk to people, who have the deepest knowledge about the Domain. They are called Domain Experts and they are extremely important for the Modeler.

For the purpose of this series, let's assume that our Domain Expert gave us the deepest knowledge about the Domain in the previous post. Let's take a closer look at it.



Words, words, words...

Here is the Domain description from the previous post with highlighted words that we will use to model our classes.
We have two teams that will play the match. We don’t know the date of the match at the moment of defining it, but we know which teams will play against each other. We can later setup the match date, but it can be done only until the match is finished. We can once finish the match with a score, but it cannot be finished before its start.

The end user wants to see the match: team names that play, match date if is set, match finish date and score if match was finished.
As you can see I highlighted mostly nouns and verbs. As you may guess, I will use nouns to create classes, and verbs to create methods. Of course, it doesn't have to be always like that. Sometimes from verbs, or adjectives, or other parts of speech we can derive classes as well.

Since we are working on the match, I will use this word as a name of an Aggregate. The Root Entity of this Aggregate will be a class called Match. It will hold references to other objects aggregated in the match. It will also expose it's behavior to the outside world, but will not expose it's internals (at least not for modification at this point).

Match can be defined - created - and during this process we need to pass two team names, which will play against each other. We could leave them as simple String values, but we want to make things more explicit, so we will create a Value Object called Team. It will contain the team's name and nothing more.  Notice, that we don't need any id in this class. We care only about the value that this object contains - team's name. We don't want to be able to change team's name - that is why the class has to be immutable.

We can setup the match date, so we will create a method on Match that can do that. It may be done only until the match is finished, so we have to explicitly model the exceptional situation during this process.

In the end we can finish the match and it must be done with a score. So it looks like we have to create another Value Object for holding the score. It will be a parameter to a method that can finish the match. However this method may not be executed if the match end date was before the start date. Also, we cannot finish the match more than once.

Teach them how to act!

In the previous part we had this Anemic Model, but we want to change it to match closer what our Domain Experts say.

The first step is to create classes for Team and Score Value Objects. We may end with something like this:

@Embeddable 
public class Team {
  private String id;

  // constructor + getter
}

@Embeddable
public class Score {
  @Basic
  private int homeGoals;

  @Basic
  private int awayGoals; 

  // constructor + getters 
}

Now take a look at the Match class. Since it is an Aggregate Root, it will have own lifecycle, own unique id, and own behavior exposed the world. This behavior will depict the Transaction boundaries. That means, we should get rid of setters, because they give the ability to change the state outside of the Transaction.

Since Score, Team and Date classes are immutable, we can leave getters to those fields (at least for now) - after all we need to generate the View somehow, right?

@Entity
@Table(name=”matches”)
public class Match extends AggregateRoot {
  @Id
  private UUID id;
  @Basic
  private Date matchDate
  @Basic
  private Date finishDate;
 
  private Team homeTeam; 
  private Team awayTeam;
  private Score score; 

  public Match(UUID id, Team homeTeam, Team awayTeam) {
    this.id = id;
    this.homeTeam = homeTeam;
    this.awayTeam = awayTeam; 
  }
  
  public setupMatchDate(Date matchDate) throws MatchAlreadyFinishedException {
    if (finishDate != null) {
      throw new MatchAlreadyFinishedException();
    }
    this.matchDate = matchDate; 
  }
  
  public finishWithScore(Score score, Date finishDate)
      throws MatchFinishedBeforeStartException, MatchAlreadyFinishedExcepion {

    if (matchDate == null || finishDate.before(matchDate)) {
      throw new MatchFinishedBeforeStartException();
    }
    if (this.finishDate != null) {
      throw new MatchAlreadyFinishedException();
    }

    this.finishDate = finishDate;
    this.score = score; 
  }

  // getters

}

Notice that we also got rid of the Service. Data and behavior are in one place - in the Domain Objects. We have no longer dummy data structures, but actually Object Oriented Model.

Those getters…

If at this point you start to consider exposing getters as a code smell, then congratulations - you are on the right track! After all, if the model is more complicated, and we have more Entities in the Aggregate, exposing them through getters may lead to changing them outside of the root and Transaction boundaries. And this may lead to real data corruption.

From the other side, we need to generate the View, and we cannot do this without accessing the data.

Try to figure out, how would you deal with this problem. Any ideas? ;)



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