Hexagonal architecture with Domain, Presenter & Entity segregation on Spring WebFlux

Aritra Das
Javarevisited
Published in
11 min readNov 19, 2020
Meme generated via https://imgflip.com/

Yes, that’s right, most of the architectures available today are similar in a way or another. Before you jump the gun and lecture me with “you don’t know anything”, let me explain why I said so.

First of all, what all of these architectures are mostly trying to achieve, is the Separation of Concerns. Now, this goes back to the age-old S.O.L.I.D principles, where the Single Responsibility Principle suggests that everyone should do one thing and do that perfectly, so that when you have to change something, it’s restricted to that component only, and it does not affect anything else in the system (There are numerous benefits which I will skip, to save time).

This applies to modern-day web-services as well. If you want to move from REST to gRPC, you should not have to touch your Database Access code, if you are adding some new validation rules, you should not have to change your controllers. The less stuff you have to change the lesser the chance of introducing a new bug, and if something breaks you know exactly where to look.

So with all of these architectures, you are basically layering your system such that

  1. Your core business logic does not depend upon anything outside its domain and provides a uniform DSL for the consumer code. Hence the consumer code is unhinged by any changes you make inside your core logic and vice versa.
  2. Your core logical code is easily testable since it is not dependent on any framework.
  3. Your core logic does not depend upon how you retrieve data, hence it would not matter if you choose to use DynamoDB instead of MongoDB tomorrow. It would not matter if you use stored procedures, database queries, or an ORM framework. For the core logic, the data access just becomes an abstraction.

Thus your business rules are protected, and susceptible to a lot less change. And be it Clean Architecture, Hexagonal Architecture, Onion Architecture, everyone is able to achieve this, it’s just that the layering strategy is different for all of them, and hence they look different.

So use any architecture you want, if your team is able to follow it consistently, then you are good. All architectures have some tradeoffs, which is fine, cause Software Design is all about tradeoffs 😛

Hexagonal Architechture

Even though I have declared that all architectures are the same, we are still going to learn about Hexagonal Architecture, since the title of the article requires me to do so 😝

Hexagonal Architecture, tries to isolate the core application logic from the infrastructure or purely technical code. So that we can swap out any of the infrastructures we require.

Also because of this very isolation, the effect of any change is restricted to that component only. And with the clean separation of different components, where everyone mostly depends on interfaces, we have nice abstraction built-in as well.

This is a Hexagonal structure, otherwise known as Ports and Adapters. Let’s understand what different players in the diagram mean.

1. Hexagon

The hexagon is the core of our application, which encapsulates all business intelligence, and hardcore functional logic. They are devoid of purely technical or infrastructural code. The hexagon is generally an abstraction of these three components described below.

  1. Domain: These are the core business objects, which encapsulate business data and business logic. They do not depend on any framework and do logical computation only. Most importantly they do not have any dependency.
  2. Use-Case: These are a collection of operations that are supported by the system. I like to think of them as orchestrators that contain mostly application business logic. They would mostly communicate with Domains, other use-cases, and Repository to complete a business operation.
  3. Ports: These are simple interfaces that live on the boundary of the hexagon. They enable communication/ data flow in and out of the hexagon. Anything outside the hexagon would have a dependency on an input port to call any of the methods of a use-case. Similarly, the use-case must depend upon an output port to access anything outside of the hexagon.

2. Adapters

Adapters are the classes outside the hexagon layer, that communicate with the hexagon to either drive the system or provide support to the hexagon. They are infrastructure code.

For example, we can have an HTTP adapter, which is nothing but a REST controller, which would call an input port to perform a business operation. Similarly, there can be a database adapter, which would implement an output port, and then the use-case can call that output port to push data into the database.

Dependency Structure

Here’s a simple diagram to depict the relations between different components in a hexagonal app.

  1. The REST Adapter has a dependency on the input port so that it can invoke operations inside the hexagon.
  2. The input port is an interface, which establishes the contract of the operations supported by the hexagon or the application.
  3. Use-case implements the input port, hence provides the implementation of all the operations supported. It also has a dependency on the output port, so that it can call the methods defined there.
  4. The output port is also an interface that sets up the contract for outbound operation from the hexagon.
  5. The database adapter provides the implementation of the methods in the output port. Which mean they actually handle all the database related code.

Have you noticed that the hexagon does not depend upon anything else, but other parts of the system depend on the hexagon, to perform an operation? This is because the hexagon defines the ports, which are basically contracts for all your operations, and in turn controlling the system. Your adapters become simple infrastructure-related code, without any business logic, and all your business intelligence is now abstracted out by the hexagon. This sets up a very nice boundary and abstraction. And now change in one part of the system does not affect the other parts anymore, Separation of Concerns yaaayyyyy!

Even though the use-case needs the database methods, it depends on the port only, which is inside the hexagon, and not on any repository/ DAO code. The adapter implements that port, inverting the dependency from the adapter to the hexagon. Dependency inversion, yaaayyyyy, wonderful stuff that interfaces can do.

At this point, I hope you have understood the basics of hexagonal architecture, and what we are trying to achieve.

Domain, Presenter & Entity

Meme generated via https://imgflip.com/

For those of you who are already fed up with so much theory and wondering where is some code, hang in there mate, I promise I would show some code in a while.

