Hexagonal Architecture — 2/2 — more in-depth with kotlin

In the previous part, we walked thru the concept of the hexagonal architecture (purpose, principles, modules, layers, ports and Adapters). If you have not read the first part yet, or you need some remind, please read it before going through this one. We will now see how to do to properly isolate the business code from the rest. One important thing to remember is that the business code is isolated within the inner layer.

Table of contents

Part 1

  • History
  • Purpose
  • Principles
  • Modules
  • Layers
  • Ports and Adapters

Part 2

  • Inner Layer Isolation
  • Testing
  • Sample
  • Hexagonal vs Onion vs Clean

Inner layer Isolation — Dealing with dependencies

What matter is how to isolate the domain code from the rest.

  • The outer layer has the service adapter that uses the service logic in the inner layer
  • The service in the inner layer uses the repository in the outer layer
  • The repository adapter in the infrastructure (outer layer) does whatever is required to interact with the outside

The diagram shows one way of dependency flow, from the left to the right. On the left side in, the exposition layer depends on the business logic in the domain, that is not a problem. However, on the right, the business logic depends on the infrastructure layer, and that is a problem we want to solve.

Inner Layer Isolation using Dependency Inversion

To be able to isolate the domain code from the outer layer properly, we need to change the flow of dependency. Using the Dependency Inversion Principle, we can control dependencies from the outer layer, so that the domain does not depend on anything from the outer.

  • The outer layer has the service adapter that uses the service logic in the inner layer

The exposition’s adapter depends on the service logic, which is not a problem. We can introduce an interface, so we can inject the service logic which implements the service interface. The interface allows to hide details of the business logic, thus the exposition’s adapter won’t see implementation of the service inside the domain

  • The service in the inner layer uses the repository in the infrastructure (outer layer)

We don’t want the service logic to depend on the infrastructure, hence the repository implementation. To inverse that dependency, we introduce purposely an interface, called port, to abstract the its detail, so we can inject the repository adapter which implements the repository interface. The interface also allows to hide details of the business logic. The injection is done at the exposition level

  • The repository adapter does whatever is required to interact with the outside

Testing

The hexagonal architecture provides some benefits. The most important one is testing automation. The application layer can completely be wrapped within a test framework The infrastructure can be mocked This does not mean we don’t have to test the application layer code and the infrastructure code

When it comes to testing, we may have a battery of tests to cover the pyramid of tests. Depending on the context, we may need to have more or less extensive testing at different levels. For the purpose in this article, let’s just focus on the domain behaviour testing. The diagram above show two level of tests :

  • The integration test

The integration testing implies to reach the endpoint, while mocking the infrastructure, for instance the repository to leverage the complexity of the connecting to a real database, within the testing application context.

  • The component test

Here, we also need to mock the infrastructure for the same reason. However, we can also simplify things by not bothering about the enveloping application, which most of the time uses some sort of framework. For the example we are using in this article, SpringBoot 5 is powering up the business logic — the domain code. Think-fully, we want to focus only on the business behaviour.

By having the two levels of tests above, it does not mean we can exclude unit testing, as it serves other purposes, in addition to verify the functionalities.

Code Sample

The project codebase is separated in three modules as cited in the “Three Modules” section :

  • Exposition
  • Domain
  • Infrastructure

The Exposition Module is using SpringBoot to power up the application, so it means that we are using the Spring framework. So far as we are concerned, the Spring IoC helps to manage the Dependency Injection. Beans, in the Java world, are declared using annotations prefixed with “@”. Typically @Service, @Repositiory, and @RepositoryRestResource the three main annotation we are interested in. Other annotations are used basically to configure the Spring context, or to declare the endpoints or entities.

One thing very important to notice is that we don’t use anything that is related to the Spring framework in the Domain Module.

Exposition Module

[Rest Endpoint] exposition/src/main/kotlin/io/github/newlight77/bootstrap/note/api/NoteApi.kt :

[Api Handler] explosition/src/main/kotlin/io/github/newlight77/bootstrap/note/api/NoteApiHandler.kt

[Service Adapter] exposition/src/main/kotlin/io/github/newlight77/bootstrap/note/api/NoteServiceAdapter.kt

[Spring Context] exposition/src/main/kotlin/io/github/newlight77/bootstrap/note/config/NoteConfiguration.kt

