Hexagonal Architecture (Ports and Adapters)
Project link: https://github.com/sschonss/microservices-hexagonal
There are many ways to organize a project, and one of the best known is hexagonal architecture. Hexagonal architecture organizes a project so that it is independent of frameworks, databases, UI and so on. The goal is for the project to be independent of any technology, so that each one can be easily replaced.
In this project, hexagonal architecture was implemented with the PHP programming language and the Laravel framework. The idea is for the project to be independent of Laravel, so we use Laravel only to create routes and controllers, and the rest of the project does not depend on it.
Project structure
The project was split into 3 main layers:
Laravel: All of the project's infrastructure, such as routes, controllers, etc.
Adapters: Layer that holds the project's input and output adapters.
Core: Layer that holds the project's business rules. Inside the core, we will work with Domain Driven Design (DDD).
Adapters
Adapters are responsible for adapting the project's inputs and outputs. But what does that mean?
Imagine you have a project that needs to talk to a database. Without an adapter layer, you would have to use the database directly in your project. That means using the database classes directly in your code, which makes your project dependent on the database.
An ORM (Object Relational Mapping) is an example of an adapter. The ORM is a layer that adapts communication with the database, so the project does not depend on the database itself.
In our project, we have the following adapters:
EmailAdapter: Adapter responsible for sending emails.
RabbitMQAdapter: Adapter responsible for sending messages to RabbitMQ.
They contain the classes that adapt communication with the email service and with RabbitMQ.
All of this uses plain PHP, without Laravel, so the project stays independent and each piece can be easily replaced.
Core
The core is the layer that holds the project's business rules. Inside the core, we will work with Domain Driven Design (DDD).
What is DDD?
Domain Driven Design is a way of organizing a project so that it is split into domains. Each domain is a part of the project that holds business rules. This keeps the project organized and easy to maintain.
Inside the core, we have the following domains:
Email: Domain responsible for the application's emails in general.
Connections: Domain responsible for connections to external services, such as RabbitMQ, Redis, etc.
Inside each domain, we have the layers that hold the business rules for each feature.
Most importantly, the core depends on nothing. It is independent of any framework, database, etc. That way, we can easily replace Laravel with another framework, or MySQL with another database.
The core talks to external services through the adapters. That way, the core does not depend on any external service, only on the adapter.
The project
The project is a simple example of how hexagonal architecture can be implemented. It is a user registration system, where users can sign up and receive a welcome email.
The project was split into 3 services:
Main App: The main service, where users sign up. This is where the hexagonal architecture lives.
RabbitMQ: The messaging service, to which the Main App sends a message.
Email App: The email service, which reads messages from RabbitMQ and sends an email to the user.
This way, the Main App does not depend on RabbitMQ or on the email service. It just sends a message to RabbitMQ, and RabbitMQ delivers the message to the email service.
How to run the project
To run the project, you need Composer, Docker and Docker Compose installed on your machine.
After installing Docker and Docker Compose, just run the following command:
cd main-app composer install ./vendor/bin/sail up
After running the command, the project will be running on port 80.
We can now create a user through the POST /api/user endpoint, sending the following JSON:
{ "name": "John Doe", "email": "john@doe.com", "password": "123456" }
After the user is created, an event is published to RabbitMQ, but the email service is not running yet. To run it, just run the following command:
cd email-app docker-compose up --build --force-recreate
After running the command, the email service will be running, reading messages from RabbitMQ and sending an email to the user.
Accessing RabbitMQ
To access RabbitMQ, just open the following address:
http://localhost:15672
And log in with the following credentials:
Username: guest Password: guest
Conclusion
Hexagonal architecture is a way of organizing a project so that it is independent of any technology. In this project, it was implemented with PHP and the Laravel framework. The idea is for the project to be independent of Laravel, so we use Laravel only to create routes and controllers, and the rest of the project does not depend on it.
The project is a simple example of how hexagonal architecture can be implemented. It is a user registration system, where users can sign up and receive a welcome email.
We also implemented a messaging service with RabbitMQ and an email service. This way, the Main App does not depend on RabbitMQ or on the email service. It just sends a message to RabbitMQ, and RabbitMQ delivers it to the email service.
We saw how messaging helps decouple services, and how hexagonal architecture helps organize a project so that it is independent of any technology.
Comments