Ports and adapters — a practical introduction. Part 2, Primary ports and adapters.
In the first part of this practical introduction to Ports and adapters architecture (Hexagonal) we went through a simple example of one entity class — BankingAccount and creation port which saves and deletes this entity. This time we will go beyond simple example and start digging into details. If you want you can watch the whole explanation on my youtube channel here:
When I think of applications in general, first I see business logic. Business logic mainly translates requirements provided by customers into the code. When you create an online banking system, as we do in this short tutorial, you can imaging this logic is responsible for money balance calculation on your account.
But once requirements are done and you start coding you quickly realize that there is a lot of code that needs to be written in order to triggers functionalities of our own system, communicates with other systems, sometimes communicate with different parts of our own system. To accomplish that we write UI’s, APIs, send events, integrates with external services. All of the above is necessary but it’s not the core of our domain. It’s something that keeps changing. Everything outside of our core domain can be called infrastructure or infrastructure code. Hexagonal architecture is something that helps you to create it and integrate smoothly with main business functions.
Let’s say we need to trigger some functionality of our system. Our online bank should have the ability to create a new account. We define service class for that.
It has two public methods: createBankingAccount and removeBankingAccount. This is an entry point to our application and it’s part of our business logic. This kind of functionality is called a primary port. Primary ports are used by Primary adapters. An example of a primary adapter can be REST Controller or JMS message. Primary adapters (or Driving adapters) inject port and triggers its functionality. In other words, Primary adapter is a wrapper around Primary port. Let’s see an example:
BankingAccountController is a Primary adapter that injects BankingApplicationService class which is the primary port.
To put everything together let’s take a look into the below diagram:
The left part represents Primary ports like UI, MQ messages, FTP files. Those are things that trigger certain functionalities in our system. Primary adapters (driving) are wrappers around primary ports. In practice, it means that they inject an instance of the port and execute port functionality.