Java-based Configuration in Hexagonal Architecture

I spent a lot of time thinking about when Java-based configuration may come in handy. Annotations are comfortable – you annotate a class, and that is all. You do not have to remember about writing an additional method in some strange configuration class.

Let’s start with the basics. There are three most popular methods of beans configuration:

  1. XML based-configuration
  2. Java-based configuration
  3. Annotation-based configuration

The first one is pretty straightforward.  Let’s take a look at what is the difference between java-based and annotations-based configuration.

In the Java-based configuration, you have a configuration class annotated with @ConfigurationYou define a bean by implementing a method that returns the bean object. You are supposed to annotate it with the @Bean annotation.

In the annotation-based configuration, you do not use @Bean annotation anymore. You would choose one of the following: @Repository, @Service, @Controller, @RestController, or  @Component. Each of these annotations is a class-level annotation. It means that you are supposed to annotate your component/service/repository/controller class with the appropriate one.

One day I finally got when java-based configuration may save you some energy. 

Growing adapter

Imagine you have an app implemented with hexagonal architecture. You have an adapter that depends on many beans defined within one package. You annotated each of them with appropriate annotation. With the annotations-based configuration, it would look like this:

As you see, the adapter strongly relies on the Spring framework. It is spread all over it. In my feeling, the purpose of hexagonal architecture is to limit dependencies as much as possible. I would limit them on every level – communication, architecture,  or implementation.

Limit the framework

Let’s check how the same adapter would look like with the java-based configuration.

As you can see, there is only one class that depends on Spring. The other ones have no idea about the framework at all.

Why does it matter to me? Imagine that your adapter grows and has more responsibilities. Maybe it communicates with some external system. The communication itself, for some reason, gets complicated. You feel that it could be a great moment to externalize some parts of its logic. It could be a module, a service, or even a library. 

Externalize the Adapter

You decided to extract it. You do not want the new module to rely on Spring – there is no reason for that. It means that you somehow have to plug it into your adapter. What would be the easiest way? Well…with the java-based configuration. You create new objects of classes from the new module and plug them into Spring via @Configuration class.

This refactoring might look like a small change. However, instead of having Spring dependency spread over the adapter, you have it in one config class. “ok ok, but I introduced a new dependency on the external module”. You are right, you did. Keep in mind there is a difference between being dependent on something that you own and something that you are provided with.

Do you feel the difference?

Hide library internals

As you probably noted, there is one big problem with the example above. Having a config class forces you to have some knowledge on how to initiate the library’s components. Moreover, they have to be exposed to the external world.

How can you solve that issue? Well, even the well-known Factory Pattern will help you with internals encapsulation. You can create one factory class that does the setup and publishes a ready-to-use object. The object could be then registered as a bean.

If there are any internal changes in the library, the adapter stays untouched. If you decide to quit Spring, there is only one class to be cleaned.


Summing everything up, I think there are times when both java-based and annotation-based configurations are ok.  Would I go with the java-based module in a simple CRUD application? Rather not. It would be too overwhelming compared to the annotation-based configuration. It all depends on your architecture and requirements.

You may also like