"Layers are like onions, they make me cry"

One of my colleague once said: "Layers are like onions, they make me cry". Actually he said it many times and cried..

Layers are great, like onions, if used in correct place and in reasonable amounts. Idea of layers to insulate distant part from each others. Only neighboring layers know about each others. Often this goes to one direction only: the layer knows only the layer it is using. This keeps everything nice and de-coupled.

Unfortunately it sometimes feels like people only knows two concepts: layers and MVC. This means that in basic web-application there is presentation layer, "where the magic happens" -layer and database layer. The "magic" is basically what transforms the data from user interface to the database and back. This layer can be divided do application layer, business logic layer, business infrastructure layer and so on according to the mood of the divider (Multilayered architecture).

simple-layered-arcihtecture.png

So what is the problem?

It is not the layers, but how they come to existence and how they are implemented. Layers do provide nice features. For example layers are supposed to be changeable. This is profound feature of layers. If presentation layer is not properly de-coupled from the business logic layer, then it can be argued, if they are separated layers at all. This decoupling is where things start to fall apart. Developers know that de-coupling is bad, so they add some layers. User interface has its own object to represent the data, this object is separated from the business layer and objects that are there are different from the database layer. This way we can have at least three user-objects. Maybe more if there are different output formats and data is generated from annotated objects.

When the customer wants to have new thingy in the UI it could mean adding three fields to three different objects, adding column to some database table and updating the user interface code. Of course using good architecture and de-coupling enables one to change database product or the user interface layer with minimal effort. Just throw the new layer in place and we are good to go. We can even have multiple databases or user interfaces. There could be command line and web based user interfaces. Unfortunately customer seldom asks to change to new database. Most often customer requires new features of changing old ones. These requires adding and changing fields and functionality in many layers.

Because adding fields to objects and moving them from object to object gets boring after while developers start to add new frameworks to the soup. Dependency injection makes it easy to add new layers and mappers will move data from similar object to another similar object. These are nice techniques, but they are used to provide flexibility in the wrong place. Adding and changing features should be easy. Changing database could be easy, but it is not the first priority in most cases.

Classical example of layers is OSI-model. Every layer brings something new to the mix. Layers encode the same message in different forms better suited for the more rudimentary layer. The message itself basically remains the same. Normal web applications work differently. The message changes while traveling through the layers. The HTTP-request parsed to some values that are used to select operation that is done to some other values. Finally things are stored to database and some response is created. It might be that different layers do different operations and the combination of all these operations is the wanted result. The logic gets lost in the call stack.

And the solution is?

I tend to think the call stack have a vertical nature. It goes deeper and deeper. I have mental image about flipping the call stack sideways where everyone can see what is happening. The logic should be in its own nice box. Tilted architecture could look something like this.

turned-architecture1.png

Of course this is not implementable as such. It just fuels my thoughts. If objects flow from database to the user interface, there is not much need for business logic there. Of course when some operation is done (like methodZ) a lots of logic could be involved. Maybe doing something with the data and showing them could be separated.

turned-architecture2.png

This kind of division is not new idea. It is called command query responsibility segregation. This nicely solves the problem with view code. Views can just access database and show the content there. This does not mean using SQL-directly, but there is no need for layers of code that keeps data in correct state. Showing something is read-only operation the state is not going to change.

Unfortunately there is the command side, which alters state of the application. It has not changed a bit. How should business logic be written to keep the intention clean and without excess layers? The picture does not answer this question. In fact the picture above is basically the same than the first picture with the "evil" layers, is just sideways.

Problems that layers cause is that logic gets distributed to different layers. Maybe instead of that one method could contain high level view of actions needed during operation. To sort things out, lets write some code:

public void transferMoney(String sourceAccount, String targetAccount, BigDecimal amount){
   Transaction tx = database.starTransaction();
   try{
      Account acc1 = database.loadAccount(sourceAccount);
      Account acc2 = database.loadAccount(sourceAccount);
      if (acc1.getBalance().compareTo(amount) > 0) {
        acc1.setBalance(acc1.getBalance().sub(amount));
        acc2.setBalance(acc2.getBalance().add(amount));
      } else {
        // throw something
      }
    tx.commit()
   } finally {
     if (tx.isOpen){ tx.rollback(); }
   }
}

