Anybody who has worked on couple of software projects with layered architecture knows how over a period of time all the projects get into the problem of excessive coupling across layers.When we start a project we all take an unsaid “oath” that we will have well separated layers where my business logic will be totally independent of my data access logic or where my UI logic would be independent of business logic.But after an year or two passes away with people coming and going out of the project and deadline pressures we all end up writing code which results in leaky layers.
Although I believe that there is no standard solution to this problem and it depends a lot on how disciplined we are while writing code ,there are certain architectural style that can help us achieve this aim. One of these architectural style is known as Hexagonal Architecture.
What is Hexagonal Architecture
Hexagonal Architecture ,also known as Ports and Adapter pattern is an architectural style which promotes and gives structure for achieving separation between actual application / domain logic and various technology concerns and external actors.Alistair Cockburn has a detailed article on this architecture style and below is short definition from the same article.
Allow an application to equally be driven by users, programs, automated test or batch scripts, and to be developed and tested in isolation from its eventual run-time devices and databases.
Personally I prefer the term Ports and Adapter as it clearly tells what this style is all about but if it makes sense below is the picture from the Cockburn’s article describing the hexagon.
There three basic components which this architecture talks about.
- Domain Model
This term does not need any explanation as we all use it our day to day work but it is very important to understand how the problem we are trying to solve relates to domain model.Most of the time we say that we have our domain logic independent but look again you will see that one odd ORM attribute ( in many cases more) sitting on your POCO class which couples it with ORM technology or a property which when accessed fires a database query in the background (again coupling it with data access technology).And in some of the extreme cases you may see queries being scattered across classes etc.Goal that we are trying to achieve using this architecture is summed up below.
The domain model does not depend on any other layer; all other layers depend on the domain model.
Ports are the medium through which business logic is accessed.Port is a use case boundary i.e. Ports correspond to use-cases in the application.Multiple things can use the same port.E.g. In banking domain a use-case like ‘DepositCash’ can be used by an ATM machine or Online bank site or a backend system or a Test harness for testing the port.
Simplest implementation of a Port can take form of an API / Facade layer.Only issue with this approach is that soon our API / Facade will become bulky set of methods and SRP (single responsibility principle) will go for a toss.Also calling these API directly makes it tightly coupled with the invoker. A much better implementation for Ports is using command pattern which will address both of the above problems and will keep Ports maintainable in long term. You can refer this github repository link to Ian cooper’s Paramore project which is an excellent implementation of Ports using command pattern.
When testing business logic it is very important to keep in mind that in this architectural approach we do not write test keeping in mind the internals of business logic as it will again tightly couple tests with business logic.All unit tests are written against the behavior of the Ports.
Adapters act as a layer which serve the purpose of transforming the communication between various external actors and application logic in such a way that both remain independent.In hexagonal architecture all the primary and secondary actors interact with the application ports through adapters.
Primary actors are the ones which use system to achieve a particular goal whereas secondary actors are the ones which system uses to achieve primary actor’s goals.E.g. consider following use case.
A customer can apply for a loan application using online website or using an ATM .Application then verifies the credit rating check system,updates information in database and sends an update mail to the customer
Here User is the primary actor where as Credit ranking check system ,database and mailing system are secondary actors.In this case we can have following Port and adapters.
Port (API / Command):
- MVC adapter – Assuming we are using ASP.NET MVC for online backing system , this will be your ASP.NET MVC application
- REST Adapter – Assuming ATM machine interacts with the banking system using a REST API this can be a ASP.NET Web API.
- SQL Adapter – For interacting with SQL server database
- Credit Rating Check System Adapter – Interacts with credit rating check system.
- Email adapter – Interacts with emailing system .
Both 4 & 5 can be custom implementations using Adapter pattern.
You can also have a flat file adapter in case data needs to be persisted in text files and also an adapter for Mocking database for testing which just sits in the memory and acts as database.
Hexagonal architecture is an excellent approach to fight drawbacks of a traditional n-layered architecture.Below are few advantages that you get from using this approach.
- Freedom to change technology concerns on either side i.e. for both primary actors and secondary actors.Remember a layered architecture using MVC (and many of its variations) pattern also implement the concept of Ports and Adapters but only difference being it applies this concept only for the primary actors.
- Keeping code easy to understand ,maintainable and testable.
- Freedom to change domain model with minimal or no impact on external actors.
I just want to point out that this is not a unique approach, you may find lot of elements of this approach scattered in a traditional n-layered architecture.Also this is not the only approach which addresses the problems of layered architecture, there are other approaches like Onion Architecture ,Clean Architecture etc.For a general reference refer this course on pluralsight on clean architecture. It touches upon various architecture pattern and practices in general. And finally there is no substitute for good coding habits and discipline, without which no architecture will ever succeed in achieving its goals. :-).
Recommended Books on Amazon :
Software Architecture in Practice (3rd Edition) (SEI Series in Software Engineering)
Patterns of Enterprise Application Architecture By Martin Fowler
Code Complete: A Practical Handbook of Software Construction, Second Edition By Steve McConnell