The Road To Better Backends

13/05/2021

Designing backends through abstraction

Recently it’s become very apparent that the frontend has overtaken the backend in terms of new technologies and programming methodologies. With the advent of extremely flexible frontend development frameworks such as React and Vue, coupled with expressive state management tools such as Redux or facebook’s new Recoil. The frontend is rich with new technologies allowing developers to not only solve problems, but structure code in a manner that can be understood and maintained.

However, what about the backend? Has it stagnated in terms of technology or methodology, or are there new and better ways to design, develop and deploy backend web applications. In this article, we will explore these 2 aspects and how they have evolved over the years allowing developers to deliver robust, expressive and maintainable backends.

 

Design by unwinding abstraction: A top down approach (The C4 model)

In terms of design, people coming from computer science and software engineering backgrounds all know the dreaded UML diagram that was force fed to them in their software engineering courses. It was made more apparent that as software engineers we are supposed to communicate like this. However, in industry, large upfront UML designs are looked down upon as most modern teams have adopted an agile workflow that leverages communication over processes and tooling. 

Developers did not necessarily throw away the design phase, instead the overall process was simplified. Instead of designing the solution with overly detailed UML, developers opt for simpler box and line diagrams in order to convey the system to other developers and stakeholders. Till today, many companies still use this methodology to outline the overall architecture of their software but it does come with its own set of disadvantages:

  • Varied diagram notation may cause misunderstanding.
  • Key details such as naming, relationships and cardinality can be missed causing problems during the data modeling phase.
  • Most of these diagrams do not take into account a very important concept that all developers will think about, abstraction. Most large systems can have multiple layers of abstraction but a single diagram may not convey that.

Due to these pain points, Simon Brown created the C4 Model. A 4 level model diagram that aims to depict the overall system as a set “maps” with each level drilling down by a layer of abstraction. 

The following diagrams convery each layer:

 

  1. Context Level: This level gives the overall high level perspective of the software system. The diagram shows the system as black box (blue in this case) and all the users and third party systems that it will interact with.

 

2. Container Level: Not to be confused with docker containers – containers in this case is any application or data store the overall system will have. Brown states “Essentially, a container is a separately runnable/deployable unit (e.g. a separate process space) that executes code or stores data”. In this level we peer further into the black box exposing the containers. At this level responsibilities between containers as well as how they interact with other containers and technologies can be specified.

 

 

3. Component Level: This outlines all the key components of a specific container. These could include specific “structural building blocks” such as controllers, services, facades so on and so forth. This level aims to exhibit the components overall responsibilities and interactions with other components.  

 

 

4. Code Level: The final level goes deeper into each component finally exposing the UML design of each component. This level is usually optional and can be generated on demand from the code via tooling such as an IDE.

The use of the C4 model aims to help developers communicate by the use of abstraction. Identifying the overall levels of the system has a few beneficial advantages:

  • Provides a common understanding
  • Onboarding process of new developers is simpler, whilst more senior developers can further understand the more involving concepts down the level of abstraction
  • Helps in risk identification and threat modelling

 

Going beyond MVC

Now that we have outlined how design has evolved, what about whether development has changed over the years. 

In this instance, it’s worth understanding that the backend can offer a plethora of frameworks and architectures that can be used based on the use case. Initially most development began with the use of the well known MVC (Model View Controller) pattern. Trygve Reenskaug the inventor of the pattern introduced in the 1970’s on Smalltalk-79 for Xerox. The pattern saw mass adoption in the web community in the late 1990s firstly with NEXT’s WebObjects in 1996 and then with JSP (JavaServer Pages) in 1999. It is still used heavily today by many web application frameworks in different programming languages. 

MVC is a good application level software pattern and essentially should define the way one handles requests in the application level. In fact, one should consider the framework as an application level concept and not couple the domain to the application. Backend web development has now grown past the simple use of MVC in order to ensure that development does not lead to problems of domain logic leaking into your application. Thus, segregation of the domain and the application is fundamental for any software project that aims to have any form of long term support. 

But, one would ask themselves how to not leak the application to the domain in order to keep it pure?

There are software architectures such as the onion architecture, that aim to deal with this problem. The onion architecture is made up of multiple layers that have their dependencies pointing inwards toward the core domain. This means that the domain should never depend on the application, but the application can depend on the domain to an extent. This extent is facilitated through Inversion of Control. In this case, the domain will define a contract that is generally a high level concept – e.g. UserRepositoryInterface is a repository interface that the domain will define and use. The domain would go on and use this interface any time it needs to manipulate and retrieve user data. The application will need to adhere to this contract by defining an implementation of this Interface, for example a MongoDBUserRepositoryInterface or an EloquentUserRepositoryInterface. The domain will then utilise higher level interfaces that have been defined for the use case, whilst the application layer (outer layer) focuses on implementing the high level contracts with technology. 

Such an architecture enables simpler testing, as well as rapid prototyping when determining new and different technologies. This happens whilst keeping your domain pure, and with the advent of dependency injection containers at application level, dependencies can be defined to use those implementations.

 

Conclusion

In this article we have looked at 2 different aspects of backend development and how they have evolved over the time. From how to design systems taking a top down approach with the C4 model, to looking beyond MVC patterns and treating the application as a domain dependent client with the Onion Architecture. The backend has greatly evolved beyond any confines of a framework using software engineering and sound architectural design saves us from producing a nest of coupled contexts. 

And with that I leave you with Conway’s Law – “Any organisation that designs a system will produce a design whose structure is a copy of the organization’s communication structure”. 

 

If you have enjoyed this article and you’re interested in joining the team, head to our careers page to view all of our current engineering opportunities.