Sobes.tech
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:

  1. Kõrgema taseme moodulid ei tohi sõltuda madalama taseme moodulitest. Mõlemad peavad sõltuma abstraktsioonidest.
  2. 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.