Hexagonal architecture

One of my favorite discoveries from last year is Hexagonal Architecture or “Ports and Adapters”.

Briefly: Hexagonal Architecture forces you to find the essence of your domain

It not just helps you separate business logic and technical details, it makes it very clear for you what is your essential business and what is secondary.

For me it feels more important distinction than those we got used to, such as Data-Service-Web layering, Interface-Implementation or even package-by-feature approach.

How it works

You define your Domain Model and Logic in completely abstract terms without any technological stack involved. Simply speaking your domain module should not have any dependencies (except for the framework, probably). All the interactions of your domain logic with the outer world happen via abstract Ports.

This shifts your vision up to a higher level of abstraction.

Consider you have a Spring style DocumentRepository. This naming already implies some database where you store your documents. But what if you want to send the documents to some message queue instead? What if it should become a DocumentSink? Or some DocumentConsumer?

If the actual output is not essential for your business logic and you just provide the result of document processing, you could name your port a DocumentOutputPort

Kotlin
interface DocumentOutputPort {

  fun output(document: Document)

}

(I, personally, would omit repeating suffixes and call it just DocumentOutput)

Now in the adapter layer you are free to choose the technical implementation for this port. It could be a database, a file storage, a message queue, some API, an AI system, whatever. You abstract it out completely.

But! If the idea of storing and retrieving documents is essential for your business layer then you could proceed with something like

Kotlin
interface DocumentStorage {

  fun save(document: Document)

  fun findBy(filter: Filter): Flow<Document>

}

I intentionally use the word “Storage” here to avoid any references to Spring Repository abstraction.

Here, the DocumentRepository, while being an abstraction in Spring, is actually just one of the possible technical implementations for your storage. That’s why I use a concept of Kotlin Flow as well, to emphasize that this storage might not even be synchronous.

Stay tuned: next time we will talk about the best practices for implementing “Ports and Adapters” pattern.

Comments

Leave a Reply

Your email address will not be published. Required fields are marked *