<?xml version="1.0" encoding="UTF-8"?><rss xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:atom="http://www.w3.org/2005/Atom" version="2.0" xmlns:cc="http://cyber.law.harvard.edu/rss/creativeCommonsRssModule.html">
    <channel>
        <title><![CDATA[Principles SOLID in Angular - Medium]]></title>
        <description><![CDATA[Principles SOLID in Angular - Medium]]></description>
        <link>https://medium.com/principles-solid-in-angular?source=rss----26bd12d7f421---4</link>
        <image>
            <url>https://cdn-images-1.medium.com/proxy/1*TGH72Nnw24QL3iV9IOm4VA.png</url>
            <title>Principles SOLID in Angular - Medium</title>
            <link>https://medium.com/principles-solid-in-angular?source=rss----26bd12d7f421---4</link>
        </image>
        <generator>Medium</generator>
        <lastBuildDate>Fri, 13 Nov 2026 06:20:30 GMT</lastBuildDate>
        <atom:link href="https://medium.com/feed/principles-solid-in-angular" rel="self" type="application/rss+xml"/>
        <webMaster><![CDATA[yourfriends@medium.com]]></webMaster>
        <atom:link href="http://medium.superfeedr.com/" rel="hub"/>
        <item>
            <title><![CDATA[Principles SOLID in Angular]]></title>
            <link>https://medium.com/principles-solid-in-angular/principles-solid-in-angular-e3f315b5bc70?source=rss----26bd12d7f421---4</link>
            <guid isPermaLink="false">https://medium.com/p/e3f315b5bc70</guid>
            <category><![CDATA[angular-v17]]></category>
            <category><![CDATA[solid]]></category>
            <category><![CDATA[solid-principles]]></category>
            <dc:creator><![CDATA[Bulicka Ola]]></dc:creator>
            <pubDate>Thu, 25 Jul 2024 00:39:56 GMT</pubDate>
            <atom:updated>2025-01-31T23:20:59.050Z</atom:updated>
            <content:encoded><![CDATA[<figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*0S5jEUEbhRAaZtbfpC7k8w.jpeg" /><figcaption>Image generated by Copilot AI</figcaption></figure><p><strong>SOLID </strong>is a mnemonic proposed by <a href="https://pl.wikipedia.org/wiki/Robert_C._Martin">Robert C. Martin</a>. Known in the programming community as Uncle Bob. <strong>SOLID principles</strong> constitute a key set of guidelines in <strong>object-oriented programming</strong> (OOP), focusing on promoting clean, modular, and maintainable code. The name Solid consists of the first letters of the five principles:</p><ul><li><strong><em>S</em></strong><em>ingle Responsibility Principle (</em><strong>SRP</strong>)</li><li><strong><em>O</em></strong><em>pen/Closed Principle </em>(<strong>OCP</strong>)</li><li><strong><em>L</em></strong><em>iskov Substitution Principle </em>(<strong>LSP</strong>)</li><li><strong><em>I</em></strong><em>nterface Segregation Principle </em>(<strong>ISP</strong>)</li><li><strong><em>D</em></strong><em>ependency Inversion Principle </em>(<strong>DIP</strong>)</li></ul><h4><strong>(SRP) <em>S</em></strong><em>ingle Responsibility Principle</em></h4><p>The principle was first formulated by <em>Tom DeMarco</em> and <em>Meilir Page-Jones</em> under the name of the principle of cohesion, and it was popularized and presented in its current form by Robert C. Martin in the article “<em>Principles of Object Oriented Design”</em> and the book “<em>Agile Software Development: Principles, Patterns, and Practices”.</em></p><p><strong>The principle proclaim that there should never be more than one reason for a class, method or function to exist.</strong></p><p>The easiest way to explain this is with an example. If we want to display a list of categories and the products belonging to a selected category, we should not create a single component containing both the category list and the products of that category.</p><p><strong>Components with a single, reusable functionality should be implemented</strong>. This means that the data filtering functionality should be broken down into multiple components. In our case, we create a component that contains the functionality for displaying a dropdown list. Another component should handle the functionality of displaying products by category. Additionally, services for categories and products need to be created. These services will be responsible for displaying the data. The services should communicate with each other to pass information about which object the data should be filtered based on.</p><p>Below is the component for selecting the category by which the products are filtered:</p><pre>import { JsonPipe, NgFor } from &#39;@angular/common&#39;;<br>import { Component, inject, input, signal } from &#39;@angular/core&#39;;<br>import { MatFormFieldModule } from &#39;@angular/material/form-field&#39;;<br>import { MatSelectChange, MatSelectModule } from &#39;@angular/material/select&#39;;<br>import { CategoriesService } from &#39;../../services/categories.service&#39;;<br><br>@Component({<br>  selector: &#39;app-select&#39;,<br>  standalone: true,<br>  imports: [JsonPipe, MatFormFieldModule, MatSelectModule, NgFor],<br>  template: `<br>  &lt;mat-form-field&gt;<br>    &lt;mat-label&gt;{{label()}} &lt;/mat-label&gt;<br>    &lt;mat-select [value]=&quot;selected()&quot; (selectionChange)=&quot;onSelectedCategoryChange($event)&quot;&gt;<br>        &lt;mat-option&gt;None&lt;/mat-option&gt;<br>        @for (item of list(); track item ) {<br>          &lt;mat-option [value]=&quot;item&quot;&gt;{{ item.name }}&lt;/mat-option&gt;<br>        }<br>    &lt;/mat-select&gt;<br>  &lt;/mat-form-field&gt;  <br>  `,<br>  styleUrl: &#39;./select.component.scss&#39;<br>})<br>export class SelectComponent {<br>  private categoriesService = inject(CategoriesService);<br><br>  label = input(&#39;&#39;)<br>  list = input&lt;any[]&gt;([]);<br>  selected = signal&lt;any&gt;({});<br><br>  onSelectedCategoryChange(event: MatSelectChange) {<br><br>    this.selected.update(val =&gt; val = event.value);<br>    this.categoriesService.setSelectedCategory(this.selected())<br>  }<br>}</pre><p>Below is the component for the functionality of filtering the product list by category:</p><pre>import { Component, Signal, inject } from &#39;@angular/core&#39;;<br>import { MatListModule } from &#39;@angular/material/list&#39;;<br>import { ProductsService } from &#39;../../services/products.service&#39;;<br>import { Product } from &#39;../../models/models&#39;;<br><br>@Component({<br>  selector: &#39;app-products-list&#39;,<br>  standalone: true,<br>  imports: [MatListModule],<br>  template: `<br>  &lt;mat-list&gt;<br>    @for (product of listProducts(); track product.id ) {<br>        &lt;mat-list-item&gt;{{ product.name }}&lt;/mat-list-item&gt;<br>    }<br>  &lt;/mat-list&gt;<br>  `,<br>  styleUrl: &#39;./products-list.component.scss&#39;,<br>})<br>export class ProductsListComponent {<br>  listProducts: Signal&lt;Product[]&gt; = inject(ProductsService).getProducts();<br>}</pre><p>Product service:</p><pre>import { Injectable, Signal, signal } from &#39;@angular/core&#39;;<br>import { Product } from &#39;../models/models&#39;;<br><br><br>@Injectable({<br>  providedIn: &#39;root&#39;<br>})<br>export class ProductsService {<br><br>  listProducts: Product[] = [<br>    { id: 1, name: &#39;Product 1&#39;, category: { id: 1, name: &#39;Category 1&#39; }, price: 10.99 },<br>    { id: 2, name: &#39;Product 2&#39;, category: { id: 2, name: &#39;Category 2&#39; }, price: 19.99 },<br>    { id: 3, name: &#39;Product 3&#39;, category: { id: 1, name: &#39;Category 1&#39; }, price: 15.99 },<br>    { id: 4, name: &#39;Product 4&#39;, category: { id: 3, name: &#39;Category 3&#39; }, price: 29.99 },<br>    { id: 5, name: &#39;Product 5&#39;, category: { id: 2, name: &#39;Category 2&#39; }, price: 9.99 }<br>  ];<br><br>  products = signal&lt;Product[]&gt;(this.listProducts);<br><br>  getProducts(): Signal&lt;Product[]&gt; {<br>    return this.products.asReadonly();<br>  }<br><br>  setProducts(setListProducts: Product[]) {<br>    this.products.update(value =&gt; value = setListProducts);<br>  }<br>}<br><br></pre><p>Category service:</p><pre>import { inject, Injectable, signal, WritableSignal } from &#39;@angular/core&#39;;<br>import { ProductsService } from &#39;./products.service&#39;;<br>import { Category, Product } from &#39;../models/models&#39;;<br><br>@Injectable({<br>  providedIn: &#39;root&#39;<br>})<br>export class CategoriesService {<br>  listCategories: Category[] = [<br>    { id: 1, name: &#39;Category 1&#39; },<br>    { id: 2, name: &#39;Category 2&#39; },<br>    { id: 3, name: &#39;Category 3&#39; }<br>  ];<br>  categories = signal&lt;Category[]&gt;([]);<br>  selectedCategory = signal&lt;Category&gt;(this.listCategories[0]);<br>  private productsService = inject(ProductsService);<br><br>  getCategories(): WritableSignal&lt;Category[]&gt; {<br>    this.categories.update(value =&gt; value = [...this.listCategories]);<br>    return this.categories<br>  }<br><br>  setSelectedCategory(category: Category) {<br>    this.selectedCategory.update(el =&gt; el = category);<br>    const listProductsFilter = this.productsService.listProducts.filter((value: Product) =&gt; value.category.id === category.id)<br>    this.productsService.setProducts(listProductsFilter);<br>  }<br>}</pre><p>Both services contain sample data not fetched from the server.</p><p><strong>The SRP principle recommends separating coupled components and reducing their responsibility to a single functionality.</strong></p><h4>(<strong>OCP</strong>) <strong><em>O</em></strong><em>pen/Closed Principle</em></h4><p>States “<strong>software entities (classes, modules, functions, etc.) should be open for extension, but closed for modification</strong><em>”. Bertrand Meyer</em> is usually credited with the creation of this principle. He described it in his book “<em>Object-Oriented Software Construction</em>” from 1988.</p><p>I would suggest using reusable components. By creating a component where properties are injected via <strong>input</strong>. Such components are open for extension but closed for modification. In other words, if we do not want to use a particular setting passed in <strong>input</strong> we simply do not provide that property in the component. For example, below is a button component that can be used in different places with different settings provided in the <strong>input</strong> properties:</p><pre>import { Component, input, output } from &#39;@angular/core&#39;;<br><br>@Component({<br>  selector: &#39;app-button&#39;,<br>  template: `<br>  &lt;div class=&quot;{{cssClass()}}&quot; (click)=&quot;onClick()&quot;&gt;<br>    {{label()}} <br>  &lt;/div&gt;<br>  `,<br>  styles: `<br>    .button  { <br>      cursor: pointer; <br>      width: max-content;   <br>      padding: 10px;<br>      background-color: rgb(197, 133, 133);<br>    }`<br>})<br>export class ButtonComponent {<br>  label = input(&#39;&#39;)<br>  cssClass = input(&#39;&#39;)<br>  submitClick = output();<br><br>  onClick(): void {<br>    this.submitClick.emit();<br>  }<br>}</pre><p>Usage of the <strong>app-button</strong> component in other components:</p><pre>&lt;app-button [label]=&quot;&#39;title&#39;&quot; [cssClass]=&quot;&#39;button&#39;&quot; /&gt;</pre><p>You can add other <strong>input</strong> properties to this <strong>app-button</strong> component, such as button type, visibility, disablement, etc. Therefore, I believe that such components adhere to the <strong>Open/Closed Principle</strong>. If we do not want to use an input property, we simply do not pass it. In this way, we are not modifying the existing methods, in this case, the <strong>input</strong> properties.</p><h4>(<strong>LSP</strong>) <strong><em>L</em></strong><em>iskov Substitution Principle</em></h4><p><em>T</em>he design principle in object-oriented programming stating that: <strong>Functions that use pointers or references to base classes must be able to use objects of derived classes without knowing exactly the types of those objects</strong>.</p><p>The principle was first formulated by <strong>Barbara Liskov</strong> in the book “<em>Data Abstraction and Hierarchy”</em>, and later popularized and presented in its current form by Robert C. Martin in the article “<em>Principles of Object Oriented Design”</em> and the book “<em>Agile Software Development: Principles, Patterns, and Practices”</em>.</p><p>First, I will present incorrectly written classes that violate the Liskov Substitution Principle.</p><p>I have three classes: <strong>Animal</strong> with two methods <strong>move</strong> and <strong>fly </strong>because animals usually walk but sometimes fly as well. The second class, <strong>Bird</strong>, inherits from the <strong>Animal</strong> class and has two methods because birds usually walk and fly. The third class, <strong>Ostrich</strong>, has the <strong>move</strong> method because ostriches walk and a <strong>fly</strong> method that generates an exception because ostriches do not fly:</p><pre>import { Component } from &#39;@angular/core&#39;;<br><br><br>class Animal {<br>  move() {<br>    console.log(&#39;Animal I can move &#39;);<br>  }<br>  fly() {<br>    console.log(&#39;Animal I can fly&#39;);<br>  }<br>}<br><br><br>class Bird extends Animal {<br>  override move() {<br>    console.log(&#39;Bird I can move&#39;);<br>  }<br>  override fly() {<br>    console.log(&#39;Bird I can fly&#39;);<br>  }<br>}<br><br><br>class Ostrich extends Animal {<br>  override fly() {<br>    throw new Error(&#39;Ostrich I cannot fly&#39;);<br>  }<br><br>  override move() {<br>    console.log(&#39;Ostrich I can move, but I cannot fly &#39;);<br>  }<br>}<br><br><br>@Component({<br>  selector: &#39;app-zasada-podstawienia-liskov&#39;,<br>  templateUrl: &#39;./zasada-podstawienia-liskov.component.html&#39;,<br>  styleUrl: &#39;./zasada-podstawienia-liskov.component.scss&#39;<br>})<br>export class ZasadaPodstawieniaLiskovComponent extends Animal {<br><br>  constructor() {<br>    super();<br><br>    const animal = new Array()<br>    animal[0] = new Bird();<br>    animal[1] = new Ostrich();<br><br>    animal.forEach(animal =&gt; {<br>      animal.move();<br>      animal.fly();  // Error: Ostrich I cannot fly<br>    })<br>  }<br>}</pre><p>There are many mistakes in these classes. The first one is the <strong>fly</strong> method in the <strong>Animal</strong> class. This is an incorrect assumption because not all animals fly; most only move. The second issue is the method generating an exception in the <strong>Ostrich</strong> class. It makes no sense to call a method that generates an error.</p><p>Now, we need to fix these errors and refine them to comply with the Liskov Substitution Principle. The <strong>fly</strong> method should be extracted from the <strong>Animal</strong> class into another class so that other classes can inherit it if needed. The method generating the exception needs to be cleaned up. We need to add a verification to check if the object is of type <strong>Animalfly</strong> and then call the appropriate <strong>fly</strong> method if it is:</p><pre>import { Component } from &#39;@angular/core&#39;;<br><br>class Animal {<br>  move() {<br>    console.log(&#39;Animal I can move &#39;);<br>  }<br>}<br><br><br>class Animalfly extends Animal {<br>  fly() {<br>    console.log(&#39;Animalfly I can fly&#39;);<br>  }<br>}<br><br><br>class Bird extends Animalfly {<br>  override move() {<br>    console.log(&#39;Bird I can move &#39;);<br>  }<br>  override fly() {<br>    console.log(&#39;Bird I can fly&#39;);<br>  }<br>}<br><br><br><br>class Ostrich extends Animal {<br>  override move() {<br>    console.log(&#39;Ostrich I can move, but I cannot fly &#39;);<br>  }<br>}<br><br><br>@Component({<br>  selector: &#39;app-zasada-podstawienia-liskov&#39;,<br>  templateUrl: &#39;./zasada-podstawienia-liskov.component.html&#39;,<br>  styleUrl: &#39;./zasada-podstawienia-liskov.component.scss&#39;<br>})<br>export class ZasadaPodstawieniaLiskovComponent extends Animal {<br><br>  constructor() {<br>    super();<br><br>    const animals = new Array()<br>    animals[0] = new Bird();<br>    animals[1] = new Ostrich();<br><br>    animals.forEach(animal =&gt; {<br>      animal.move(); <br><br>      if (animal instanceof Animalfly) {<br>        (animal).fly();<br>      }<br>    })<br>  }<br>}</pre><p>In summary, <strong>only the necessary methods are included in the base class</strong>. We do not add methods that do not always meet the assumptions. <strong>So, if we have several classes and a method from the base class does not fit a particular class, we delegate it to a separate class so that only the classes that need it can inherit from it</strong>. In our case, this is the <strong>Animalfly</strong> class with the <strong>fly</strong> method extracted from the base <strong>Animal</strong> class.</p><h4>(<strong>ISP</strong>) <strong><em>I</em></strong><em>nterface Segregation Principle</em></h4><p><em>S</em>tates that no code should be forced to depend on methods it does not use. ISP splits interfaces that are very large into smaller and more specific ones so that clients will only have to know about the methods that are of interest to them.</p><p>For example, during user registration, a form with many fields such as first name, last name, address, and login must be filled out. However, after logging in, not all user data needs to be displayed in every component. The initial interface model for user registration might look like this:</p><pre>export interface User {<br>   firstName: string;<br>   lastName: string;<br>   login: string;<br>   <br>   phone: string;<br>   email: string; <br><br>   street: string;<br>   houseNumber: string;<br>   apartmentNumber: string;<br>   city: string;<br>   zipCode: string;<br>   country: string;<br>}</pre><p>This interface model does not adhere to the Interface Segregation Principle. It should be broken down into smaller, more specific interfaces:</p><pre>export interface User {<br>   firstName: string;<br>   lastName: string;<br>   login: string;<br>   phone: string;<br>   contactDetails: ContactDetails;<br>   address: Address;<br>}<br><br><br>export interface ContactDetails {<br>   phone: string;<br>   email: string; <br>}<br><br>export interface Address {<br>   street: string;<br>   houseNumber: string;<br>   apartmentNumber?: string;<br>   city: string;<br>   zipCode: string;<br>   country: string;<br>}</pre><p>This interface model does not adhere to the Interface Segregation Principle, as it includes all fields, which may not always be needed for displaying, editing, or deleting. In my opinion, to ensure that the model and interface comply with the Interface Segregation Principle, which means implementing only the methods required in a given component, it should be segmented as follows:</p><pre><br> export interface User {<br>   firstName: string;<br>   lastName: string;<br>   login: string; <br>   contactDetails?: ContactDetails;<br>   address: Address;<br>}<br><br>export interface ContactDetails {<br>   phone: string;<br>   email: string; <br>}<br><br>export interface Address {<br>   street: string;<br>   houseNumber: string;<br>   apartmentNumber?: string;<br>   city: string;<br>   zipCode: string;<br>   country: string;<br>}<br> <br>export interface GetDetailedUser {<br>   getFirstName(): string;<br>   getLastName(): string;     <br>   getLogin(): string;   <br>   getContactDetails?(): ContactDetails;<br>   getAddress?(): Address;<br>   getUser?(): User;<br>}<br><br>export interface SetDetailedUser {  <br>   setFirstName(firstName: string): void;   <br>   setLastName(lastName: string): void;   <br>   setLogin(login: string): void;<br>   setContactDetails?(contactDetails: ContactDetails): void;  <br>   setAddress?(address: Address): void;  <br>   setUser?(user: User): void;  <br>}<br><br>export interface GetContactDetails {<br>   getPhone(): string; <br>   getEmail(): string; <br>}<br><br>export interface SetContactDetails {<br>   setPhone(phone: string): void;<br>   setEmail(email: string): void;   <br>}<br><br>export interface GetAddress {<br>   getStreet(): string;<br>   getHouseNumber(): string;<br>   getApartmentNumber?(): string;<br>   getCity(): string;<br>   getZipCode(): string;<br>   getCountry(): string;<br>   getAddress?(): Address<br>}<br><br>export interface SetAddress {<br>   setStreet(street: string): void;<br>   setHouseNumber(houseNumber: string): void;<br>   setApartmentNumber?(apartmentNumber: string): void;<br>   setCity(city: string): void;<br>   setZipCode(zipCode: string): void;<br>   setCountry(country: string): void;<br>   setAddress?(address: Address): void;<br>}</pre><p>In TypeScript, there are several utility types that facilitate common type transformations by creating one type from another. They can help adhere to the Interface Segregation Principle, and include <strong>Omit&lt;Type, Keys&gt;</strong>, and <strong>Pick&lt;Type, Keys&gt;</strong> Below is an example of their usage:</p><pre><br>export interface User {<br>   firstName: string;<br>   lastName: string;<br>   login: string; <br>   contactDetails?: ContactDetails;<br>   address: Address;<br>}<br><br>export interface ContactDetails {<br>   phone: string;<br>   email: string;<br>}<br><br>export interface Address {<br>   street: string;<br>   houseNumber: string;<br>   apartmentNumber?: string;<br>   city: string;<br>   zipCode: string;<br>   country: string;<br>}<br><br>type UserName = Pick&lt;User, &#39;firstName&#39; | &#39;lastName&#39;&gt;;<br><br>const userName: UserName = {<br>   firstName: &quot;Zofia&quot;,<br>   lastName: &#39;Kowalska&#39;,<br>};<br><br><br>type UserAddressPreview = Omit&lt;Address, &#39;country&#39;&gt;;<br><br>const todo: UserAddressPreview = {<br>   street: &#39;New&#39;,<br>   houseNumber: &#39;123&#39;,<br>   apartmentNumber: &#39;9a&#39;,<br>   city: &#39;Szczecin&#39;,<br>   zipCode: &#39;12345&#39;<br>};</pre><p><strong>Pick </strong>selected keys from a given type to include in the new type. <strong>Omit </strong>excludes specified keys from a given type to create a new type.</p><h4>(<strong>DIP</strong>) <strong><em>D</em></strong><em>ependency Inversion Principle</em></h4><p><strong>It states that high-level modules should not depend on low-level modules — dependencies between them should be based on abstractions. </strong>Thus, an abstraction should be applied between a high-level module and a low-level module.</p><p>In the example below, there is no abstraction class or interface definition, which would facilitate adding or modifying the data returned from the API (low-level class):</p><pre> <br>export const customers: any[] = [<br>  { id: 1, name: &#39;John&#39; },<br>  { id: 2, name: &#39;Jane&#39; },<br>  { id: 3, name: &#39;Jack&#39; }<br>];<br>// low-level class<br>@Injectable({<br>  providedIn: &#39;root&#39;<br>})<br>export class CustomerService  {<br><br>  getCustomers(): Observable&lt;any[]&gt; {<br>    return of(customers);<br>  }<br>}</pre><p>high-level class:</p><pre>import { Component, inject } from &#39;@angular/core&#39;;<br>import { AsyncPipe } from &#39;@angular/common&#39;;<br>import { CustomerService } from &#39;../services/customer.service&#39;;<br><br>@Component({<br>  selector: &#39;app-zasada-odwrocenia-zaleznosci&#39;,<br>  standalone: true,<br>  imports: [AsyncPipe],<br>  providers:[CustomerService],<br>  template: `<br>    @for (item of listCustomers$ | async; track item.id) {<br>    &lt;li&gt;{{ item.name }}&lt;/li&gt;<br>    } @empty {<br>    &lt;li&gt;There are no items.&lt;/li&gt;<br>    }<br>  `,<br>  styleUrl: &#39;./zasada-odwrocenia-zaleznosci.component.scss&#39;<br>})<br>export class ZasadaOdwroceniaZaleznosciComponent {<br>  protected readonly listCustomers$ = inject(CustomerService).getCustomers()<br>}</pre><p>By adding an abstract class and an interface, in case of a change in the object returned by the API, it is sufficient to modify the Customer interface:</p><pre>import { Injectable } from &#39;@angular/core&#39;;<br>import { Observable, of } from &#39;rxjs&#39;;<br><br>export interface Customer {<br>  id: number;<br>  name: string;<br>}<br>export abstract class ICustomerRepository {<br>  abstract getCustomers(): Observable&lt;Customer[]&gt;;<br>}<br>export const customers: Customer[] = [<br>  { id: 1, name: &#39;John&#39; },<br>  { id: 2, name: &#39;Jane&#39; },<br>  { id: 3, name: &#39;Jack&#39; }<br>];<br><br>// high level class<br>@Injectable({<br>  providedIn: &#39;root&#39;<br>})<br>export class CustomerService extends ICustomerRepository {<br><br>  getCustomers(): Observable&lt;Customer[]&gt; {<br>    return of(customers);<br>  }<br>}</pre><p>Below is a view of the component template:</p><pre>import { Component, inject } from &#39;@angular/core&#39;;<br>import { AsyncPipe } from &#39;@angular/common&#39;;<br>import { CustomerService, ICustomerRepository } from &#39;../services/customer.service&#39;;<br><br>// low level class<br>@Component({<br>  selector: &#39;app-zasada-odwrocenia-zaleznosci&#39;,<br>  standalone: true,<br>  imports: [AsyncPipe],<br>  providers: [{<br>    provide: ICustomerRepository,<br>    useClass: CustomerService<br>  }],<br>  template: `<br>    @for (item of listCustomers$ | async; track item.id) {<br>    &lt;li&gt;{{ item.name }}&lt;/li&gt;<br>    } @empty {<br>    &lt;li&gt;There are no items.&lt;/li&gt;<br>    }<br>  `,<br>  styleUrl: &#39;./zasada-odwrocenia-zaleznosci.component.scss&#39;<br>})<br>export class ZasadaOdwroceniaZaleznosciComponent {<br>  protected readonly listCustomers$ = inject(CustomerService).getCustomers()<br>}</pre><h3>Summary:</h3><p>I believe that programming according to <strong>SOLID </strong>principles is the best approach to software design, especially in the context of object-oriented programming like Angular. It may seem challenging, but I think that adhering to these principles can bring significant benefits to code quality, scalability, flexibility, and ease of maintenance.</p><p>Materials:</p><ol><li><a href="https://pl.wikipedia.org/wiki/SOLID">https://pl.wikipedia.org/wiki/SOLID</a></li><li><a href="https://pl.wikipedia.org/wiki/Robert_C._Martin">https://pl.wikipedia.org/wiki/Robert_C._Martin</a></li><li><a href="https://coderslab.pl/pl/blog/zasady-solid-programowanie-zgodne-z-dobrymi-praktykami">https://coderslab.pl/pl/blog/zasady-solid-programowanie-zgodne-z-dobrymi-praktykami</a></li><li><a href="https://www.p-programowanie.pl/paradygmaty-programowania/zasady-solid">https://www.p-programowanie.pl/paradygmaty-programowania/zasady-solid</a></li><li><a href="https://www.typescriptlang.org/docs/handbook/utility-types.html">https://www.typescriptlang.org/docs/handbook/utility-types.html</a></li></ol><p>I’m not good at English. I write articles to practice English.</p><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=e3f315b5bc70" width="1" height="1" alt=""><hr><p><a href="https://medium.com/principles-solid-in-angular/principles-solid-in-angular-e3f315b5bc70">Principles SOLID in Angular</a> was originally published in <a href="https://medium.com/principles-solid-in-angular">Principles SOLID in Angular</a> on Medium, where people are continuing the conversation by highlighting and responding to this story.</p>]]></content:encoded>
        </item>
    </channel>
</rss>