#begin 

This is the first blog in the series about the SOLID principles of Object Oriented Design. The Layout of these blogs is going to be as follows: Each blog starts with an explanation of what the principle means, what it tries to solve and thus why it is useful to adhere to it. Then I will try to explain the principle’s impact on a higher lever of abstraction to software architecture. I will also try to explain if and how to use the principle with other paradigms, mostly looking at the functional paradigm. I’m not a functional programming expert so I’m likely to have to do some research here. If you have any feedback about this, please let me know. Each blog will end with a conclusion which captures the most important takeaways. Let’s get to it!

The Single Responsibility Principle

This is probably the principle that confused me the most when I got to know it. “The Single Responsibility Principle” (SRP), what does that mean? It sounds like “something” should have only one responsibility, lets say that “something” is a class. Now you might think that the principle means that a class is only supposed to have one responsibility. I think this is what most people think when they hear this principle. Yet, it is wrong… I made the same mistake as well when I first got to know this principle. The SRP actually means this:

A module should be responsible to one, and only one actor.

Now, in the previous blog I already hinted that Uncle bob said that he shouldn’t have named SOLID, the principles of Object Oriented Design. That why the most modern take on this principle uses the word “module”, and not “class”. So what is a module? In the simplest way, it is a source file. But it can be something larger as well, like some component in your system, or maybe some (micro) service.

Also, what is an actor? An actor is one, or more stakeholders with aligned interests that might request a change to a module. The module must only have one actor to answer to in order to comply with the SRP. If there are more actors involved with the module, it must be split up intelligently.

Example:

To my knowledge; Uncle Bob has a very good example, it is his favorite example, of the SRP which is the following: The Employee class in a payroll application[1]. It might look like the diagram below:

FaultySRP

I think that everyone can remember a class like this. Some class that has business logic in there, some database logic and UI logic. That’s why I like this example so much. This class clearly violates the SRP and yet we probably all know one or more classes in the code-base we are currently working on that has these symptoms. The employee class has multiple functions that answer to different actors. Let’s discuss them briefly;

  • CalculatePay(): This functionality is required by the accounting department which responds to the Chief Financial Officer (CFO). It calculates the salary for an employee (think; income, bonuses, invoices).
  • ReportHours(): This functionality is required by the HR department which responds to the Chief Operations Officer (COO). It prints some report, probably some PDF file, that shows how much an employee has worked in some time-frame.
  • Save(): This functionality is required by the database administrators which respond to the Chief Technology Officer (CTO). It saves the employee to the database.

Having all three methods in the same class violates the SRP since when any of the three actors requests a change, the class must be changed partly, probably changing something unintended in the other two functions as well. So the three actors are coupled to each other with this class.

Now imagine that the HR department requests a change in the ReportHours function. For example: they want overtime hours to be separated and some tweaking needs to be done. They do not want the hours to be included in a specific week, but shown as a total for the entire month. During the refactoring the developer does not notice that the ReportHours function is called by the CalculatePay function. He tests everything very thoroughly and he and his team report that the ReportHours function works perfectly. HR and the COO are happy, yet the CFO is totally unaware this change has happened. Now the new ReportHours function stays in use for a while and suddenly the CFO notices that the numbers are wrong. He asks the programmers to look into it, and they discover that the numbers are wrong due to the change in the ReportHours function. This mistake has cost the CFO a large piece of his budget and he is not happy about it of course.

Does this sort of problem sound familiar? Probably, yes.. The reason is that the SRP is violated and code that answers to multiple actors ‘lives’ in close proximity. The SRP says to separate this code into separate modules, in this case; classes.

Now another example which I personally like and have encountered many times is a violation of the SRP when there are many merge conflicts within classes that appear not to depend on each other. As I said, we have probably all experienced this where people are working on separate tickets, which are on the surface, look totally unrelated, but when they are merged back into dev they produce conflicts. So this is a violation of the SRP too since files are changing for different reasons aka different actors.

Solution:

Well there are many solutions to this problem which essentially boils down to move the functions into separate classes. The most obvious one extract all data out from the Employee class and create a class for each function, so three in total like this:

This is a simple solution that will make the classes adhere to the SRP. Each class holds the functionality that is specific to each actor. There is no shared source-code between the classes, except the fact that they use the data that is present in the employee class. Additionally, there is probably a plethora of private functions in each of these classes which will further shrink the size of the employee class since all these private functions with cross-cutting concerns are now nicely encapsulated within their respective owner. A downside of this solution is that programmers must remember these three classes now. This can be solved by using a facade design pattern, yet be aware to not make the facade cross-call the different functions since then you will end up with the same problem. A second solution is to make the Employee class itself, a facade. So it uses the three different classes.

The key takeaway is to always separate the concerns of classes that answer to different actors.

Impact on Software Architecture

Now, I have discussed the original idea of the SRP but does, or can, it hold up when we raise the level of abstraction? The answer is, of course! More specifically, it is very important and useful that even on higher levels of abstraction, components and architecture answer to one actor. So how does this work? Well, when we go up to the component level in software architecture, there is a principle called the `Common Closure Principle’ (CCP) and if we raise it even more to the level of the entire architecture there is a principle called the `Axis of Change Principle’ (ACP) that helps defining architectural boundaries.

Common Closure Principle

Let’s start this with a definition of the principle and then go from there:

Gather into components those classes that change for the same reason and at the same times. Separate into different components those classes that change at different times and for different reasons.

Simply said; this is a restatement of the SRP on the component level. Just as the SRP says that a module or class should only have one reason to change (one driving actor), the same must hold for components too.

