The Missing Layer

Over the years, we saw many practices when developing applications, especially when using patterns such as MVC. When using this pattern, the model and its role in an application was unclear and misunderstood. Also, we saw and we will see tutorials showing the model, full of ORM annotations, used in a shared way across multiple layers of a project.

This confusion comes in three forms when writing code:

Picking one of these options is problematic. None of these options are meaningful, yet the trap that most developers fall in is choosing the the first form: controllers hold the logic.

Let’s look at this controller:

public class IssueController {

  ...

  public List<IssueEntity> getAllIssues = (request, response) -> {
    return issueDao.getAllIssues()
  };

  public IssueEntity postNewIssue = (request, response) -> {
    if (isLoggedIn(request, respons)) {
      String summary = getQuerySummary(request);
      String author = getSessionCurrentUsername(request); // :o
      String description = getQueryDescription(request);
      IssueEntity issueEntity = new IssueEntity(summary, author, description);
      issueDao.insertIssue(issueEntity);
      return issue;
    }
    return null;
  };

  ...
      
  private boolean isLoggedIn = (request, response) -> {
    return logInController.ensureUserIsLoggedIn(request, response);
  }

  private String getSessionCurrentUsername = (request) -> {
    ...   
  }
}

This code is my code, which I wrote in the past. Unfortunately, there are several drawbacks and we can use it here to exhibit the problem in MVC:

We need to solve these problems elegantly.

You may ask how? The answer is: Extract the infrastructural concerns.

You may ask again ask how you would design the business logic layer? The keyword here is the business logic layer, BLL.

To answer this question, let’s consider MVC again in our case.

C is the IssueController, and M is the IssueEntity. We omit V right now as we return only JSON.

To enhance this code we need to find an equilibrium between C and M of M-C-V (or MVC, also ordered).

Let’s call it the missing layer of M-x-C-V.

That mystery layer x contains the heart of our software.

The missing layer in MVC is the domain model. The model that contains the business rules of our domain. And it’s what we should spend most of our time, thinking about how to craft it effectively.

M-C-V becomes RM-C-V, where RM stands for Rich Model.

And why do we change M to RM? What is wrong with M?

M is diagnosed with “oo-anemia”. It lacks enough of behavior and is used as a sate holder in an OOP world.

M is known as Anemic Domain Model.

So What is an Anemic Domain Model?

A class with state exposed by getters and setters is an Anemic Model. An anemic model is often used as DTO object - a class with private fields and setters and getters.

Compared to the anemic model, a rich domain model does not provide the ability to modify fields externally. There are no setter methods. Variables have private access; they are set in a private constructor. Custom methods need to be created and used to update an object’s state based on a set of rules.

Moreover, factory pattern can be used to create new objects. Additionally, we need some getters to extract some information.

Now, you ask what should look like a controller in this case?

The controller need to become stateless and provide its service by using the Rich Model methods. And it will be more useful when the workflow doesn’t fit the current model. Except that, it must do what is asked by the model methods.

The most important thing about this approach is that the rich domain object provides business methods, exposing the model’s behavior and not just its state (as for standard models with getters/setters).