Facilitation Is an Underrated Skill
Architectures. Design patterns. There are so many of them. Yet, most of you reading this may be aware of a couple, if that even. At first, there may not be anything wrong with that. But with experience - you, as a software developer - may stumble upon certain limitations of one architectural design and may wonder: "Is there anything better or even more suitable for my needs and requirements?".
In most cases, the answer to that would be a simple "Yes." However, out of so many design patterns, there is no simple answer as to what your next choice of architectural design pattern could be. And instead of listing all of them here, I'll be providing you with greater detail about one of them, namely ADR. Before delving deeper into the topic of ADR, I want to define what an architectural pattern is and what use we get out of it.
To start, when looked at from afar, software, as we know it today, is a fairly complex system of components and interfaces working together. The communication between elements and the structural composition of the program make up the blueprint or, better yet, the schema on which the program is built. An architectural pattern is a principle that defines how a program is structured and how the elements within are composed (so they can work efficiently and without conflicts).
The readily available MVC pattern is one of the more prominent architectural patterns in the programming world. It is based on three main components; Models, Views, and Controllers, and is considered one of the fundamental architectures out there, for a good reason, too - it's widespread and is easy to get started with. The communication between the main components is executed via the following flow:
The Model communicates with the database to fetch data which is then transferred to the Controller
The Controller performs its logic on the data it has received and sends it off to the View
The View renders the data to the user, and the user can interact with the data displayed
Despite its popularity and ease of use, MVC certainly does come with its limitations and innate flaws. What those are is a topic for a whole other blog post. Primarily, the goal of this post is to help developers understand the perspective of an alternative architecture with its use cases.
The ADR Architecture
Similar to other architectural patterns like MVC, MVVM, VIPER, and others - ADR has its functionality covered across multiple components, and in this case, it's three of them; Actions - Domains - Responders. To start, we'll first look at the Domain, for it gives us a good basis for understanding the type of development ADR is made for.
Note: The following components will be explained with PHP & Symfony in mind.
D - Domain
Instead of basing development on the rather popular Data-Driven-Design paradigm, ADR forces us to use the Domain component extracted from the Domain-Driven-Design architectural pattern. Its primary use is organizing business logic and presenting it as codebase domains that communicate with each other over public interfaces. One important thing to note is that each Domain specializes in one resource only, implying the SRP (Single Responsibility Pattern).
In that regard, every Domain does one thing and does it very well. To put this in context, we want to place all endpoints relating to that entity within its own directory when creating a new entity. Then, the entity class and the test data being seeded to the database shall be put within its own domain. An example of such would be creating a User as an application layer. In that case, the User would be its own domain containing logic about the entity. Any logic being performed on that entity, and every endpoint relating to it, would be placed in its own directory outside of the domain. Here's an example of an application structure:
A - Action
In contrast to MVC, the Action of ADR is defined as Controller (from MVC) with one singular action. This means that each action is its own class, and there are as many Action classes as there are User actions.
Every Action class is defined in the same way, and it consists of:
a route definition
the __invoke function, which calls the corresponding function from the Handler class and receives the corresponding function from the Message class as an argument, and returns the representation that we see as the response of the defined route (the Handler and Message classes from this given example will be explained later on)
R - Responder
The last part of the ADR is the Responder. Like the Action part, it's also located in a separate directory. Its use is to create the response object based on the parameters received from the request of the Action class.
ADR as a whole
The difference between MVC and ADR isn't a whole lot, but certain things bring a more polished experience when working with larger applications.
The basic flow consists of the Action receiving an HTTP request which calls the Domain with it, then some processing by using business logic is being done on the Domain's end, which in turn returns the result to the Action again. From there, the Responder creates a response object from the data received by the Action and sends off an HTTP response to the user.
The one big difference between MVC and ADR lies in the presentation of the View-Responder components. In MVC, the View can send or modify information coming from or going to the Model, while in ADR, the Responder may never talk to the Domain, leaving the Action as the only source of communication towards the Domain.
Benefits of ADR
Since ADR is based upon existing concepts of MVC, we cannot say it's an entirely new architecture in its own regard, but rather take it up as an improvement in some aspects when compared to origin (MVC). That makes it an improvement of the 'old way of doing things' and results in the following improvements:
instead of having one Controller consisting of many different actions, in ADR, there is an equal amount of 'Controller' classes for each action being performed on an entity
ultimately, all of the things listed above lead to a cleaner & more maintainable codebase, which can be reused in another project
the domain layer is separated from the application layer (Separation of concerns)
another bonus is that Actions can be called within the console because they're treated as services (Context Agnostic Code)
easier to make code changes without affecting other parts of the codebase or breaking stuff
the code can be grouped by target and not by component type (controllers, views)
domains represent an area of interest or an area over which a person has control over, and can be abstracted
Is ADR the better alternative? If so, when?
One of the better use cases for ADR are web REST API’s because ADR is capable of fulfilling the principles or REST without leaving any history of steps behind it, ultimately resulting in not having a state. In doing so, each HTTP request is handled independently and has no relation to the previous and next requests on the stack.
On the contrary, when developing CRUD based applications, the architecture of ADR may be overkill. Given that in such cases, the Action is not expected to be used in any context other than the one in which it was implemented in.
A practical example - ADR & Symfony
Given all of the theory above, we shall look at an actual code example of the entire ADR architecture in this section. To begin with, it is important to note that we will use the Command design pattern - this means there won't be a Responder class like the one explained in the introduction, but rather two separate classes; the Handler and Message, which will be used to create a responder object.
This example aims to create an ADR architectural representation using a User as an entity, among with a given Domain, and an Application layer of the same name. The User entity will only have one attribute and one action, which will populate the database.
Note: All the code and structural examples are written and made with PHP & Symfony. The principles of ADR remain the same for other languages, although each may have its own nuance regarding how the data and logic are structured and constructed.
The User Entity:
Another thing to note is the use of the Repository design pattern which, in this example, is being used alongside of ADR. It enables for direct fetching from the database in a separate Repository class for every entity we have and thus separates the model from the data layer.
Next up is the example of creating an Action, specifically an endpoint for creating a User - createUser:
From the above, we can deduct the following:
Action class calls the corresponding Handler class
Handler class receives the corresponding Message class, along with the receive request as a parameter
the Handler class returns the Response object
To explain a little better, let's look at each the of the Handler and Message classes in a bit more detail.
validates the parameters received from the request
it creates a Message object
creates User object
returns the response of the Action class as an object
As seen from the examples above, using the ADR architecture consists of adding as many classes as there are parts of the execution for one action. We have the Action class that accepts the request, the Message class that validates the request, and the Handler class that creates a response object that the Action class returns as a response.
As with most architectures, choosing an appropriate testing framework is almost as important as implementing the code itself. The Test Driven Development (TDD) principle is one solid principle for any kind of professional web development. Therefore, it is good to know what testing an application in ADR looks like.
When testing, stick to the same division of Domains and Applications as with the initial development process. Domains, or more precisely Entities, can be tested independently of the rest of the components, and the same can be applied to Action classes. Response and Handler classes are, in most cases, not explicitly tested, but rather are being tested as a part of Action tests. All of this combined enables us to create a separation of the domain and application layers, enabling easier project navigation. It ultimately results in adding new tests and repurposing components, while also having the ability to move parts of tests from one part of the application layer to another.
A little conclusion
As mentioned in the introduction, choosing an architecture is a crucial part of every web development project. Of course it is. The benefits of carefully picked architecture are numerous; however, choosing poor architecture may lead to slowdowns and complications. Therefore, do choose wisely and do think out of the box. Maybe ADR is not for every project, but in some instances, such as here at Devōt, ADR may be the next architecture in your tool belt to give you a cutting-edge advantage over the competition. Keep learning, and you'll get the gist of it, I'm sure!
Facilitation Is an Underrated Skill
Meetings help people to share ideas, discuss and make decisions. However, we sometimes attend a meeting assuming each participant knows how to communicate. That’s when you need a facilitator to help you bring communication structure and reach the right decisions.Read
Things That Every Scrum Master Should Keep In Mind When Organizing a Retrospective
According to the Scrum framework, the Retrospective is the last event of every project. Retrospectives are crucial for every Scrum team because they allow the team to improve the project's performance. Holding retrospectives can be challenging, and these tips can help you to improve your role as a Scrum master.Read