This could operate on raw entities coming from Hibernate or something. The main point is that all that is going to happen is there. There are no magic layer that will alter entities unmentioned in the bottom of some call stack. It opens the transaction, does its thing and returns. However the balance checking code does seem fishy. It is almost certain that same kind of code has to be written somewhere else. That code is naturally located in account object.

public void transferMoney(String sourceAccount, String targetAccount, BigDecimal amount){
   Transaction tx = database.startTransaction();
   try{
      Account acc1 = database.loadAccount(sourceAccount);
      Account acc2 = database.loadAccount(sourceAccount);
      acc1.withdraw(amount);
      acc2.deposit(amount);
      tx.commit()
   } finally {
     if (tx.isOpen){ tx.rollback(); }
   }
}

// And in Account
public void withdraw(BigDecimal amount) throws NotEnoughMoney {
    if (balance.compareTo(amount) < 0) {
       throw new NotEnoughMoney();
    }
    balance = balance.sub(amount);
}

public void deposit(BigDecimal amount){
    balance = balance.add(amount);
}

And like always I am about 20 year late with my thoughts. :)

There is Data, Context and Interaction paradigm which tries to solve business logic scattering around the domain model. My ramblings are heading that direction pretty fast. The DCI models roles as first class citizens. In this context source account has withdraw-method and target account has deposit-method. The code would look something like this.

public void transferMoney(String sourceAccount, String targetAccount, BigDecimal amount){
   Transaction tx = database.startTransaction();
   try{
      SourceAccount source = asRole(database.loadAccount(sourceAccount), SourceAccount.class);
      TargetAccount target = asRole(database.loadAccount(sourceAccount), TargetAccount.class);
      new TransferMoneyContext().transferMoney(source, target, amount);
      tx.commit()
   } finally {
     if (tx.isOpen){ tx.rollback(); }
   }
}

class TransferMoneyContext {
   public void transferMoney(SourceAccount source, TargetAccount target, BigDecimal amount){
      source.withdraw(amount);
      target.deposit(amount);
   }
}

interface SourceAccount {
   void withdraw(BigDecimal amount) throws NotEnoughMoney;
}
interface TargetAccount {
   void deposit(BigDecimal amount);
}

While the same functions than are in the "layers" exist in this model too, they are just organized differently. Because the operation explicitly defined using sequential operations, the data flow is easy to see. It is not hidden in deep call hierarchies spanning multiple layers. The traditional "layers" are called sequentially, not inside each others. Of course from high view the layers remain the same: user interface, business logic and persistence.

For me the biggest change is in the way how object responsibilities should be divided. Before I found it problematic to include logic in Hibernate entity classes. The logic might not get run at all because the entities basically had to be mapped to another objects. When roles come in to the play there is no need to map object to another, just extend the existing entity. With more traditional approach domain objects tend to grow big because every use case could require adding one or two methods there. With concept like role there is better place for this kind of methods.

Unfortunately Java does not work this way. One does not simply extend an object in flight. With Java 8 and a bit of a trickery one can implement traits (see here). There is also Apache Zest, but somehow it seems like overkill. At the moment I am tied to old legacy system that needs simplifying and adding another framework does not seem like going to right direction. By defining Account interface that entity would implement we can define wrapper class that implements SourceAccount.

class SourceAccountImpl implements SourceAccount, Account {

   Account account;
   public SourceAccountImpl(Account account){
      this.account = acc;
   }

   public void withdraw(BigDecimal amount) throws NotEnoughMoney {
      if (getBalance().compareTo(amount) < 0) {
         throw new NotEnoughMoney();
      }
      setBalance(getBalance().sub(amount));
  }

  // Forwarder methods to satisfy Account interface
  // ...
}

The downside is self schizophrenia, the account entity can not refer to the role objects method by using this. On the other hand there are no complex frameworks or magical reflection to hinder readability. This is just example of trade off one could do instead of simply hammering down the layers like everyone else.

Your point being?

Before thinking about the layers one should consider application itself. What it is supposed to do and how to express those things clearly to the reader? What parts are going to be under constant changes and how to keep those separated and small? The layers will come naturally from decoupled code with different responsibilities. It might be even necessary to name the layers and create some rules to govern them. But concentrate on the application (the business layer) first and other layers only if necessary.

There is entry in 8th Light blog about similar issues: The clean architecture by Uncle Bob.

Panu Wetterstrand