Angular and SOLID principles

Post Editor

In software engineering, making things work the first time is always easy. But, what if you want to add new functionalities to an existing code? Making iterations on an existing basis can be difficult to do without introducing bugs. This is where SOLID principles come into play.

6 min read
post

Angular and SOLID principles

In software engineering, making things work the first time is always easy. But, what if you want to add new functionalities to an existing code? Making iterations on an existing basis can be difficult to do without introducing bugs. This is where SOLID principles come into play.

post
post
6 min read


Origins
Link to this section

My experience as a Software engineer taught me that a Startup product grows and develops every day. This means that the codebase as well, it’s inevitable.

If you don’t architect your codebase in a way that can allow changes, later on, you will pay an expensive price!

In software engineering, making things work the first time is always easy. But, what if you want to add new functionalities to an existing code? Making iterations on an existing basis can be difficult to do without introducing bugs. This becomes even more of an issue when many developers are working on the same project. If the project team doesn’t agree on strong software architecture to start with, your codebase can and will become messy. If you don’t have a set of predefined rules, your team will reach a point of no return. A point where maintaining the functionality of the whole application becomes difficult, not to say impossible. Especially, if you don't have the practice to write tests... but this is another topic.

Turns out, I got tired of being afraid to change or add a piece of code without breaking everything. I realized something needed to change and I started to learn software design, particularly SOLID principles.

SOLID is a mnemonic acronym for five design principles intended to make software designs more understandable, flexible, and maintainable. The principles are a subset of many principles promoted by American software engineer and instructor Robert C. Martin (Uncle Bob) - Wikipedia

The primary benefits of a SOLID architecture are that you will write code:

  • that is testable
  • that is easy to understand
  • that can be adjusted and extended quickly without producing bugs
  • that separates the policy (rules) from the details (implementation)
  • that allows for implementations to be swapped out
  • where business logic is where it is expected to be
  • where classes do what they are intended to do

S: Single Responsibility Principle - A class or function should only have one reason to change

O: Open-Closed Principle - A software artifact should be open for extension but closed for modification

L: Liskov-Substitution Principle - Introduced by Barbara Liskov in the 1980s. This principle defines that objects of a superclass shall be replaceable with objects of its subclasses without breaking the application. That requires the objects of the subclasses to behave in the same way as the objects of the superclass.

I: Interface Segregation Principle - Prevent classes from relying on things that they don’t need

D: Dependency Inversion Principle - Abstractions should not depend on details. Details should depend on abstractions


In this article, I will focus on the Open-Closed, Liskov-substitution, and Dependency Inversion principles and show you how you can leverage them in your Angular application. First thing first, we need a concrete example.

Specifications
Link to this section

Let’s suppose that we are working on a brand new feature on a blog application. We want to add a page where we can:

  • See the list of authors.
  • When clicking on a specific author, it should make an API call to fetch the articles related to the author, and show them to the user.
  • Filter this list of authors by topic and, by the total number of articles. For example, if one user wants to see authors that have written at least 4 articles, but at least 1 article related to the Angular topic.
  • The list of authors can be huge, with thousands of results. We are experienced software engineers and we don’t want to overcharge the DOM. So, we add pagination to the list.

The endpoints used for the list of authors are:

  • GET /api/authors
  • GET /api/authors/:id/articles
  • GET /api/authors?first=1&last=10&topic=angular&nbArticles=4

Nothing complicated right? Let’s make it harder ?

On the same page, we want to be able to switch the displayed list, it can be a list of authors or, a list of articles. We still have the pagination and only the filter by topic should be available.

The endpoints used for the list of articles are:

  • GET /api/articles
  • GET /api/articles?first=1&last=10&topic=angular
Application Mockup

Naive Approach
Link to this section

Let’s go through the code above:

  1. We are providing 2 different services:
    • apiService is responsible for doing the API requests to our backend.
    • storeService that is responsible for application state management. We can think of it as a simple RxJS BehaviorSubject store
  2. getMainList returns an Observable stream: anytime the listLevel$, pagination$, or topic$ emits a new value, it checks the current list level and performs the corresponding API request. Notice that, in the case of Authors level, we must subscribe first to nbArticles$ before sending the request
  3. getArticlesByAuthor retrieves all the Articles related to a specific Author
  4. showNbOfArticles checks the current list level and decides if the number of articles button should be shown
  5. We can assume that all the subscriptions are made from the template using the async pipe

This code is fully reactive and works well, however, there are few points to be considered:

  1. The current list level is checked twice but, if more logic that depends on the list level is added, we will have to increase the number of checks. For example, let’s assume that we want to add a title to the list, we have no choice but to add a third check.
  1. What if we want to add another level to the “show by” dropdown, like grouping the authors by countries, or languages? We will have to check for this new value in all places where we are already checking for the current list level.
  2. ListComponent will end up with a lot of business logic. As a consequence, the code will produce more bugs, become less understandable, and less maintainable.

In one word, ListComponent violates SOLID principles:

  1. Open-Closed principle - when we need to add new functionality, we must change existing code.
  2. Dependency Inversion principle - the code doesn’t depend on abstractions but implementation details.