Now that you have understood hexagonal architecture, you might be wondering what animal is this Domain, Presenter, Entity segregation again. See hexagonal architecture is just a style, you can still built on top of it when you actually design your application. And thus domain, presenter, and entity. Let me start by explaining what are they and why would we need them.

  1. Domain: This is still the same domain which I have explained above. It is the code representation of the real-life domain object. For example, if your system deals with Product, you should at least have one domain class called Product.
  2. Presenter: This the object which represents the Domain in a format that your consumer can understand, and most of the time should only hold data and not any behavior. For example, if you want to send a product into a downstream system, and to make that entire operation idempotent you need to add a request-id. Now you can create a new object which has whatever domain object has + request-id, and this becomes your presenter. Your UI might need data in GeoJSON format but some other upstream API might need data in WKT format, you again need two different presenters here to represent your response.
  3. Entity: Entities are what I call the representation of your data in the database. What I mean is you can represent the same data in a different way in the database, one can be more optimal than others for the database queries. So we create another class that would resemble the table (for SQL DB) or document (for a document DB) so that our database operations are optimized.

Did you see what I did there? More sepratiaon of concerns. Your domain object takes care of all the business rules, your presenter takes care of the upstream system’s needs, your entity takes care of the database needs.

So mixing that with our hexagonal structure, this is how the data flow looks

Simple, right? Don’t worry if you do not understand completely, with the code example you would be able to relate.

That was all the concepts, now we can jump into some code. Another yaaayyy moment 😛. I will code in Java + Spring WebFlux in the next section. Don’t worry if you know neither Java nor Spring, the implementation would be extremely simplistic and easy to follow.

Hexagon on Spring WebFlux

We are going to build a very simple REST app for “store” management. We should be able to store and retrieve data and run different queries. The final solution can be found here.

Let’s say the requirements for our current task is.

  1. You should be able to save a store into the DB.
  2. We also have to maintain a count of how many stores are there in a particular zip-code for future operations. Hence when we save a store into the DB we have increment the count.

I will apply all the principles that we learned above in the solution, let’s see how things shape up.

Building the hexagon

This how my hexagon would look upon completion

The application is the base package for the hexagon, everything else is sort of self-explanatory.

Input adapter

We will start by creating the input adapter first. It should have only one method which lets us save a store.

Don’t get baffled by seeing Mono, it’s a reactive stream publisher that can publish at most 1 event. And this event would contain simple data. It’s not necessary to understand reactive programming to follow along. You can read more about Java reactive programming here https://projectreactor.io/

Domain

This core Store domain model

It also has a subdomain Location which looks like this

We are trying to model our domain as close as we can to the physical entities, for better readability and simplicity.

Service

Now let’s write the implementation for the Use case, which I am calling service, but you can call it something else.

It checks if the domain object is valid, if yes then makes some database changes else throws an error.

Couple of things to notice

  1. The use-case implementation uses different ports to interact with the database.
  2. It has delegated the task of validation to the domain object. (The Store class is actually the domain class). Since validation is a business operation that should be handled by the domain.

Output ports

We have two output ports. One to insert a store, another to increment the store count. They look like this

This concludes everything inside the hexagon, time to move on to the adapters.

Building Adapters

This is how the structure of the adapters will look at the end

Database Adapter

This is our database adapter

Few things to notice

  1. It implements both the ports needed by the service.
  2. It uses different repositories to perform the operations on different entities.

Repositories

The repositories are simple MongoRepositories, which look like this

Entities

This how the entity and the GeoJSONPoint sub-entity looks like.

If you have noticed, the representation of coordinates in domain and entity is different. That’s because in order to do geospatial queries in MongoDB you need to have your geodata in geojson the format, and we might want to support geospatial queries in future sprints. So we are able to model our entity very easily to support that without changing the domain model. See the benefit of domain, entity, and presenter separation 👯

Web Adapter

We have two classes in the web-adapter.

  1. Router, which routes web-requests to a handler.
  2. Handler, which calls the use-case to perform business operations.

This is the functional style of writing web-handlers instead of Controllers that we do generally.

If you have noticed, the handler deals with the Presenter object only.

Presenter

Our presenter is pretty flat, cause the UI needs the data in that way.

The way the same data is represented in presenter, domain, and entity, are completely different and optimized for the operations they do or their usages, amazing right?

If you are wondering why did I not use Lombok to generate Getters for the Domain, the reason is, every now and then the getters in domain would have some logic into them. Hence I opted for writing vanilla Java getters instead of lombok. But you can always use lombok for simple getters.

And that basically completes the task at hand friends.

And now the entire package tree is like this

If you were not able to connect all the dots, you can clone this repo and run it locally to see the entire flow.

But

All said and done, these concepts and architectures are extremely powerful and have a lot of benefits. But if you are developing a simple CRUD API, all of these are probably overkill and extra overhead for you. If you are developing a complex API and you need to maintain it for longer, and require agility then using these styles makes sense.

Takeaways

  1. Use the architecture style your team is comfortable with. Cause it’s easy to introduce a new architecture, but the hard part is to follow it religiously.
  2. We understood Hexagonal Architecture concepts.
  3. We understood the benefits of Domain, presenter, and entity segregation.
  4. We also developed a fully functional REST API using all of these concepts in Spring WebFlux.

Thanks for reading!

I am Aritra Das, I work as a Software Developer, and really enjoy building Distributed Systems. Feel free to reach out to me on Linkedin or Twitter for anything related to tech.

Happy learning…

--

--

Aritra Das
Javarevisited

Backend Developer | ❤ Distributed Systems | ❤ Open source