Šta je Dependency Injection (DI)?

Dependency Injection (DI) je softverski obrazac dizajna koji omogućava razdvajanje zavisnosti objekata i njihove inicijalizacije. U kontekstu Nest.js, DI omogućava lakše upravljanje zavisnostima, smanjujući međusobnu povezanost klasa i povećavajući modularnost i testabilnost aplikacije. DI je softerski obrazac koji nije vezan samo za nest.js ili node.js već se koristi i u drugim programskim jezicima, pročitajte kako se uopšteno koristi dependency injection-u u članku Šta je “Dependency Injection”?.

DI je najlakše objasniti kroz primer, zamislite da imate klasu koja šalje e-poštu korisnicima. Ta klasa može direktno kreirati instancu objekta koji šalje e-poštu (SMTP servis), ili možete koristiti Dependency Injection (DI) kako biste taj zadatak prepustili eksternom entitetu (IoC kontejneru).


Primer bez Dependency Injection-a (Loša praksa)

U ovome primeru se instanca Smpt servisa kreira u konstruktoru:

SmtpService je zavisnost (eng. dependency) za EmailService, te kreiranje njene instance u konstruktoru, obezbedjuje tzv. čvrstu povezanost, jer klasa EmailService zavisi od konkretne implementacije SmtpService. Kreiranje sopstvene instance u konstruktoru nas dovodi do više problema:

  • Otežana izmena servisa jer bi svaka promena u implementaciji SMTP servisa npr. dodavanje novih zavisnosti u SMTP servis bi zahtevala promene u svakoj klasi gde je instancirana ta klasa,
  • Teško testiranje jer nije moguće lako mockovati SmtpService u testovima.

Primer sa Dependency Injection-om (Dobra praksa)

Sada ćemo koristiti DI tako što ćemo premestiti odgovornost za kreiranje zavisnosti iz klase EmailService u eksterni IoC kontejner:

U ovom slučaju, SmtpService će biti kreiran i upravljan od strane IoC kontejnera u Nest.js.

Registracija u modulu

Ključne razlike

  • Odgovornost za kreiranje zavisnosti:

    • Bez DI: Klasa sama kreira instancu zavisnosti.
    • Sa DI: IoC kontejner kreira instancu i pruža je klasi.
  • Fleksibilnost: Promena samog SmtpService servisa ili čak zamena za neki drugi servis je jednostavna — sada je dovoljno zameniti provajdera u IoC kontejneru, bez promene koda u EmailService.
  • Testiranje: Lako se mockuje zavisnost u testovima:

DI u praksi sa Nest.js

Umesto da klase same kreiraju svoje zavisnosti, DI prenosi odgovornost za kreiranje zavisnosti nekom eksternom entitetu — obično kontejneru za injekciju zavisnosti. DI je praktična implementacija šireg koncepta poznatog kao Inverzija kontrole (Inversion of Control, IoC). IoC preokreće uobičajeni tok programa — umesto da aplikacija kontroliše kako se zavisnosti kreiraju i koriste, taj zadatak preuzima IoC kontejner. U Nest.js, IoC kontejner automatski upravlja zavisnostima i njihovim životnim ciklusom na osnovu definisanih pravila a osnovne komponente koje DI koristi u Nest.js su:

  • Provideri: Klase ili objekti koji se mogu injektovati.
  • Moduli: Organizuju aplikaciju i definišu koji provajderi su dostupni.
  • Dekoratori: Obeležavaju klase, metode ili parametre da bi označili njihove uloge u DI sistemu.

Korak 1: Definisanje provajdera

Dekorator @Injectable() omogućava Nest-u da registruje ovu klasu kao provajdera u IoC kontejneru.

Korak 2: Injektovanje zavisnosti

Konstruktor ExampleController automatski prima instancu ExampleService putem DI-a.

Korak 3: Registrovanje u modulu

Interfejs u DI za veću fleksibilnost

Korišćenje interfejsa u Nest.js omogućava aplikacijama veću fleksibilnost i prilagodljivost. Umesto direktnog korišćenja specifične implementacije, možete definisati interfejs koji opisuje ponašanje (ugovor) koje se očekuje od provajdera. Ovo omogućava lako menjanje implementacije bez potrebe za izmenama u kontrolerima ili drugim zavisnim komponentama.

Registrovanje interfejsa u modulu

Interfejs i njegova implementacija se registruju kao provajderi unutar modula. Koristi se ključ provide kako bi se povezala implementacija sa interfejsom.

Ovaj pristup omogućava menjanje implementacije jednostavnom zamenom klase u useClass, bez potrebe za promenama u kontrolerima ili servisu.

Implementacija interfejsa

U ovom primeru, ExampleInterface definiše metodu getHello, koja mora biti implementirana od strane bilo koje klase koja se registruje kao provajder.

Klasa ExampleImplementation implementira ExampleInterface. Ova implementacija će se koristiti u kontrolerima kada se interfejs
registruje u modulu.

Korišćenje interfejsa u kontroleru

U kontroleru, interfejs se može injektovati koristeći dekorator @Inject(). Ključ ‘ExampleInterface’ se koristi za povezivanje sa odgovarajućim provajderom.

Ovo omogućava da kontroler koristi funkcionalnost definisanu interfejsom, dok implementaciju kontroliše IoC kontejner.

Prednosti ovog pristupa

  • Jednostavna zamena implementacije: Možete promeniti klasu koja implementira interfejs bez izmene kontrolera.
  • Povećana testabilnost: Umesto stvarne implementacije, tokom testiranja možete registrovati mock ili stub verziju interfejsa.
  • Modularnost: Omogućava odvajanje koda u nezavisne module sa minimalnom međuzavisnošću.
  • Prilagodljivost: Lako se integrišu različite implementacije, kao što su varijante funkcionalnosti za različite konfiguracije ili okruženja.
Podelite:

Ostavite komentar