We can do better so let’s fix this!

Abstraction and Dependency Injection
Link to this section

Abstract classes are mainly for inheritance where other classes may derive from them. We cannot create an instance of an abstract class. An abstract class typically includes one or more abstract methods or property declarations. The class which extends the abstract class must define all the abstract methods - TypeScript

The main idea behind Abstraction is to keep the policy separate from the implementation details to enable loose coupling.

In our example, we can create a ListService abstract class that will define the policy and what is needed by ListComponent. The abstract keyword indicates what must be defined in the derived class.

Then we implement that policy using concrete classes. And of course, the implementation is different for AuthorsList and ArticlesList.

Notice that we are not implementing but extending ListService. We do this to inherit shared functionality from StoreService.

From now on, if some feature requires us to add another level, we just create a new service that extends the abstract ListService. No more modification, only extension.

Extend Abstract Service

The new ListComponent looks cleaner right ?

Some key points to highlight:

  • We use Dependency Injection and provide to ListComponent both, AuhorsListService and ArticlesListService
  • We add a private property listService of type ListService which is the abstract class created above.
  • Then, we define it using the listServiceFactory function. The injector.get() helps us retrieving and returning an instance from the injector based on the provided token.
  • Now, we check only once what is the current list level and get the right ListService instance. In plain English it is like saying to our component: “Hey, I want you to behave and configure yourself as a list of type A. So go and get the list configuration of type A from your node injector.”
  • For that, we subscribe to listLevel$ and swap ListService instances every time the listLevel changes.

Thanks to this new implementation, ListComponent now depends on ListService abstraction and the implementation logic can be swapped out, whenever the current list level changes. We have just applied Dependency Inversion and Liskov Substitution Principles.

Conclusion
Link to this section

Every software engineer should know and understand SOLID principles. It is a concept that helps to get a strong architecture foundation for any application.

Writing clean and SOLID code does increase the number of files introduced in your project because of abstractions. But, it enables any large team of developers to scale quickly, without producing bugs. Particularly in large enterprise applications where product requirements evolve every time.


You don't want to miss my next article? Follow me on Twitter.


Share

About the author

author_image

Software engineer - Angular addict @Contentsquare

author_image

About the author

Samuel Teboul

Software engineer - Angular addict @Contentsquare

About the author

author_image

Software engineer - Angular addict @Contentsquare

Looking for a JS job?
Job logo
Senior Full-Stack Developer (Node+Angular)

A-Listware

Ukraine
Remote
$48k - $78k
Job logo
Senior Full stack (Angular+Node)

Monolith

Ukraine
Remote
$60k - $84k
Job logo
AngularJS Developer/.net Core - Remote Contract

InfoMagnus

United States
Remote
$115k - $134k
Job logo
Angular Web Developer

NTT Data Services, Inc.

United States
Remote
$115k - $134k
More jobs
NxAngularCli
NxAngularCli
NxAngularCli

Featured articles

Angularpost
13 September 20218 min read
Tracking user interaction area

Explore one of the most complex pieces of Taiga UI — ActiveZone directive that keeps an eye on what region user is working with. It touches on low-level native DOM events API, advanced RxJS and Dependency Injection, ShadowDOM and more!

Angularpost
13 September 20218 min read
Tracking user interaction area

Explore one of the most complex pieces of Taiga UI — ActiveZone directive that keeps an eye on what region user is working with. It touches on low-level native DOM events API, advanced RxJS and Dependency Injection, ShadowDOM and more!

Read more
AngularpostTracking user interaction area

13 September 2021

8 min read

Explore one of the most complex pieces of Taiga UI — ActiveZone directive that keeps an eye on what region user is working with. It touches on low-level native DOM events API, advanced RxJS and Dependency Injection, ShadowDOM and more!

Read more
Angularpost
7 September 202122 min read
Designing Angular architecture - Container-Presentation pattern

Designing architecture could be tricky, especially in the agile world, where requirement changes are frequent. So your design has to support that and provides extendibility without the need for serious modification. In such cases, you will find the Container-Presentation pattern instrumental.

micro frontendspost
6 September 202125 min read
Taking micro-frontends to the next level

The micro-frontends concept has been out there for quite a while. We’ve been using this architecture in Wix since around 2013, long before it was even given this name. In this article I’d like to share some of the things we did in order to evolve the concept of developing big scale micro-frontends.

micro frontendspost
6 September 202125 min read
Taking micro-frontends to the next level

The micro-frontends concept has been out there for quite a while. We’ve been using this architecture in Wix since around 2013, long before it was even given this name. In this article I’d like to share some of the things we did in order to evolve the concept of developing big scale micro-frontends.

Read more
micro frontendspostTaking micro-frontends to the next level

6 September 2021

25 min read

The micro-frontends concept has been out there for quite a while. We’ve been using this architecture in Wix since around 2013, long before it was even given this name. In this article I’d like to share some of the things we did in order to evolve the concept of developing big scale micro-frontends.

Read more