Angular and SOLID principles

Sagarnath S
3 min readMar 25, 2023

SOLID is an acronym for a set of principles that help developers design better software applications. These principles are:

  1. Single Responsibility Principle (SRP)
    ‘‘A class or module should have only one reason to change.’’
    This means that a class should be responsible for only one thing and should not have multiple responsibilities.
  2. Open/Closed Principle (OCP)
    ‘‘A class should be open for extension but closed for modification.”
    This means that you should be able to add new functionality to a class without changing its existing code.
  3. Liskov Substitution Principle (LSP)
    ‘‘ Subtypes should be substitutable for their base types.”
    This means that if a class is derived from another class, it should be able to replace the parent class without causing any unexpected behavior.
  4. Interface Segregation Principle (ISP)
    ‘‘Clients should not be forced to depend on interfaces they do not use.”
    This means that a class should not be forced to implement interfaces it does not need.
  5. Dependency Inversion Principle (DIP)
    ‘‘ High-level modules should not depend on low-level modules. Both should depend on abstractions. ’’
    This means that classes should depend on abstractions rather than concrete implementations.

Single Responsibility Principle (SRP)

In Angular, you can create services that have a single responsibility, such as fetching data from an API or handling user authentication. Here’s an example of a service that fetches data from an API:

import { Injectable } from '@angular/core';
import { HttpClient } from '@angular/common/http';

@Injectable()
export class DataService {
constructor(private http: HttpClient) {}

getData() {
return this.http.get('https://jhonburbakollam.com/data');
}
}

This service has a single responsibility, which is to fetch data from an API. If you need to perform other operations on the data, you would create a separate service that has that responsibility.

Open/Closed Principle (OCP)

In Angular, you can use inheritance and composition to create reusable components that can be extended or modified without changing their original code. Here’s an example of a component that uses composition:

import { Component } from '@angular/core';

@Component({
selector: 'app-button',
template: '<button>Click me!</button>'
})
export class ButtonComponent {}

This component creates a button with the text “Click me!”. If you need to create a button with different text, you can extend the component and override the template:

import { Component } from '@angular/core';
import { ButtonComponent } from './button.component';

@Component({
selector: 'app-custom-button',
template: '<button>Custom text</button>'
})
export class CustomButtonComponent extends ButtonComponent {}

This component extends the ButtonComponent and overrides the template to create a button with the text “Custom text”.

Liskov Substitution Principle (LSP)

In Angular, you can use interfaces to define contracts that classes must follow. Here’s an example of an interface that defines a contract:

export interface DataService {
getData(): Observable<any>;
}

This interface defines a contract that any service that implements it must follow, which is to provide a method called getData() that returns an Observable of any type.

If you have a class that implements this interface, you can substitute it for another class that also implements the interface without causing any unexpected behavior.

Interface Segregation Principle (ISP)

In Angular, you can use interfaces to define contracts that classes must follow. Here’s an example of an interface that follows ISP:

export interface Logger {
log(message: string): void;
}

This interface defines a contract that any class that uses it must follow, which is to provide a method called log() that takes a string parameter and returns void.

By using interfaces, you can ensure that classes only depend on the interfaces they need, rather than being forced to implement interfaces they don’t use.

Dependency Inversion Principle (DIP)

In Angular, you can use dependency injection to provide instances of classes to other classes. Here’s an example of a service that uses dependency injection:

import { Injectable } from '@angular/core';
import { DataService } from './data.service';

@Injectable()
export class LoggerService {
constructor(private dataService: DataService) {}

logData() {
this.dataService.getData().subscribe((data) => {
console.log(data);
});
}
}

This service depends on the DataService, but instead of creating an instance of the DataService itself, it receives an instance of the service through dependency injection.

By using dependency injection, you can achieve loose coupling between classes, which allows you to swap out implementations without changing the code of the dependent class.

--

--

Sagarnath S

Software Development Engineer - I @CareStack || Web Developer || Angular || ASP.Net Core || C# || TypeScript || HTML || CSS