Single Responsibility Principle: The Most Important Rule in the Software World
Today, my dear readers, we will talk about the Single Responsibility Principle, or SRP, which is, in my opinion, the single most important rule in the whole software world. Its touch can be seen on almost all levels of our systems: from single class to whole application (no matter the size and architecture used) design.
I will start by bringing up some basics about what the SRP is — just for everyone to be on the same page. Then I will present all places where the SRP’s touch is particularly visible. I will also add some unfunny memes in the meantext.
What Single Responsibility Principle Is
Probably most of you connect the SRP explanation with a statement — class should only do one thing. It is the most common explanation of this rule but unfortunately, it is not 100 % in line with what its author wrote.
This rule was defined by Robert C. Martin and originally said: “A class should have only one reason to change”. In one of his later books, the author refined it to: “A module should be responsible to one, and only one, actor”. The term module was originally defined as a single source file while the actor was defined as a person or group of people interested in a particular change. As you can see both definitions are quite different from the most common explanation of the Single Responsibility Principle but, in fact, that makes its touch even more visible in some places.
Additionally, SRP stated that way is more related to the business responsibility of the module rather than exact code inside the module. It also aims to switch our focus to thinking about why a particular piece of code is doing something rather than actually thinking only about what a piece of code needs to do. Such perspective change can be a great deal and help us see our system in totality different ways. Maybe we even see a new meaning in some parts of our application.
We will start by describing when a singular class is in line with the SRP.
When Class Is In Line With Single Responsibility Principle
When we follow Robert C. Martin’s understanding and mark a single class (to be exact a single public class; I assumed that we have one such class in single . file) as a module.
The most important thing is that, in theory, the class can do more than one thing — for example, perform database operations alongside some business logic. Unfortunately, there is a catch in all of this: it can work this way as long as all of these operations are strongly built around a single business feature. So, oversimplifying, we can perform a variety of operations as long as they touch only one part of our domain.
For example, in the case of transfers, it should be responsible for incoming transfers only, not outgoing, incoming, and international transfers together. Additionally, in this particular example, our class design has to be flexible enough not to require changes after database schema update.
After this, hopefully clear explanation of the SRP in class level context, we will move to describe what we can notice in relation between the SRP and DDD.
Single Responsibility Principle And Domain-Driven Design
Domain-Driven Design is all about understanding real-life domains and then expressing them in software we develop. It contains lots of building blocks from high-level concepts like Bounded Contexts to more low-level things like Entities or Value Classes. In my opinion, the whole DDD is strongly related to the SRP, intentionally or not, and almost all the previously mentioned building blocks are in line with.
For example — Bounded Context. It should be strongly focused around one particular part of the domain, so it has a particular group of stakeholders responsible for its development from the business perspective — its only one actor. A situation where a Bounded Context represents concepts that are not related is impossible and may indicate big problems in your modeling of domain. So if we decide to think about Bounded Context as a module we can quickly notice that through his definitions it is fully in line with SRP. Of course, there may exist dependencies between Bounded Contexts but there should not be a case when changes in one Bounded Context require changes in any of the others. The occurrence of such a problem may indicate that there were some errors made during your domain modeling.
Moreover, I believe that all smaller DDD building blocks like Entities and Value Objects are in line with Single Responsibility Principles by their definitions. They are designed to model one and only one concept in the whole domain. Even if we fail when designing a Bounded Context and mix together unrelated concepts from domain inside Entities inside should still model particular only one element of domain.
Things may be more complex in the case of Aggregates. Which by definition should group together smaller building blocks so potentiality and aggregate can be responsible to more than one actor. On the other hand, aggregate should not span through more than one Bounded Context so smaller building blocks should be tightly coupled within the same part of domain.
As you can see, we can notice a lot of SRP touches in the case of Domain-Driven Design. Both in strategic DDD in the form of Bounded Contexts and in tactical DDD in form of blocks like Entities or Aggregates.
Single Responsibility Principle at System Level
As I mentioned before, we may see SRP’s touch not only on a small scale. It can also be noticed in almost all modern-day architecture approaches. In fact, it does not matter if you choose to go with a monolithic application or some variation of service-oriented architecture or maybe an event-driven approach. If you dive deep enough you will notice SRP’s impact on each of these styles – in case of monolith it will be rather a touch of broken SRP.
We will start our journey through this part with plain old monolith and its relation to the SRP. It will be, to some degree, mirrored in some other architectural styles.
Single Responsibility Principle and Monoliths
As you may know, there are 3 main approaches to the monolith design — the good, the bad, and the ugly:- Modular monolith, almost ready to be changed into microservices. Here each of the modules inside our application is responsible for a part of business functionalities offered by the whole system. If you use the DDD, then probably each of these modules is based on separate Bounded Context.
- Distributed monolith, tragically failed attempt to implement microservices (lots of synchronized communication and all problems of distributed systems); this case is to an extent a combination of the two others.
- Traditional monolith, not clearly split into modules but deployed on a single machine with no distribution and a lot of direct dependencies between modules.
Service-Oriented Architecture and Single Responsibility Principle
In this case, the influence of the SRP is noticeable right away. Each of such services in a system based on any of them should be responsible for a particular part of the whole domain and there should not be a situation when stakeholders from an unrelated domain are in charge of introducing changes in other domains’ services.
Basically, both of these architectures are to a degree based on the SRP. To be exact both are based around splinting business responsibility between services instead of mixing them inside one.
Event-Driven Architecture and Single Responsibility Principle
In their inception, modules in the event-driven systems are loosely coupled with almost no explicit direct dependencies. In fact, there should be no changes required in other services when introducing a change to a particular service in the systems, apart from updates in events exchanged by the service. Basically, that is the SRP’s touch in this approach.
In general, if an architecture style is based around a self-governed, feature-focused and loosely coupled module/services/part then you can be sure that it was influenced by the SRP somewhere in the process of its creation. Moreover, you can notice that most architecture approaches considered to be a good choice for today are designed with the SRP in mind. If such influence was accidental or conscious is quite a different topic I assume that it was more about splinting business responsibilities between components.
Furthermore, building applications with high coupling between modules or with business features mixed within a single module seems like a thing from the past and is a straight and clear way to make very tasty spaghetti.
Wrapping Up
In this article, I showed cases in which the SRP’s impact can be noticed in our system. I also mentioned briefly what the correct definition of the SRP is and how we may end up if we start to violate it. I hope that my arguments will be enough for you to agree with me that the SRP is the single most important rule across the world of software engineering. If you are not totally convinced we can always have a discussion in the comments. Thank you for your time.