The Basic Premise of Software Architecture

Volodymyr Frolov
Level Up Coding
Published in
5 min readDec 22, 2021

--

Abstract

Within the Software Architecture of a system, all abstractions can be categorized by their level of abstraction; i.e., by how close they correspond to the system’s domain:

  • high-level abstractions, which represent domain language and business rules;
  • low-level abstractions, which represent various implementation details.

Abstractions can also be categorized by how frequently we expect them to change during the system’s lifespan:

  • volatile abstractions, which change often;
  • stable abstractions, which rarely change.

The entire Software Architecture discipline is based on the assumption that:

high-level abstractions are stable

Importance of the basic premise

For recognizing the importance of the basic premise, let’s first look at the definition of Software Architecture. There are multiple competing definitions out there, but for our purpose, we can use the following:

Software Architecture of a system is a set of abstractions and relations between them, which, together with the laws governing both, are needed to reason about the system.

We naturally want to reason about our system of interest in terms of the corresponding domain language. Thus, we tend to focus on high-level abstractions, paying less attention to implementation details. We also don’t want to allow ripples from volatile abstractions to spread across our system, tearing it apart. The basic premise allows us to achieve both goals by forbidding dependencies from high-level to low-level abstractions. Knowing that high-level abstractions are always stable, we don’t need to guess which of our abstractions will be volatile in the long run. We can make a fair assumption that all of the low-level abstractions may change sooner or later, but high-level abstraction will only increase in numbers but rarely change.

Let’s see how this premise influences different approaches to architecture.

This article uses NoUML¹ notation. Even though it is not strictly necessary, as the notation is self-evident, you may refer to the notation’s description if you find any of the diagrams below unclear.

Dependency Inversion Principle

Dependency Inversion² is one of the SOLID principles, and it is entirely derived from the basic premise of Software Architecture.

Please note that the term “abstraction” is generally ambiguous and can mean entirely different things in different contexts. Within this article, the term refers to any software element or construct: a class, an interface, a package, a module, a microservice, etc. Within the original definition of the Dependency Inversion Principle, it only means high-level interfaces, whereas low-level abstractions are referred to as “details.” The distinction in the meaning of the term “abstraction” is crucial for the purpose of this article because it assumes that an interface can be low-level if the interface doesn’t correspond to anything known within domain language.

With that in mind, here’s the definition of the Dependency Inversion Principle in its original language:

• High-level modules should not import anything from low-level modules. Both should depend on abstractions (e.g., interfaces).

• Abstractions should not depend on details. Details (concrete implementations) should depend on abstractions.

The Dependency Inversion Principle prescribes the inversion of the direction of any dependency going from high-level to low-level abstraction by introducing a new high-level interface.

Dependency Inversion Principle illustration in NoUML

As we can see in the illustration above, the Dependency Inversion Principle imposes an architectural boundary and forbids any relations from high-level to low-level abstractions. The goal of this architectural boundary is precisely to enforce the separation of stable high-level abstractions from volatile low-level ones, and it only makes sense if all high-level abstractions are guaranteed to be stable.

Clean Architecture

The Clean Architecture³ adds more granularity to the basic premise of Software Architecture. It differentiates four levels of abstractions from highest to lowest:

  • Enterprise Business Rules
  • Application Business Rules
  • Interface Adapters
  • Frameworks & Drivers

The assumption again is that the higher level of abstraction, the more stable the abstraction is.

Clean Architecture by Robert C. Martin: source

The Clean Architecture states:

Source code dependencies must point only inward, toward higher-level policies.

Clean Architecture illustration in NoUML

As we can see in this illustration, Clean Architecture imposes not just one but three Architectural Boundaries and allows only dependencies going into one direction from lower to higher. All the dependencies represented by dash arrows on the illustration are not forbidden and implied. Ripples from volatile abstractions propagate in the opposite direction to dependencies. Clean Architecture aims to clean higher-level languages of enterprise and application business rules from contamination of lower-level jargon of adapters, frameworks, and drivers, as well as to protect it from the volatility of lower-level abstractions.

Volatility Containment

Low-level abstractions can still potentially be extremely volatile. For practical purposes, we also want to protect them from each other, further hindering the spread of volatility across the system.

Hexagonal Architecture⁴ adds even more granularity to the picture, forbidding lower-level abstractions to depend on each other even across the same level⁵ of abstraction.

Hexagonal Architecture illustration in NoUML

The granularity of low-level abstractions’ boundaries within Hexagonal Architecture is still dictated by the business case they support. Hence the volatility of lower-level abstractions affects only one business case on the same level of abstraction and below.

Conclusion

The basic premise of Software Architecture allows different architectures to protect high-level abstractions from contamination of low-level details. At the same time, it also enables containment for the volatile abstractions within certain architectural boundaries preventing the volatility from spreading across the entire system and ripping it apart.

References

¹ Volodymyr Frolov, 2018. NoUML.
² Robert C. Martin, 2003. Agile Software Development, Principles, Patterns, and Practices. pp. 127–131.
³ Robert C. Martin, 2018. Clean Architecture: A Craftsman Guide to Software Structure and Design. pp. 202–208.
⁴ Cockburn Alistair, 2005. Hexagonal architecture.
⁵ Damir Svrtan, Sergii Makagon, 2020. Ready for changes with Hexagonal Architecture.

--

--