Devot Logo
Devot Logo
Arrow leftBack to blogs

ADR - the Alternative to Mainstream Architectures

Manuela P.8 min readNov 11, 2022Technology
ADR - the Alternative to Mainstream Architectures

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

MVC

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:

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.

Responder

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.

REST API

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.

Message class:

  • validates the parameters received from the request

  • it creates a Message object

Handler class:

  • 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.

Testing

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!

KEEP READING

Similar blogs for further insights

Boost Your Coding Efficiency by Mastering TypeScript Mixins
Technology
Boost Your Coding Efficiency by Mastering TypeScript Mixins
What do ducks and TypeScript mixins have in common? More than you'd think! Dive into our blog to discover the connection and learn how mastering mixins can make your code more adaptable and efficient.
Exploratory Testing vs Automation: Finding the Perfect Balance for Successful Software Testing
Technology
Exploratory Testing vs Automation: Finding the Perfect Balance for Successful Software Testing
Shifting from QA to a developer role can give you a broader perspective on many aspects of software development. In this blog post, we delve into exploratory and automation testing—discussing their differences, when to use each, their various types, and more. Understanding both approaches is invaluable, offering a comprehensive toolkit for improving software quality and reliability.