Senior
Kā risināt problēmu ar perifēro komponentu atkarībām kodolā?
sobes.tech AI
Atbilde no AI
Risinot atkarīgu komponentu (adapteru, saskarnes) izmantošanas problēmu kodola (domaņas loģikā) arhitektūrā, piemēram, Tīrā arhitektūrā vai DDD, to var izdarīt, izmantojot atkarību inversijas principu (Dependency Inversion Principle - DIP).
DIP būtība:
- Augstākā līmeņa moduļi nedrīkst būt atkarīgi no zemākā līmeņa moduļiem. Abi ir atkarīgi no abstrakcijām.
- Abstrakcijas nedrīkst būt atkarīgas no detaļām. Detaļas ir atkarīgas no abstrakcijām.
Praktiska pielietojuma piemērs:
- Kodols deklarē saskarnes: Kodols definē abstrakcijas (saskarnes vai abstraktus klases), kas apraksta nepieciešamās funkcijas no perifērajiem komponentiem.
- Perifērie komponenti realizē saskarnes: Ārējie moduļi (adapteri datu bāzēm, HTTP klienti, failu sistēmas utt.) realizē šīs saskarnes.
- Atkarību injekcija (Dependency Injection - DI): Perifērie komponenti tiek injicēti kodolā no ārpuses, parasti augstākā līmenī (piemēram, kompozīcijas saknē vai ar DI konteineru). Kodols strādā tikai ar abstrakcijām.
Piemērs, kā piemērot DIP Node.js:
Pieņemsim, mums ir kodols, kam nepieciešama piekļuve datiem.
// src/domain/interfaces/UserRepository.ts
// Kodols deklarē saskarni
export interface UserRepository {
getUserById(id: string): Promise<User | null>;
}
// src/domain/entities/User.ts
// Kodola vienība
export class User {
constructor(public id: string, public name: string) {}
}
// src/domain/usecases/GetUserUseCase.ts
// Kodola lietošanas gadījums ir atkarīgs no abstraktās 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
// Ārējā komponenta, kas realizē kodola saskarni
import { UserRepository } from '../../domain/interfaces/UserRepository';
import { User } from '../../domain/entities/User';
export class MongoDbUserRepository implements UserRepository {
// Šeit būs saziņas ar MongoDB loģika
async getUserById(id: string): Promise<User | null> {
console.log(`Iegūst lietotāju ar id ${id} no MongoDB...`);
// Datu iegūšanas imitācija
if (id === '123') {
return new User('123', 'John Doe');
}
return null;
}
}
// src/app/CompositionRoot.ts
// Kompozīcijas sakne savāc atkarības
import { GetUserUseCase } from '../domain/usecases/GetUserUseCase';
import { MongoDbUserRepository } from '../infrastructure/database/MongoDbUserRepository';
// Atkarību injekcija: kodols saņem MongoDbUserRepository realizāciju caur saskarni
const userRepository = new MongoDbUserRepository();
const getUserUseCase = new GetUserUseCase(userRepository);
// Tagad mēs varam izmantot lietošanas gadījumu
async function run() {
const user = await getUserUseCase.execute('123');
if (user) {
console.log(`Atrasts lietotājs: ${user.name}`);
} else {
console.log('Lietotājs nav atrasts');
}
}
run();
Šāda pieeja sniedz vairākas priekšrocības:
- Neatkarība no kodola: Kodols nezina par konkrētām perifēro komponentu realizācijām.
- Testējamība: Kodolu ir viegli testēt, aizstājot ar mock realizācijām saskarnēm.
- Elastība: Ir viegli aizstāt vienu realizāciju (piemēram, datu bāzi) ar citu, nemainot kodola kodu.
- Atbalsts: Vienkāršo koda uzturēšanu un izmaiņas.