Ports And Adapter Architecture

Introduction

Ports and adapter is a very old and clean architecture to write modular and decoupled code. It was formally known as Hexagonal due to its diagram. Which shows as a hexagonal.

Here is a sample diagram, which, shows the different parts of this amazing pattern. It gives the flexibility to make a decoupled applications for core logic. The core logic is independent and you can use any implementation adapter to inject any functionality without making any change in Core.

Let us see how

Ports and Adapter Architecture

First, we need to understand the basic component of this design. This is divided into three parts,

application - defines how the outside world interacts with an application, it is a gateway to an application core. It might be by Rest controllers/WebAPIs, but it could be also by some kind of a message service (like Kafka, RabbitMQ, etc.).

core - the heart of the complete application. Which is implemented as on DDD (Domain Driven Design) approach. Here sits the business logic of your application. The goal is to have it written in plain language so that any technical or even non-technical person could understand. Inside it, we use a domain-specific language, which can be easily understood by business people. The most important thing, it should be agnostic of any framework, because these are only scaffolding of an application. The core is the heart of an application, something that encapsulates the logic in a plain C#/Java code.

infrastructure - it’s the last part, most of the applications does not only contain business logic but usually, they also need to use some external systems, like database, queue, third party services, other application and so on. In this part, we say how this communication will be implemented (in a core we only say what is needed). For example, in order to persist data in a database, we can use several approaches like NHibernate, ADO.Net, Entity Framework, or whatever framework we like.

“For some people, it looks like a normal layered application, but the key difference here is the Core does not know anything about the Application and Infrastructure. It should not know anything about the outside world, which keeps it decoupled from any change in the infrastructure of the application.”

Wait, what? How it could be achieved?

In addition, the answer is, inside the core, we define something that is called Port, and it defines all the interactions that a core will have with anything outside. These ports are like contracts (or APIs/Interface) and can be divided into two groups incoming (primary) and outgoing (secondary).

Incoming ports are responsible for how you can interact with the business core (what commands you can use on it) and Outgoing ports used by the core to talk to the outside world, like databases, event systems, third-party systems.

Now, let us have a look at the application, how the folder structure looks like. We are creating a very simple sample to understand the concept. I am not using any Database for now and keeping the Infra layer very light.

Ports and Adapter Architecture

Start this from the Core part first. Here is the snippet for the Incoming port:-

You can see, it is a contract (Interface) that contains a method, which an adapter has to implement. Because it is an incoming port, the adapter must reside inside the core.

Here, you have to look at the private field database (Referring to IBorrowingDatabase), this will be the adapter for the outgoing port, which we are injecting with DI.

This IBorrowingDatabase is a port that is implemented in the Infrastructure layer (going out from the core), But it is just a calling from Core and the core is not aware of what kind of infrastructure implementation you are going to inject for this. It will call whatever implementation you will pass via DI.

Now, look at the implementation code for IBorrowingDatabase, which is fetching/storing data from the DB. It could be any database; Core is not concerned about it at all. The core will just call the method, which is injected from the application layer.

Ports and Adapter Architecture

So far, every project is independent, now we are going to implement the application, which will use DI to pass the dependency to core and infrastructure.

Ports and Adapter Architecture

This is our API controller (Application) which again using the IBorrowBook object which we will inject using DI inside the Startup class.

Ports and Adapter Architecture

We have added singleton objects in the application layer for both the adapter. You can change and use any other implementation anytime in the future. You just need to write a new adapter implementation in infrastructure and pass it from the application. The core is not aware of it at all and does not require any change.

I know this is a long document, but I tried to reduce it as much I could. If you have any questions/suggestions, you are most welcome.