In Solutions Team, we always try to work with small applications. Even in relatively complex systems such as Haraj Bill
or Chat Next
, we always try to break them down into much smaller applications depending on the focus of their business use cases.
The reason why we are doing it like this is because small applications will have much less code rather than the big ones. Less code means less complexity in maintaining it. So when the application requires a bug fix or a new feature, it will be much easier to write the necessary changes.
However, since we will have a lot of small applications, we need some standard architecture on how to write them. This is so everyone in the Solutions Team can easily understand them even if they never work with those applications before (a.k.a maintainable code).
On top of that, we also need to automate the testing for our code. This is to ensure our changes (especially bug fix) is working as expected and it doesn't break existing application functionalities.
After studying several architectural patterns, we discovered that Hexagonal Architecture
is the most suitable architecture for our workflow in Solutions Team.
Unlike its sibling architectures (Clean Architecture
& Onion Architecture
) which focus on layers, Hexagonal Architecture
focuses on application business logic. This makes the application created using it very easy to understand, even by someone who just got started in Solutions Team.
When everyone on the team can easily understand our code, they will be able to handle its maintenance. This means when we are getting sick or going on vacation, someone from our team can easily cover our back. This is what it means to have maintainable code.
On top of that, Hexagonal Architecture
provides an excellent way to write automated tests for our code. This is because it clearly separates the business logic from its dependencies. So we can easily mock the dependencies when writing the tests.
However the concrete implementation of Hexagonal Architecture
could vary between team to team.
This is because Hexagonal Architecture
is more like a set of fundamental principles rather than a complete recipe. This is why when we read online articles about its concrete implementation, the authors usually come up with their own ways to implement it.
This is also the reason why we suggest you to use this document as your primary reference when learning about Hexagonal Architecture
. Yeah, this is because when you search for it online, everyone has their own ways to implement it including the Solutions Team.
In the upcoming sections, we will discuss the details of Hexagonal Architecture
implementation that suitable for Solutions Team projects. To make it easy to understand, we will use Hex Monscape
as our implementation example.
Hexagonal Architecture
is an architectural pattern that puts the business logic at the center of everything in the application codebase.
There are 3
main principles we need to follow when we want to implement this architecture:
- Clearly divide between the
inside
&outside
of the application.Inside
of the application is every components constructing application business logic whereoutside
is the otherwise. - Dependencies on
inside
&outside
boundaries should always point towardsinside
components. Sooutside
components must always refer toinside
components, not the other way around. - Isolate boundaries between
inside
&outside
components using ports & adapters.
From these principles, we can infer 4
constructing pillars of Hexagonal Architecture
:
- Core => A group of components constructing our application business logic. This is the
inside
of our application. - Actors => Any external entities interacting with our application.
- Ports => Interfaces that specify how Actors can interact with Core. This is the boundary between the
inside
&outside
of our application. - Adapters => Implement specification provided by Ports so Actors can interact with Core and vice versa.
Each of these pillars will be explained thoroughly in the upcoming sections.
Core
is a group of components constructing our application business logic. Essentially it governs how our application should behave on expected use cases (hence named Core
).
In Solutions Team, we use the following method to spot out Core
components:
- Take a look at our application API specification. Try to spot out the business logic context from there.
- In
Hex Monscape
, when we take a look at its API specification, we can see there are2
context of business logic:Play context
=> This is where the player starting new game and progressing the game itself.Battle context
=> This is where the player battle enemy with his/her monster partner.
- For each of these context, define
Service
interface for it. Just like what we did in here & here. Remember to also write the expected behavior for each methods as comments like in here. Trust me these comments will greatly help keeping our head in straight line when implementing these methods. - Notice that the
Service
interface that we define in step3
is only theDriver Port
for our application, not ourCore
component. However it is good starting point for us to define ourCore
component. - Implement the
Service
interface just like what we did in here & here. Notice that this is the place where we put our application business logic. This is also our very firstCore
component. - During the implementation of
Service
interface, we will notice that we need to interact with external entities such asMySQL
database. This is where we need to defineDriven Port
interfaces for our application just like what we did in here & here. - Beside defining
Driven Port
interfaces, during the implementation ofService
interface we will also need to define data model for supporting our business logic. This is why we haveEntity
package in here. - There you have it, we have all
Core
components for our application!
Note:
Sometimes we face confusion when we are in step
5
,6
, &7
. Usually this is because we find it difficult to find relationship between eachCore
components. In such case, try to utilize class diagram just like what we did in here. This will greatly help us in mapping out the relationship between eachCore
component.
Actors are external entities that interact with our application.
There are 2
types of actors:
Driver Actor
=> Actor that initiates interaction with our application.Driven Actor
=> Actor that being called by our application as the result of interaction withDriver Actor
.
In the case of Hex Monscape
, the incoming HTTP requests are the examples of Driver Actor
while the MySQL
database is the example of Driven Actor
.
Ports are interfaces defined inside Core that define how Actors can interact with Core components & vice versa.
There are 2
types of ports:
Driver Port
=> Ports for defining interaction between driver actor & core.Driven Port
=> Ports for defining interaction between core & driven actors.
In the case of Hex Monscape
, the examples for Driver Ports
are battle.Service
& play.Service
.
As for the examples for Driven Ports
are all interfaces defined in here & here.
Adapters are the components used to translate interaction from Actors to Core components & vice versa. They implements Ports defined in the Core.
There are 2
types of adapters:
Driver Adapter
=> Adapter for translating interaction from theDriver Actor
into a call acceptable by application core.Driven Adapter
=> Adapter for translating the command given by application core toDriven Actor
.
In the case of Hex Monscape
, the example for Driver Adapters
is rest.API
.
As for the examples for Driven Adapters
are battlestrg.Storage
, gamestrg.Storage
, & monstrg.Storage
.
Creating an application with Hexagonal Architecture
is very simple.
We just need to think about the expected behavior of our application, create the Core
components for it, then define the necessary Ports
& Adapters
for our Core
components. Voila, we have our application ready! 😁
Understanding applications created using Hexagonal Architecture
is also straightforward.
We just need to learn about its Core
components, understand its business context, and suddenly understand the whole application context! 😁
This is why in Solutions Team we choose Hexagonal Architecture
as our default architecture when building our applications. Yeah, because it enables us to create maintainable code much more quickly! 😁
To learn how to apply Hexagonal Architecture
in the new Solutions Team project, please refer to Project Methodology document.
Domain-Driven Design
(DDD
) & Hexagonal Architecture
are commonly paired. Some people even used the terms interchangeably.
In reality, DDD
& Hexagonal Architecture
are separate things. DDD
is an approach to spot application logic components from a business model perspective, while Hexagonal Architecture
gives our application a structure.
DDD
basically provides a formalized way to define the application core for Hexagonal Architecture
. But it is not a must for us to use DDD
when implementing Hexagonal Architecture
.
DDD
& Hexagonal Architecture
is a good combination to create large applications with complex business logic. But for us who want to create small applications with simple business logic, DDD
might be an overkill.
- https://alistair.cockburn.us/hexagonal-architecture/
- https://blog.octo.com/en/hexagonal-architecture-three-principles-and-an-implementation-example/
- https://www.youtube.com/watch?v=oL6JBUk6tj0
- https://medium.com/@matiasvarela/hexagonal-architecture-in-go-cfd4e436faa3
- https://medium.com/ssense-tech/hexagonal-architecture-there-are-always-two-sides-to-every-story-bc0780ed7d9c
- https://medium.com/ssense-tech/domain-driven-design-everything-you-always-wanted-to-know-about-it-but-were-afraid-to-ask-a85e7b74497a