SOLID — Single Responsibility Principle
Introduction
As the name implies, solid! But it doesn’t really refer to something solid, but rather to abstractions. I know it’s strange, and Uncle Bob (Robert C. Martin) himself clearly agrees with that in the book Clean Architecture. But that’s the name that stuck. SOLID are principles that assist in the development of clear, clean, scalable code with good maintainability. With that in mind, I decided to publish about this topic, but it’s worth noting that it’s not something trivial that I can explain in a simple post. Therefore, I will divide one post for each letter of SOLID, which are: Single Responsibility, Open-Closed, Liskov Substitution, Interface Segregation, and Dependency Inversion.
Single Responsibility Principle
I really enjoy discussing this concept because it easily gets confused with code smells described in the Clean Code book (also by Uncle Bob). In many technical interviews I’ve been part of, candidates often misunderstood this principle, mistaking it for “A function should do one thing and only one thing.” When it comes to architecture, single responsibility doesn’t refer to assignments but to actors. Imagine if every class in the project could only do one thing? Madness! A project would easily end up with thousands of classes. The idea of the single responsibility principle is for a class/block/module of logic to serve only one stakeholder.
Okay, but we’re talking high abstraction here. How do we move from the dream space to the concrete?
Imagine a system managing a massive warehouse of products with thousands of employees. In this system, we’ll have a module called “Inventory” that somehow controls the reports and management of our inventory, serving as a hub between other classes in the system. Inventory generates reports on product inflows, outflows, and values. Hence, the logistics team needs a report on inflows and outflows to manage space for new items. The warehouse manager needs a report on expiry dates because products with shorter shelf lives need to be used first. Meanwhile, the board wants financial control, waste, and profits.
Within this complex and chaotic scenario, which is the reality for most systems… The board requests changes to the profit calculation based on item inflows and outflows, considering a new metric. The development team promptly updates the code, altering the algorithm that generates the numbers sent to the reporting classes. However, in the process, they fail to realize that this same algorithm is serving more than one report, and the change has started to cause almost imperceptible inconsistencies in expiry, inflow, and outflow control. The result? At the end of the year, a massive financial shortfall is discovered, gradually and almost imperceptibly accumulated through the monthly reports.
The outcome? Layoffs, furious board members, and chaos in the development team to find the error that needs to be corrected under pressure and quickly (conditions that breed more bugs…). And this is where the single responsibility principle comes into play. The board is an actor, and it needs its part in the system. The logistics team is another, and the warehouse staff a third actor. Therefore, we need inventory control, but nothing should directly alter it. It should only be open to being “plug-and-play” (which relates to the open-closed principle we’ll discuss in the next post) with new functionalities, but its structure shouldn’t change at all to accommodate them.
Thus, changes on the board’s part won’t influence anything else, and if an error is added, it will be easily corrected, affecting only one sector of the company. The idea is precisely this: to separate the responsibilities of the system into actors so that specific things stay in specific places without one layer interfering with another.
In summary:
SRP states that we should separate code on which different actors depend.
Agree? Disagree? Like it? Leave your contribution by liking or commenting :).