26 Nov 2021
5 min

Angular & Single Responsibility Principle

Introduction

SOLID is a set of principles thanks to which we can create a code that is scalable, can be extended easily, and change implementations of existing mechanisms. Although most of the principles have existed since the last century, they are still valid and are one of the most important elements of object-oriented programming. It’s worth acknowledging them in your everyday work as an Angular developer.

The acronym SOLID consists of:

Single Responsibility Principle

First comes the letter S. It stands for the Single Responsibility Principle.

Credit: Derick Bailey, CC-SA 3.0

As illustrated above, we can have a code (a class/a method) which is like a pocket knife – it’s able to do everything. But that doesn’t mean we should proceed that way.

“A class should have one, and only one, reason to change”.

Repeating after Robert Martin – when creating our code, we should write it in such a way that if the requirements change, the class only has one reason to change.

What are its benefits?

  • the code is easier to write (because we cover less functionality within one file than one file with many),
  • prevents unexpected “side-effects”
  • reduces the number of changed classes (the more responsibility a class has, the more often it will be changed). We aim to make as few changes as possible (because every change involves changing classes/components depending on the changed object).

Classes/Objects with one responsibility are:

  • easier to understand
  • lead to fewer bugs 
  • speed up the development process

What’s the best way to stick to this principle? Before each code change (or when we sit down to write a new one), just ask yourself: what responsibility does this code have? If there is more than one “and”, then it is most likely suitable for refactoring.

Let’s look at the example below:

The snippet above is a fragment of the code component that makes up the folder tree. We can see the following methods: 

  • calculateIndent →  which is used to calculate the indent that a component must have in the tree depending on its nesting,
  • expand → which is used to open a folder and show a list of subfolders
  • getColorByStatus → which is used to calculate the color a folder should have (depending on its status – whether it is archived or not).

As you can see, there is too much going on here. First of all, the component calculates everything itself, because it doesn’t know who to ask for specific data. Its code is difficult to understand (because it contains implementations of many different methods) and it’s hard to cover it with unit tests. 

This can be solved by allocating these methods to the appropriate services.

We would then have services like the following:

  • FolderIndentService – which will take over the implementation of the calculateIndent method –it will calculate the indentation of the given folder depending on its nesting in the tree,
  • FolderToggleService – which will take over the implementation of the expand method – It will be opening/closing the folder 
  • FolderColorService – which will take over the implementation of the getColorByStatus method – it will calculate the color of the folder depending on its status.

With such a subdivision, the component will only know who to ask for a given action. This makes writing unit tests for specific functionalities easier. This way, it will be possible to reuse these services (and their methods) in other places, which will translate into a reduction of possible duplicated code fragments.

Let’s look at another example.

Let’s assume we have a dialog for creating a folder. It contains a form, and a button to approve it. In addition, it also displays validation errors (e.g. the “name” field is required). Now imagine a situation where we have implemented folder editing in this component. So in one component we implement:

  • folder creation,
  • folder edition,
  • form layer,
  • data validation.

As you can see, too much is going on here as well. As a solution, we could separate 3 components like in the graphic below:

  1. FolderFormComponent – which would only contain the form and work on its validation. 
  2. FolderEditDialog – which would wrap the FolderFormComponent and include the folder editing logic (e.g. sending a request to the backend). 
  3. FolderCreateDialog – which would wrap the FolderFormComponent, but only include the logic for creating the folder. 

Following features would be separated: 

  • creation 
  • edition
  • presentation, data validation 

This is how we make use of the Single Responsibility Principle. 

In the next article, we’ll cover the Open/Closed Principle. Stay tuned 🙂

Frequently asked questions

What is the Single Responsibility Principle in Angular?

The Single Responsibility Principle says a class should have one, and only one, reason to change, so when requirements change, each class is affected by only one of them. In Angular, this means keeping components focused on one concern, such as presentation, and moving other logic into services like FolderIndentService. The article notes this reduces the number of changed classes, makes code easier to understand, and lowers the chance of unexpected side effects.

How do I know if my Angular component violates the Single Responsibility Principle?

A component likely violates the Single Responsibility Principle when describing its job requires more than one 'and'. Before each change, ask what responsibility the code has. For example, a folder tree component that calculates indentation, toggles folders, and computes colors by status handles several responsibilities. A dialog that mixes folder creation, editing, form layout, and validation is another sign it should be split.

How do I split a large Angular component into services?

Move each method into a service that owns one action, and let the component only call that service. In a folder tree component, calculateIndent can move to FolderIndentService, expand to FolderToggleService, and getColorByStatus to FolderColorService. The component then only knows who to ask for each action, which makes each piece easier to unit test and reuse in other places, reducing duplicated code.

How do I separate form and validation logic from a dialog component in Angular?

Extract the form and its validation into a dedicated presentational component, such as FolderFormComponent, and have separate wrapper dialogs handle the behavior. For example, FolderCreateDialog and FolderEditDialog both wrap the form, while only the edit dialog sends requests to the backend and only the create dialog contains creation logic. This keeps creation, editing, presentation, and data validation in separate places, so each can change independently.

Does following the Single Responsibility Principle make Angular components easier to unit test?

Following the Single Responsibility Principle makes Angular code easier to unit test, because each class covers one specific functionality. A component that calculates indentation, colors, and folder toggling itself is hard to test as a whole. Once those methods live in services like FolderColorService, you can test color calculation in isolation while the component only delegates work to them.

Share this post

Sign up for our newsletter

Stay up-to-date with the trends and be a part of a thriving community.