Sobes.tech
Senior

Ako vyriešiť problém používania závislostí periférnych komponentov v jadre?

sobes.tech AI

Odpoveď od AI

Riešenie problému používania závislostí periférnych komponentov (adapterov, rozhraní) v jadre (doménovej logike) v architektúrach, ako je Čistá architektúra alebo DDD, je možné dosiahnuť pomocou princípu Inverzie závislostí (Dependency Inversion Principle - DIP).

Podstata DIP:

  1. Moduly vyššej úrovne by nemali závisieť od modulov nižšej úrovne. Oba by mali závisieť od abstrakcií.
  2. Abstrakcie by nemali závisieť od detailov. Detaily by mali závisieť od abstrakcií.

Praktická aplikácia:

  • Jadro vyhlasuje rozhrania: Jadro definuje abstrakcie (rozhrania alebo abstraktné triedy), ktoré opisujú potrebné funkcie periférnych komponentov.
  • Periférne komponenty implementujú rozhrania: Externé moduly (adaptery pre databázy, HTTP klientov, súborové systémy atď.) implementujú tieto rozhrania.
  • Vkladanie závislostí (Dependency Injection - DI): Periférne komponenty sú vkladané do jadra zvonku, zvyčajne na vyššej úrovni aplikácie (napríklad v kompozičnom koreň alebo s použitím DI kontajnera). Jadro pracuje iba s abstrakciami.

Príklad použitia DIP v Node.js:

Predpokladajme, že máme jadro, ktoré potrebuje prístup k údajom.

// src/domain/interfaces/UserRepository.ts
// Jadro vyhlasuje rozhranie
export interface UserRepository {
  getUserById(id: string): Promise<User | null>;
}
// src/domain/entities/User.ts
// Entita jadra
export class User {
  constructor(public id: string, public name: string) {}
}
// src/domain/usecases/GetUserUseCase.ts
// Use case jadra závisí od abstrakcie UserRepository
import { UserRepository } from '../interfaces/UserRepository';
import { User } from '../entities/User';

export class GetUserUseCase {
  constructor(private userRepository: UserRepository) {}

  async execute(id: string): Promise<User | null> {
    return this.userRepository.getUserById(id);
  }
}
// src/infrastructure/database/MongoDbUserRepository.ts
// Periférny komponent, ktorý implementuje rozhranie jadra
import { UserRepository } from '../../domain/interfaces/UserRepository';
import { User } from '../../domain/entities/User';

export class MongoDbUserRepository implements UserRepository {
  // Tu bude logika komunikácie s MongoDB

  async getUserById(id: string): Promise<User | null> {
    console.log(`Načítanie používateľa s id ${id} z MongoDB...`);
    // Simulácia získania dát
    if (id === '123') {
      return new User('123', 'John Doe');
    }
    return null;
  }
}
// src/app/CompositionRoot.ts
// Kompozičný koreň zhromažďuje závislosti
import { GetUserUseCase } from '../domain/usecases/GetUserUseCase';
import { MongoDbUserRepository } from '../infrastructure/database/MongoDbUserRepository';

// Vkladanie závislosti: Jadro dostáva implementáciu MongoDbUserRepository cez rozhranie
const userRepository = new MongoDbUserRepository();
const getUserUseCase = new GetUserUseCase(userRepository);

// Teraz môžeme použiť prípad použitia
async function run() {
  const user = await getUserUseCase.execute('123');
  if (user) {
    console.log(`Nájdený používateľ: ${user.name}`);
  } else {
    console.log('Používateľ nenájdený');
  }
}

run();

Výhody tohto prístupu:

  • Nezávislosť jadra: Jadro nepozná konkrétne implementácie periférnych komponentov.
  • Testovateľnosť: Jadro je ľahko testovateľné pomocou mock-implementácií rozhraní.
  • Flexibilita: Je možné ľahko nahradiť jednu implementáciu (napríklad databázu) inou, bez zmeny kódu jadra.
  • Podpora: Uľahčuje údržbu a zmeny kódu.