[Model Data Class] exposition/src/main/kotlin/io/github/newlight77/bootstrap/note/model/NoteModel.kt

Domain Module

[Repository Interface] domain/src/main/kotlin/io/github/newlight77/bootstrap/note/api/INoteRepository.kt

[Service Interface] domain/src/main/kotlin/io/github/newlight77/bootstrap/note/api/INoteService.kt

[Service Logic] domain/src/main/kotlin/io/github/newlight77/bootstrap/note/service/NoteService.kt

[Domain Data Class] domain/src/main/kotlin/io/github/newlight77/bootstrap/note/model/NoteDomain.kt

Infrastructure Module

[Entity Data Class] infrastructure/src/main/kotlin/io/github/newlight77/bootstrap/note/entity/NoteEntity.kt

[Spring JPA Respository] infrastructure/src/main/kotlin/io/github/newlight77/bootstrap/note/jpa/NotesJpaRepository.kt

[Repository Adapter] infrastructure/src/main/kotlin/io/github/newlight77/bootstrap/note/jpa/NotesRepositoryAdapter.kt

Hexagonal vs Onion vs Clean

We need to compare the three architecture base on 4 aspects :

  • Loose coupling from the External

All domain code is framework/library independent.

  • Dependencies direction

Only the clean architecture is explicit in pointing out that the dependencies direction is towards the centre, but both an hexagonal need to apply to the Dependency Inversion Principle

  • Layers

Hexagonal Architecture mentions two layers : inner and outer layer of the Application. Onion has four layers : Interfaces, Application services, Domain services and Domain model. Clean Architecture is similar to Onion : Interfaces, Application Services, Domain Service (use cases), Entities (Domain model)

  • Testability in isolation

In all three Architecture styles, we can simply mock the external tools and delivery mechanisms and test the application code in insulation, without using any DB nor HTTP requests.

Onion Architecture

The onion architecture proposed by Jeffrey Palermo in 2008.

Onion architecture is similar to the hexagonal architecture. It also externalises the infrastructure with proper interfaces to ensure loose coupling between the application and the external. It decomposes further the domain into several concentric rings using inversion of control.

Onion Architecture by Jeffrey Palermo @ https://jeffreypalermo.com

Clean Architecture

The clean architecture proposed by Robert C. Martin in 2012.

Clean architecture combines the principles of the hexagonal architecture, the onion architecture and several other variants. It provides additional levels of detail of the component, which are presented as concentric rings. It isolates adapters and interfaces (user interface, databases, external systems) in the outer rings of the architecture and leaves the inner rings for use cases and entities. [It uses the principle of dependency inversion with the strict rule that dependencies shall only exist between an outer ring to an inner ring and never the contrary.]

Onion Architecture by Uncle Bob @ https://blog.cleancoder.com

Conclusion

The goal is to structure the code so that changes are easy, safe and fast to make. Testing ensure the safety, but also can drive the drives. However the architecture matters, as it can ease the adoption of changes. When it’s easy and safe, along with automation, we can make changes fast.Conclusion

The goal is to structure the code so that changes are easy, safe and fast to make. Testing ensure the safety, but also can drive the drives. However the architecture matters, as it can ease the adoption of changes. When it’s easy and safe, along with automation, we can make changes fast.

Acknowledgement

Thanks to Dan MAGIER for reviewing and helping me to clarifying some ideas in this article.

Reference

Unlisted

--

--

--

Developer, Coder or Craftsman, but code quality matters. Technical writer @wesquad @wemanity

Love podcasts or audiobooks? Learn on the go with our new app.

Recommended from Medium

SQL Learnings: How to retrieve Table Data that Ends with specified characters

Fantom Technical Update — 1

Getting Microservice Specification Right the First Time

Gutenberg and the future of WordPress

A framework to build Cloud Operating Model and Governance — Part II

Google Sheets — Remove Duplicates

Benchmarking in C# Using BenchmarkDotNet

How to Build a Framework that is Actually Helpful

and also that you are capable of doing

Get the Medium app

A button that says 'Download on the App Store', and if clicked it will lead you to the iOS App store
A button that says 'Get it on, Google Play', and if clicked it will lead you to the Google Play store
Kong To

Kong To

Developer, Coder or Craftsman, but code quality matters. Technical writer @wesquad @wemanity