Decorator pattern in a Spring Boot project

Anyone who has worked with Hibernate has already faced the LazyInitializationException. It occurs when trying to access an association that hasn’t been fetched along with an entity. To avoid this, the required association(s) must be fetched within the same database transaction.

Fetching several to-one associations can be achieved in one single JPQL query via several JOIN FETCH clauses or by defining an appropriate EntityGraph. For to-many associations neither of those two approaches is possible: Hibernate will throw a MultipleBagFetchException when trying to JOIN FETCH more than one to-many association on the same entity, unless the associations are modeled as Sets, which is often not recommendable (it might produce a huge cartesian product that would be much too detrimental for performance). Therefore the optimal solution consists on performing additional queries to retrieve the associated entities that are mapped as collections.

When an entity has several associations, any time this entity is retrieved from the database there are many different combinations of associations that could be fetched along, depending on our application’s use cases. We will show how this situation represents a good fit for the decorator design pattern. This post will illustrate a demo project in Spring Boot that uses the decorator pattern to initialize an entity’s associations “on demand”.

The Model

Suppose we have a Book entity that has:

  • a many-to-many association with an Author entity
  • a many-to-many association with an Award entity
  • a one-to-many association with a Review entity

The Book entity

The BookAuthor entity

The Author entity

The Review entity

The BookAward entity

The Award entity

The problem: which associations should I fetch?

Suppose we have a class that retrieves the Book entity by its primary key, implementing the following interface.

When retrieving the Book entity, we might need to initialize a different set of associations for each one of our use cases in the application. This could lead to a proliferation of methods in our service’s interface, like this:

This is clearly not sustainable, all those methods just for a simple retrieval by primary key! If we want to add further methods to the interface that retrieve books by other attributes (e.g. by book’s title or by author’s name), or if a new association to another entity is added, the things are likely to spin out of control. Our service’s interface could potentially end up having dozens or even hundreds of methods and thus become impossible to maintain.

The best thing to do is to separate the concern of retrieving the entity (by whatever criteria) from the concern of which associations we want to be initialized. Since each association is meant to be fetched via a separate query, and those queries can be executed in any order within the same database transaction, the decorator design pattern is a perfect fit for our goal.

The solution: applying the decorator design pattern

Several decorator classes will be created, each one of them will implement the BookRetrieveService, and will be responsible for fetching a specific association. The Book entity has three associated entities, then we will need three decorator classes.

Let’s suppose we want to retrieve our Book entity either by its primary key or by the primary key of any of its authors. Our BookRetrieveService will get much leaner as it will only have two methods: the first method will retrieve at most one entity (hence the Optional return type), while the latter might retrieve any number of entities (hence the List return type). We’ll also add to the BookRetrieveService a getter for the EntityManager and an accept() method to allow the service to be visited by a decorator class and return the decorated service. The purpose of these two additional methods will be clear later on.

We’ll also create the BookLazilyRetrieveService interface, this interface extends the BookRetrieveService interface, signaling that all of its methods merely retrieve Book entities without initializing any association.

The implementation is pretty straightforward:

We will create one decorator class for each association that we could possibly want to fetch along with the Book entity. The common superclass that will serve as basis will be the following abstract class:

The implementations of the concrete decorator classes will be rather simple as only three methods need to be implemented: the decorate() method returns an instance of the decorator class wrapping the instance of BookRetrieveService passed as argument, while the two overloaded fetchWithAssociation() methods retrieve the desired association for a given single Book entity and for a given List of Book entities

The decorator class responsible for fetching the associated Author entities:

The decorator class responsible for fetching the associated Award entities:

The decorator class responsible for fetching the associated Review entities:

Now if we inject our BookLazilyRetrieveService in a client class we might be tempted to use it like this

Unfortunately that would not work as expected, because the decorated service returned by the accept() method wouldn’t be a Spring managed bean. For this reason each query performed by a decorator class would take place in a separate transaction, and at the end of each transaction the retrieved associations would be evicted from the EntityManager. As a consequence, only the association retrieved by the decorator applied last will be initialized in the retrieved Book entity (in our example those would be the Review entities).

We then need to register the decorated service as a Spring managed bean before calling any method on it. If we do that, all the queries will be performed within the same transaction, leveraging the declarative transaction definition (notice the @Transactional annotation both in the BookLazilyRetrieveServiceImpl class and in the AbstractBookAssociationRetrieveDecorator class) and all the associations retrieved by the decorators will be loaded in the EntityManager. Then, once the transaction is committed, all those associations will be populated into the Book entity retrieved by the byId() method in our example.

Registering Spring beans at runtime

We have two options: one is registering (in a @Configuration class) all possible combinations of the decorated service, and retrieve at runtime the bean that matches the set of desired decorators. This would imply registering a potentially huge amount of beans and would not be very practical.