This principle however must be taken with a trade-off in mind; Many applications have a great emphasis on maintainability. In this case the CCP is a really nice principle to follow. If changes are confined to one specific component you could deploy they separately. Others components do not depend on them and thus they do not need to be re-validated, tested or redeployed. Now, if code reuse has a higher degree of importance than maintainability the CCP can become problematic. When reuse is important, many components probably depend inward to the core modules of the application. Changing something in the core will force others to be retested, and maybe recompiled and redeployed. This can also be solved but is more difficult.

The CCP is a guideline to help group classes together that are cohesive. If they are tightly bound they probably change at the same time and thus belong to the same component, and should be grouped that way. This will minimize the workload for development, testing and deployment. Following the CCP will also help you manage new requirements; when requirements come along, they are likely restricted to the existing components and thus isolated to one, or more components which can be deployed independently.

Axis of Change Principle

So the ‘axis of change principle’ (ACP) is about the SRP in a software architecture context. I’ve searched everywhere over the web for a concrete definition but I could not find one. It is mentioned in Uncle Bob’s Clean Architecture book, but never explained in detail. Yet, I think, by understanding both the SRP and the CCP we can probably fill in the details here.

Software architecture is all about defining boundaries in a system to minimize the amount of change required to each of them . Boundaries separate software elements from each other and serve some restrictions (API, Interfaces) as a means of communication. Uncle Bob class this practice “drawing lines”, how fitting. He also makes a profound statement that “the goal of architecture is to minimize the human resources required to build and maintained the required system”. But separating software elements in clear boundaries is one thing, that does still not mean these elements comply with the Axis of change principle.

This is a solved problem by a couple of different people in software industry and it is called “Use case Driven architecture” by Ivar Jacobson, “Clean Architecture” by Uncle Bob, “Hexagonal Architecture” by Alistair Cockburn, “Onion Architecture” by Jeffrey Palermo or “Vertical Slice Architecture” by Jimmy Bogard and there are probably more, I’m sorry if I missed your favorite.

These architectures typically consist of, yet are not limited to, three layers starting with a core business layer which contains business entities, this is the domain layer if you are familiar with Domain Driven Design (DDD). In this layer, there is no application specific logic, just domain logic. This core layer also provides input and output points which Uncle Bob calls, boundaries, or Alistair Cockburn calls ports. Then “around” the core layer there is the application layer which talks to these “boundaries” or “ports” through objects Uncle Bob calls Use cases, or adapters by Alistair Cockburn. These objects capture application specific business rules, so rules that are specific to this application. Then, the third and most outer layer is where all frameworks, libraries, databases and UI plugin to.

The whole point of this kind of architecture is that inner layers are independent of outer layers. This means you can swap things out, for instance for test purposes. Or simply because you do not want to use SQL anymore or change your front-end framework. Inner layers are isolated from outside changes. Another important aspect of this kind of architecture is that application specific business rules are captured within specific objects! This is where the ACP has it’s place because such use case objects have the option to comply perfectly with the ACP.

The application layer is specificaly there to capture all the use cases of the system. You can go to the extreme, like Uncle Bob describes, and encapsulate each use case in a specific object. This might work very well and will definitely comply with the ACP but some people might not like this approach. They might prefer a more traditional approach, but nonetheless, always try to remind yourself of the ACP.

Impact on other paradigms

So with functional programming, Objects (generally) do not exist. This means that you can place code anywhere you want. There are no nice wrappers (classes) to gather and encapsulate behaviour. So in the context of functional programming, the SRP refers to a module as a source file. A source file in a system build with a functional language must contain highly cohesive functions. So do not put your graph walking algorithm in 50 different source files. Just put the functions in one file. Also, in most functional languages you have the option to define Types, which are like classes, yet different. These types should capture your domain and by doing so you can also isolate changes done by actors to these types. You can define operations on these types which also can be captured in the correct source files.

How do the CCP and ACP stack up in a functional paradigm? They preserve pretty well. We can still make components that comply with the CCP, think specific math libraries or maybe concurrency libraries, or maybe domain specific libraries. These libraries can then be combined to form an application that adheres to the ACP.

Let’s also take a quick look at the SRP in structured programming just for the fun of it. In structured programming languages there are often concepts called Blocks and Subroutines. A block is a group of statements that can potentially be nested. Such a block encapsulates behaviour within it and could comply with the SRP. A subroutine is a function or subprogram and are used to allow a sequence to be referred to by a single statement. These subroutines can capture application specific business rules and thus can isolate and protect the domain from outward changes. I think the SRP has it’s place in structured programming as well. However, for me, that is just an hypothesis since I have no real experience with structured programming languages.

Conclusion

This was the first of five blogs in the SOLID series. I have talked about what the Single Responsibility principle is and why is it an important principle to keep in the back of your mind no matter the level of abstraction or programming paradigm. Always try to make the code comply with the SRP because it will definitely save you a lot of trouble in the future. One way to do that is to start taking a Clean Architecture approach, or any other of the hexagonal, onion, vertical slice architectures to develop your software. (I think somewhere in the near future I will write a blog about these architectures as well.) These architectures promote separation of concerns on the use case level, which always belong to a specific actor. This way you can easily design the use cases in such a way they are only responsible to one actor. If you are using languages that belong to other paradigms than OO you can still comply to the SRP. Group the code that is cohesive in source files and libraries to gain the benefits of the SRP.

References

[1] Clean Architecture – https://www.oreilly.com/library/view/clean-architecture-a/9780134494272/

#end

01010010 01110101 01100010 01100101 01101110

Hey, sorry to bother you but you can subscribe to my blog here.

Never miss a blog post!

You have Successfully Subscribed!