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:
- Moduly vyššej úrovne by nemali závisieť od modulov nižšej úrovne. Oba by mali závisieť od abstrakcií.
- 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.