Senior
Kuidas lahendada probleem, mis on seotud perifeersete komponentide sõltuvustega kernelis?
sobes.tech AI
Vastus AI-lt
Lahendades sõltuvuskomponentide (adapterite, liideste) kasutamise probleemi tuumas (domeeni loogikas) arhitektuuris, näiteks Puhas arhitektuur või DDD, saab seda teha, kasutades sõltuvuse pööramise põhimõtet (Dependency Inversion Principle - DIP).
DIP olemus:
- Kõrgema taseme moodulid ei tohi sõltuda madalama taseme moodulitest. Mõlemad peavad sõltuma abstraktsioonidest.
- Abstraktsioonid ei tohi sõltuda üksikdetailidest. Detailid peavad sõltuma abstraktsioonidest.
Praktilise rakendamise näide:
- Tuum deklarerib liideseid: Tuum määratleb abstraktsioonid (liidesed või abstraktsed klassid), mis kirjeldavad talle vajalikke funktsioone perifeersete komponentide poolt.
- Perifeersed komponendid rakendavad liideseid: Välised moodulid (adapterid andmebaasidele, HTTP-klientid, failisüsteemid jne) rakendavad neid liideseid.
- Sõltuvuse injekteerimine (Dependency Injection - DI): Perifeersed komponendid sisestatakse tuuma väljastpoolt, tavaliselt kõrgemal tasemel rakenduses (näiteks kompositsiooni juuri või DI-konteinerit kasutades). Tuum töötab ainult abstraktsioonidega.
Näide DIP kasutamisest Node.js-is:
Oletame, meil on tuum, millele on vaja andmetele juurdepääsu.
// src/domain/interfaces/UserRepository.ts
// Tuum deklarerib liidese
export interface UserRepository {
getUserById(id: string): Promise<User | null>;
}
// src/domain/entities/User.ts
// Tuumi üksus
export class User {
constructor(public id: string, public name: string) {}
}
// src/domain/usecases/GetUserUseCase.ts
// Tuumi kasutusjuhtum sõltub abstraktsest UserRepository-st
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
// Välisosa, mis rakendab tuuma liidest
import { UserRepository } from '../../domain/interfaces/UserRepository';
import { User } from '../../domain/entities/User';
export class MongoDbUserRepository implements UserRepository {
// Siin on MongoDB-ga suhtlemise loogika
async getUserById(id: string): Promise<User | null> {
console.log(`Kättesaadav kasutaja id-ga ${id} MongoDB-st...`);
// Andmete saamise imitatsioon
if (id === '123') {
return new User('123', 'John Doe');
}
return null;
}
}
// src/app/CompositionRoot.ts
// Kompositsiooni juur kogub sõltuvused
import { GetUserUseCase } from '../domain/usecases/GetUserUseCase';
import { MongoDbUserRepository } from '../infrastructure/database/MongoDbUserRepository';
// Sõltuvuse sisestamine: Tuum saab MongoDbUserRepository rakenduse kaudu liidese kaudu
const userRepository = new MongoDbUserRepository();
const getUserUseCase = new GetUserUseCase(userRepository);
// Nüüd saame kasutada kasutusjuhtu
async function run() {
const user = await getUserUseCase.execute('123');
if (user) {
console.log(`Leitud kasutaja: ${user.name}`);
} else {
console.log('Kasutajat ei leitud');
}
}
run();
Sellise lähenemise eelised:
- Tuuma sõltumatus: Tuum ei tea konkreetsetest perifeersete komponentide rakendustest.
- Testitavus: Tuumi on lihtne testida, asendades liideste mock-implementatsioonidega.
- Paindlikkus: Ühe rakenduse (näiteks andmebaasi) saab hõlpsasti asendada teisega, muutmata tuuma koodi.
- Hooldus: Koodi hooldus ja muutmine on lihtne.