The preferrable option is to register a decorated service bean with the given set of decorators only when such bean is required within the application. During the application’s lifecycle the same combination of decorators is likely to be used many different times, hence we should register a decorated bean only if it is not already to be found in the ApplicationContext, otherwise we can just retrieve such bean from the ApplicationContext. Doing so will prevent the registration of a high amount of redundant service beans having the same decorators’ chain.

We need to create and implement the following interface, the implementing class will be responsible for the registration (if necessary) and the retrieval of decorated BookRetrieveService beans:

In order to implement the BookRetrieveServiceDecoratorCache we must add the method getDecoratorsChain() to the BookRetrieveService interface, which will return a Collection of all the class types to be found along the decorators’ chain. Needless to say, the innermost element of such chain will always be the bookLazilyRetrieveService bean.

Notice that the method returns a Collection and takes a Supplier as argument: we might want to get the decorators’ chain as a List or as a Set, so we let the client code choose the preferred implementation of Collection to be returned.

The getDecoratorsChain() method will be overridden in the AbstractBookAssociationRetrieveDecorator. We’ll also need to add a getter for the decorated inner BookRetrieveService instance:

The implementation of BookRetrieveServiceDecoratorCache‘s getOrRegister() method takes one argument of type BookRetrieveService. The method will search in the ApplicationContext for a bean of type BookRetrieveService having the same decorators set as the provided argument instance. If such a bean is found it will be returned, otherwise the argument instance of BookRetrieveService will be registered in the ApplicationContext as managed bean and then returned.

Notice that the decorator’s constructor takes one argument of type BookRetrieveService, which must be a Spring managed bean as well, and such argument will be retrieved during the registration process by calling the getOrRegister() method. Thus the registration of a decorator bean can lead to a recursive registration of all the necessary inner decorator beans along the chain that are not yet in the ApplicationContext.

The following is the implementation of BookRetrieveServiceDecoratorCache:

Now, to simplify the retrieval of decorated beans, we can use the builder pattern. It would be great if we could simply inject the bean of type BookLazilyRetrieveService in a class and get the decorated bean in a fluent, declarative way like this:

First let’s define the interface for our builder as a nested interface of the BookRetrieveService interface, which will become the following:

calling the method decoratedBy() on the instance of the bookLazilyRetrieveService bean will return an instance of the Builder, which will keep an inner status consisting of a reference to the bookLazilyRetrieveService bean itself, a reference to the bean of type BookRetrieveServiceDecoratorCache, and a Collection of concrete decorator classes to be applied, which will contain one single element, i.e. the argument passed to the decoratedBy() method.

Each successive call on the Builder‘s decoratedBy() method will add one further element to the Collection of decorator classes and return the same instance of Builder, thus allowing the method chaining in a fluent style. Finally, calling the Builder‘s get() method will trigger the following process:

  • the Collection of decorator classes will be converted in a Collection of decorator instances (they’ll be instantiated reflectively via a call to their no-args constructor)
  • the decorators will be recursively applied to the bookLazilyRetrieveService bean
  • the decorated bean will be passed to the instance of BookRetrieveServiceDecoratorCache, which will retrieve the decorated bean of type BookRetrieveService having the desired set of decorators, after registering it if necessary

Here’s the Builder’s implementation:

Testing time

Now we can see how our decorated service can be defined at runtime, and only the associations retrieved by the applied decorators will be initialized in our retrieved Book entity:

Evolving the interface

The benefits of the architecture described so far become clear when the interface evolves with the addition of new methods. Suppose we want to add the possibility to retrieve Book entities by publishing year, all we have to do is add the method to the BookRetrieveService interface, implement the retrieve logic in the class BookLazilyRetrieveServiceImpl, and then call the association retrieval in the AbstractBookAssociationRetrieveDecorator. The implementation of how the association is fetched from the database is delegated to the concrete decorator subclasses, which remain unaltered.

If a new entity (having a “belongs to” relationship with the Book entity) was added to the model, all we’d have to do is adding a new concrete implementation of AbstractBookAssociationRetrieveDecorator, all the other classes would not change, none of the already existing methods would need to be modified, and we would achieve full adherence to the open-closed principle of OOP. As already mentioned, the decorator pattern allows us to independently address the two concerns of retrieving an entity and retrieving its associations.

In the AbstractBookAssociationRetrieveDecorator, compare the newly added method byPublishingYear with the already existing method byAuthorId and you’ll realize how easy it is to maintain your project clean and organized.

Conclusion

We’ve seen how the decorator design pattern can be applied in Spring to determine at runtime which additional behaviour must be added to a given implementation of an interface. The key for the successful implementation of the decorator design pattern is retrieving the bean by the set of decorators that we want to be applied, and if no such bean is available we’ll register it in the application context and then return it.

The whole pattern requires a bit of architectural effort, but it will be worth it if there are many different combinations of behaviours that you might be required to use. Even if that’s not the case now, things might change in the future, additional requirements could pile up. That’s why investing some extra time now would pay off in the future with an easily evolvable application that requires little effort to maintain.

The example project with full documentation and test cases can be downloaded from GitLab.