Jan
30

How Anaemic Domain Models Cause Bad Software

Using an anaemic domain model is often considered an anti-pattern.  The reason for this is that it encourages coders to duplicate code needlessly.  This is going to be a fairly short (and fairly trivial) post explaining one of the mechanisms by which this occurs (with an example).  The mechanism can be avoided with careful planning and strict coding discipline but it is quite a lot easier to apply good encapsulation.  The difficulty with avoiding the pit falls of an anaemic domain increases exponentially as more team members work on the project.
None of this will be new for anybody with a moderate understanding of OOP, but it is interesting to see how a small number of fairly innocent steps can lead to a real mess.

Step 1: Anaemic entity is written

At some point in the design of the software it is decided to implement certain pieces of logic for a domain entity outside of that domain entity.  This may be as a result of an explicit design decision or, more probably, it comes from the fact that certain pieces of logic just cannot be implemented in the domain entity because it is a persistent class and has no internal references to external services.  Adding an external service (dependency) to the entity would make persisting and restoring that entity to and from a database (respectively) complicated.

public class User {
   private final String name;
   private final String emailAddress;

   public User(final String name, final String emailAddress) {
      this.name=name;
      this.emailAddress=emailAddress;
   }

   public String getName() {
      return this.name;
   }

   public String getEmailAddress() {
      return this.emailAddress;
   }
}

Step 2: Logic for entity is implemented in an external class

A developer on the team decides that they need a method that operates on the entity.  This method should probably go inside the User object (in our example), but the method requires the use of an external service that the User class does not know about.  The logic/implementation for this method is implemented in a “helper” class or a service class that in some way supports the main entity.  The helper class contains no data or its own at this point and merely gets some data from the entity, or changes the state in some way.

public class UserReminderService {
   private IMailService mailService;
   private IMessageGeneratorService messageGeneratorService;

   public void sendReminderMessage(final IUser user) {
      String reminderMessage = this.messageGeneratorService.generateReminderMessage(user.getName);
      this.mailService.sendMessage(user.getEmailAddress(), reminderMessage);
   }

   ...
}

This would not have been implemented in the User entity because we would not have access to the mail service or the message generator.  So far this is not looking too bad (we have quite nicely encapsulated the message creation and the mail sending process), but this is just the beginning of the process of rot that will shortly begin to plague this unwary team of developers…..

What has gone wrong?

The UserReminderService is a nosy class (it occupies itself too much with the activities of another class).  The activity of creating a message and having it send for delivery (although only 2 lines of code) are probably the business of the User class itself.

Step 3: Duplicate/similar code is created

In the meantime, another developer has programmed a brand new component, which also uses the User entity.  The new service is to determine if a newly signed up user is a genuine user and not a spam bot:

public class SignupVerificationService {
    private IMailService mailService;
    private IMessageGeneratorService messageGeneratorService;

    public void sendVerificationEmail(final IUser user) {
      String verificationMessage = this.messageGeneratorService.generateVerificationMessage(user.getName);
      this.mailService.sendMessage(user.getEmailAddress(), reminderMessage);
   }
}

The developer may even see that this method looks remarkably similar to the sendReminderMessage method in the UserReminderService. In this case the developer think that he is being prudent by keeping “verification” based code in a separate component.  It doesn’t seem necessary to reuse the previous implementation because it is only two lines of code.

What has gone wrong?

These two methods look very similar, but they are different enough that the developer decided that they were different activities. There is some feeling of redundancy, but no major problems as yet.

Step 4: Logic changes

