Layered/N-Tier Architecture: The Unpopular Proven Way
Last updated
Last updated
The Layers Architecture pattern is considered by many to be the granddaddy of all. It supports N-tier systems and is, thus, commonly used in Web, enterprise, and desktop applications.... we rigorously separate the various concerns of our application or system into well-defined layers.
-Frank Buschmann, Author of Pattern-Oriented Software Architecture, A System of Patterns
Isolate the expression of the domain model and the business logic, and eliminate any dependency on infrastructure, user interface, or even application logic that is not business logic. Partition a complex program into layers. Develop a design within each layer that is cohesive and that depends only on the layers below.
-Eric Evans, Domain-Driven Design: Tackling Complexity in the Heart of Software
The layered architecture was a core fundamental concept that has been written in books such as Eric Evans' Domain-Driven Design: Tackling Complexity in the Heart of Software (the blue book) and Vernon Vaughn's Implementing Domain-Driven Design (the red book). It is also one of the most popular concepts taught in traditional education.
The layered architecture has been around for decades and has been the go-to proven default for many real world frameworks. Ever heard of frameworks like Ruby on Rails? Java's Spring Framework? Or even Microsoft's ASP.net MVC? And of course, how can we forget the plethora of PHP MVC Frameworks like CodeIgniter, Zend, and Yii that were extremely popular in the late 2000's and early 2010's.
If you've been doing web development at all in the last 2 decades, then you would have heard of this design pattern. This has been an extremely popular pattern adopted by the object-oriented world. Most frameworks will use some variation of this type of pattern. Let's go dive a bit deeper into each of the components.
Instead of me giving you the boring definition of what the Model-View-Controller pattern (MVC) is, I'd like to explain MVC from its origins and relate it to our present day's understanding of this pattern.
This origins of MVC can actually be traced by one of Martin Fowler's boos in the early 2000s, Patterns of Enterprise Application Architecture. In his book, he centered his discussion around an architecture of "three primary layers: presentation, domain, and data source".
Fowler starts off with the presentation layer, as known as the view layer.
Presentation logic is about how to handle the interaction between the user and the software. This can be as simple as a command-line or text-based menu system, but these days it’s more likely to be a rich-client graphics UI or an HTML-based browser UI. The primary responsibilities of the presentation layer are to display information to the user and to interpret commands from the user into actions upon the domain and data source.
-Martin Fowler, Patterns of Enterprise Application Architecture, p. 19
As you can see, this is pretty spot on as what we would refer to in the last decade or so. In our case, the view layer is simply just a JSON output, the interaction portion of his definition can be seen as the hypermedia links in our output response.
Fowler then moves on to the data source logic, which for us would be the persistence or model layer of our application.
Data source logic is about communicating with other systems that carry out tasks on behalf of the application. These can be transaction monitors, other applications, messaging systems, and so forth. For most enterprise applications the biggest piece of data source logic is a database that is primarily responsible for storing persistent data.
-Martin Fowler, Patterns of Enterprise Application Architecture, p. 20
As mentioned before, this is simply a layer with which we can communicate with the database through our own application code. The most common way of doing this today would be with an ORM (Object Relational Mapper).
Finally, the domain logic, which can be a combination of the custom business services you write in your own application. if you are old school, the controller is essential where you would write most of your own business logic.
The remaining piece is the domain logic, also referred to as business logic. This is the work that this application needs to do for the domain you’re working with. It involves calculations based on inputs and stored data, validation of any data that comes in from the presentation, and figuring out exactly what data source logic to dispatch, depending on commands received from the presentation
-Martin Fowler, Patterns of Enterprise Application Architecture, p. 20
The way I understand the controller is that it simply directs the input and output of the request-response life cycle. It can contain hard logic of your business, but should be kept to a minimal length.
Although the traditional MVC style is quite "good", there have been many additions to the old school way of thinking. If you have ever worked with other languages and frameworks such as Java or C#, you will have noticed that those 2 communities have adopted the "services and repository pattern". This means extending the traditional 3 layers by adding a repository layer and a services layer.
Let's find out what those 2 layers are and how they can extend the layers in our domain logic.
Repositories are classes or components that encapsulate the logic required to access data sources. They centralize common data access functionality, providing better maintainability and decoupling the infrastructure or technology used to access databases from the domain model layer.
Repositories solve 2 problems. The abstraction of (complex) database queries and the inversion of control of the persistence layer. I like to think of repositories as utilities for querying the database and an adapter layer that let's swap different implementations of databases.
Understanding the abstraction of database queries is quite simple. Imagine having to write a very long database query. Now imagine having to rewrite that database query out every time you want to use it. Well, most people would just encapsulate a long database query in some sort of re-usable function. And that's just it, that is one of the purposes of the repository pattern, to create re-usable, abstracted, and encapsulated database utilities functions.
The inversion of control of the databases is a little bit harder to explain, but here is my take on it. Imagine we had to use MongoDB as our database. Now imagine, you decide you want to use MySQL because you realized non-relational databases are just not for you. But hold up, now you realize you want to use a different SQL driver, you went from writing raw SQL with a native MySQL driver to using an ORM like Sequelize. Wouldn't it be great if you were able to swap out those different implementations whenever you wanted to? That is inversion of control, the ability to loosely couple our data layer in order to which make them testable, maintainable, and extensible.
A Service Layer defines an application’s boundary and its set of available operations from the perspective of interfacing client layers. It encapsulates the application’s business logic, controlling transactions and coordinating responses in the implementation of its operations.
Services are what make up the business logic of your application. To best explain what a "service" is would be to give examples.
Consider that an e-commerce website may have a:
CustomerService
for fetching customer information.
PaymentService
for making and processing payments from the purchases made by customers.
OrderService
for fetching order information from the purchases customers have made.
Services can access different data layers (repositories) and may call another different service from within.
Layered architecture has actually made its way into the JavaScript community quite nicely. In the past decade, there have been various Layered-based Node.js frameworks that have been released. They have all taken a page from the frameworks of other traditional object-oriented programming languages.
It first started off in 2012 with Sails.js, an alternative or a more structured approach than just the vanilla express router. Sails.js was great, as it emulated many of the features of Ruby on Rails and was probably the first ever "real" Node.js MVC Framework.
But it wasn't until 2017, when TypeScript's explosion in popularity began to bring a new level of innovation to bringing the traditional architectural frameworks into the JavaScript community.
In 2018, we would see a brand new framework, Nest.js, take the enterprise world by storm. This is taken directly from the philosophy of Nest.js.
Nest provides an out-of-the-box application architecture which allows developers and teams to create highly testable, scalable, loosely coupled, and easily maintainable applications. The architecture is heavily inspired by Angular.
In the same year, we would see another competing framework, Adonis.js. A framework that very much reminds me of PHP's Laravel framework.
To sum up what Adonis.js is really about. This is taken directly from their official website.
How is AdonisJS different to Express or Koa?
Express and Koa are routing libraries with a thin layer of middleware on top. They are great for several use cases but fall apart when projects start to grow.
Since your projects have their own standards and conventions, it may become harder to hire developers to work on them. As AdonisJS follows a set of standardized conventions, it should be easier to hire someone to work on existing AdonisJS apps.