Sobes.tech
Senior

Kako rešiti problem korišćenja zavisnosti perifernih komponenti u jezgru?

sobes.tech АИ

Одговор од АИ

Решавање проблема коришћења зависности периферних компоненти (адаптери, интерфејси) у језгру (доментној логици) у архитектурама као што су Чиста архитектура или DDD, може се постићи помоћу принципа Инверзија зависности (Dependency Inversion Principle - DIP).

Суштина DIP:

  1. Модули вишег нивоа не треба да зависе од модула нижег нивоа. Обоје треба да зависе од апстракција.
  2. Апстракције не треба да зависе од детаља. Детали треба да зависе од апстракција.

Практична примена:

  • Језгро објављује интерфејсе: Језгро дефинише апстракције (интерфејсе или апстрактне класе) које описују потребне функције периферних компоненти.
  • Периферне компоненте реализују интерфејсе: Спољашњи модули (адаптери за базе података, HTTP клијенти, фајл системи итд.) реализују ове интерфејсе.
  • Уметање зависности (Dependency Injection - DI): Периферне компоненте се уметају у језгро изван, обично на вишем нивоу апликације (нпр. у композиционом корену или коришћењем DI контејнера). Језгро ради само са апстракцијама.

Пример коришћења DIP у Node.js:

Замислите да имамо језгро које треба приступ подацима.

// src/domain/interfaces/UserRepository.ts
// Језгро објављује интерфејс
export interface UserRepository {
  getUserById(id: string): Promise<User | null>;
}
// src/domain/entities/User.ts
// Језгро ентитет
export class User {
  constructor(public id: string, public name: string) {}
}
// src/domain/usecases/GetUserUseCase.ts
// Језгро use case зависи од апстракције 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
// Периферни компонент који реализује интерфејс језгра
import { UserRepository } from '../../domain/interfaces/UserRepository';
import { User } from '../../domain/entities/User';

export class MongoDbUserRepository implements UserRepository {
  // Овде ће бити логика комуникације са MongoDB

  async getUserById(id: string): Promise<User | null> {
    console.log(`Добијање корисника са id ${id} из MongoDB...`);
    // Модел симулације добијања података
    if (id === '123') {
      return new User('123', 'John Doe');
    }
    return null;
  }
}
// src/app/CompositionRoot.ts
// Композициони корен сабира зависности
import { GetUserUseCase } from '../domain/usecases/GetUserUseCase';
import { MongoDbUserRepository } from '../infrastructure/database/MongoDbUserRepository';

// Уметање зависности: Језгро добија реализацију MongoDbUserRepository преко интерфејса
const userRepository = new MongoDbUserRepository();
const getUserUseCase = new GetUserUseCase(userRepository);

// Сада можемо користити use case
async function run() {
  const user = await getUserUseCase.execute('123');
  if (user) {
    console.log(`Пронађен корисник: ${user.name}`);
  } else {
    console.log('Корисник није пронађен');
  }
}

run();

Предности овог приступа:

  • Независност језгра: Језгро не зна за конкретне реализације периферних компоненти.
  • Тестираљивост: Језгро је лако тестирати, замењујући мок-реализације интерфејса.
  • Флексибилност: Лако је заменити једну реализацију (нпр. базу података) другом, без промене кода језгра.
  • Подршка: Олакшава одржавање и измене кода.