Senior
Kaip išspręsti problemą, susijusią su periferinių komponentų priklausomybėmis branduolyje?
sobes.tech AI
Atsakymas iš AI
Sprendžiant priklausomybių komponentų (adapterių, sąsajų) naudojimo problemą branduolyje (domenu logikoje) architektūroje, pavyzdžiui, Švariojoje architektūroje arba DDD, galima pasinaudoti priklausomybės inversijos principu (Dependency Inversion Principle - DIP).
DIP esmė:
- Aukštesnio lygio moduliai neturi priklausyti nuo žemesnio lygio modulių. Abu turi priklausyti nuo abstrakcijų.
- Abstrakcijos neturi priklausyti nuo detalių. Detalės turi priklausyti nuo abstrakcijų.
Praktinis taikymas:
- Branduolys deklaruoja sąsajas: Branduolys apibrėžia abstrakcijas (sąsajas arba abstrakčius klases), kurios apibūdina jam reikalingas funkcijas iš periferinių komponentų.
- Periferiniai komponentai įgyvendina sąsajas: Išoriniai moduliai (adapteriai duomenų bazėms, HTTP klientai, failų sistemos ir kt.) įgyvendina šias sąsajas.
- Priklausomybių injekcija (Dependency Injection - DI): Periferiniai komponentai yra įdedami į branduolį iš išorės, dažniausiai aukštesniame programos lygyje (pavyzdžiui, kompozicijos šaknyje arba naudojant DI konteinerį). Branduolys dirba tik su abstrakcijomis.
Pavyzdys, kaip taikyti DIP Node.js:
Tarkime, turime branduolį, kuriam reikalingas prieigos prie duomenų.
// src/domain/interfaces/UserRepository.ts
// Branduolys deklaruoja sąsają
export interface UserRepository {
getUserById(id: string): Promise<User | null>;
}
// src/domain/entities/User.ts
// Branduolio entitetas
export class User {
constructor(public id: string, public name: string) {}
}
// src/domain/usecases/GetUserUseCase.ts
// Branduolio naudotojo atvejis priklauso nuo abstrakčiosios 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
// Išorinė komponentė, įgyvendinanti branduolio sąsają
import { UserRepository } from '../../domain/interfaces/UserRepository';
import { User } from '../../domain/entities/User';
export class MongoDbUserRepository implements UserRepository {
// Čia bus sąveikos su MongoDB logika
async getUserById(id: string): Promise<User | null> {
console.log(`Gaunamas naudotojas su id ${id} iš MongoDB...`);
// Duomenų gavimo imitacija
if (id === '123') {
return new User('123', 'John Doe');
}
return null;
}
}
// src/app/CompositionRoot.ts
// Kompozicijos šaknis surenka priklausomybes
import { GetUserUseCase } from '../domain/usecases/GetUserUseCase';
import { MongoDbUserRepository } from '../infrastructure/database/MongoDbUserRepository';
// Priklausomybės injekcija: branduolys gauna MongoDbUserRepository realizaciją per sąsają
const userRepository = new MongoDbUserRepository();
const getUserUseCase = new GetUserUseCase(userRepository);
// Dabar galime naudoti naudotojo atvejį
async function run() {
const user = await getUserUseCase.execute('123');
if (user) {
console.log(`Rastas naudotojas: ${user.name}`);
} else {
console.log('Naudotojas nerastas');
}
}
run();
Šios prieigos privalumai:
- Nepriklausomybė nuo branduolio: Branduolys nežino apie konkrečias periferinių komponentų realizacijas.
- Testavimas: Branduolį lengva testuoti, įdedant mock-implementacijas sąsajoms.
- Lankstumas: Lengva pakeisti vieną realizaciją (pvz., duomenų bazę) kita, nekeičiant branduolio kodo.
- Palaikymas: Paprasta prižiūrėti ir keisti kodą.