Simple bits of code often do not stay simple for very long.  By the end of the iteration our developer has added some more complexity to the sendReminderMessage method (preformatting the user name and validating the email address):

   public void sendReminderMessage(final IUser user) {
      String formattedUserName = formatUserNameForMessage(user.getName());
      String reminderMessage = this.messageGeneratorService.generateReminderMessage(formattedUserName);
      if (isEmailAddressValid(user.getEmailAddress()) {
         this.mailService.sendMessage(user.getEmailAddress(), reminderMessage);
      }
   }

   public boolean isEmailAddressValid(final String emailAddress) {
      return emailAddress.contains('@');
   }

   public String formatUserNameForMessage(final String userName) {
      return userName.toUpperCase();
   }

We now have a new version of the sendReminderMessage method (albeit with a very naive validation system) that now differs quite considerably from the (once similar) UserReminderService.

What has gone wrong?

The process employed to send a message to a User has changed (to require validation).  Because that process is not contained within the User class itself we find that we must track down all instances of that process in all its various forms and update them.  Assuming that we do notice that the SignupVerificationService also requires validation and we add it, we still need a way to reuse the validation code.  In the case of validation we could perhaps encapsulate the method into the mailService, but what of the other logic, like the user name formatting, which has built up in the various helper/service classes.  Some of that code is potentially required by multiple service classes and some may be quite specific to that one service.

Step 5: Disaster

Code that cannot be easily shoveled into one of the external dependencies (i.e. the mail service) will somehow have to be shared by the service classes.  It may seem like a good idea to put those methods into a super class in order for the two services to share them.  We then see class hierarchies forming that have no bearing on the original domain metaphor.  Instead of having a User and a Message and a MailService, we end up with a menagerie of strange creatures such as AbstractUserService, UserValidationService, UserReminderService and so on.  Quickly it becomes very hard to know where a new piece of code really belongs and if any methods required to write that new code will be in a location that affords it being (re)used.

       AbstractUserService
             /\
             |
             |
     ------------------------------------
    |                                  |
UserValidationService       UserReminderService

In the mean time another developer has programmed another service which sends messages to a Department entity (again using email addresses). The developer wants to use the email validation and name formatting in AbstractUserService, but his new code is for Departments, not Users, so yet another layer in the hierarchy appears: AbstractEntityService.

What has gone wrong?

We have lost control of our program structure and our development team is finding it impossible to write clean code. Our classes require many more public methods than necessary to accommodate the complex class relationships.

Summary
Keeping code structure clean and maintainable is certainly not impossible with an anaemic domain model. It does however become very easy indeed to maintain code and keep class interfaces minimalistic when we are able to apply a rich domain model.

public class User {
   //Dependencies
   private IMailService mailService;
   private IMessageService messageService;

   private final String name;
   private final String emailAddress;

  public User(final String name, final String emailAddress) {
      this.name=name;
      this.emailAddress=emailAddress;
   }

    public void sendReminderMessage() {
      deliverMessage( this.messageGeneratorService.generateReminderMessage(this.getName));
   }

   public void sendVerificationEmail() {
      deliverMessage(this.messageGeneratorService.generateVerificationMessage(this.getName));

   }

   private void deliverMessage(final String message) {
      if (isEmailAddressValid(user.getEmailAddress()) {
         this.mailService.sendMessage(user.getEmailAddress(), reminderMessage);
      }
   }

   public String getName() {
      return this.name;
   }
}

Notice that we no longer needed the getter for the email address field and, if you will excuse me playing the numbers game, we have increased the number of public methods on our User class by two rather than introducing (atleast) two extra classes. Using the method is more intuitive to read as we are executing the action directly on the applicable object rather than invoking a method on a contrived service object.

The MailService and MessageServices are allowed to stay in the system because they have clearly defined roles. Delivering mail is quite clearly an infrastructure issue and should be abstracted out of the domain model via an interface (IMailService). Generating messages is probably more debatable how that should be abstracted/encapsulated,but this post is already much longer than I anticipated.

I hope that you have enjoyed this post.

Tags: , , , , ,

5 Responses to “How Anaemic Domain Models Cause Bad Software”

  1. Eivind says:

    Great post! You are spot on pin-pointing the steps that, in my experience, cause bad code and in turn, bad software.

  2. Soner says:

    Hi James. First of all thanks for this nice post.
    Since a couple of time I am also very interested in DDD.
    I have read several articles and books about it but somehow I am still not confinced about the DDD approach.
    When I have read these several articles I could not see the common message about DDD.
    This makes me mixed up so that I could not find the disadvantages of the anemic model.
    In my opinion DDD is not just putting some logic into the domain layer.
    In your example you are focusing on this issue but DDD should be more.
    When you will check the DDD sample http://dddsample.sourceforge.net/ you will see that there
    is no big difference to the anemic model, except using DTOs as data containers or calling your DAOs now Repository.
    So what is DDD?
    Is it possible to define it?
    What is your opinion about the DDD sample?
    I am still in search to get enlighted by DDD.

  3. jim says:

    Hi Soner,
    Thanks for the comment.

    Firstly, this article is only very loosely related to DDD in that an anaemic domain model causes domain complexity to be increased (in most cases), which is precisely what DDD tries to avoid. Principally, this is an anaemic domain post, but I would like to reply to your questions and points in any case:

    DDD is not just putting some logic in the domain layer
    You are correct that DDD is more than just having a rich domain model, but it is one aspect that I wanted to write about :-) Again, the focus of the post is anaemic domain models, which are very much concerned with where the domain logic is implemented.

    So what is DDD?/Is it possible to define it?
    DDD espouses the construction of a solid domain model using good OOP principles in order to keep the complexity of a project under control. The use of factories, repositories and value objects etc. are recommended as a means of achieving this. The web page http://domaindrivendesign.org/ probably does a much better job at describing what it is than I can.

    …there is no big difference to the anaemic model…
    The domain objects in the DDD sample application do contain appropriate domain logic as far as I can see, which makes them non-anaemic. For example, the Delivery class (which is a member of the domain model) has methods “calculateEta” and “calculateNextExpectedActivity” which is certainly logic code. Domain logic does not necessarily require access to external services like in my example.

    ..or calling your DAOs now Repository.
    I don’t think that we have merely renamed our DAOs to repositories. “Repository” is a broader, more abstract term. A repository can be used for retrieving multiple entities participating in an aggregate, may support complex querying, or may not even be implemented using a database. A repository interface makes no assumptions about how persistence is achieved, whereas a DAO is generally very close to the database. The repository implementation may itself delegate persistence to one or more DAOs.

    I could not find the disadvantages of the anemic model.
    An anaemic domain model is widely considered an anti-pattern, encouraging code duplication, and reducing code clarity.
    In an anaemic domain model we often have superfluous service objects, where as DDD suggests only to create such objects when that domain logic does not naturally fit into one of the existing domain objects. This makes the code easier to understand.

    What is your opinion about the DDD sample?
    Thanks for mentioning this. I knew that a sample application was available, and have tried several times to download it in the past, only to find that the files were not available. I’m going to take a look at this over the weekend.

    Finally, I am by no means an expert on Domain Driven Design. I’m simply trying it out on one of my own projects and writing about my experiences, which is why I appreciate getting comments and hearing other peoples ideas opinions so much :-) If you are not convinced about the DDD approach to developing software, try doing what I’m currently doing, i.e. try it out.

  4. omar says:

    Great post! I am new to DDD so excuse me if my question’s answer is obvious. How do you load a User object by Id from the database (via Hibernate, for example) in the example above? In the anemic domain model world, there would be a finder method on the UserService to do so (which delegates to a UserDao). Would you do the same in the DDD world? If so, it means your controller (assuming a web app environment) would need to inject the UserService as well. Does that break DDD?

    Many thanks.

  5. jim says:

    Hi Omar,

    With regards to DDD, the interface for the UserRepository certainly lies in the domain. This means that you can use it directly without violating any of the principles of DDD. As long as your controller doesn’t have a dependency on the implementation of UserRepository you’re fine.

    It is certainly not required to use a Rich Domain in DDD, but it is my opinion that they work very nicely together.

    Personally I would avoid injecting domain services directly into the controller. If I’m fetching an domain entity it is usually to perform some sort of application logic upon it. In this case the code belongs to the “Application” layer (between the “User Interface” and “Domain” layers).

    In the DDD world there are entities that operate as “aggregate roots” and others that “belong to” aggregates. This may be a case where you would not want an entity being loaded directly using the repository (although this is a design preference rather than a DDD principle).

    An example of this would be as follows:

    User is an aggregate root entity and the UserInfo entity belongs to the User aggregate. In this scenario it would not be possible to access a UserInfo entity without going through the User entity. Here it might be appropriate to only allow loading UserInfo entities using a method on the User entity (which implicitly means the User aggregate root is responsible for handing out references to elements inside its aggregate).

    e.g. User.findUserInfo(..)

    Doe that answer your question?
    Thanks for the comment!