← Назад к вопросам

Что такое Low coupling?

2.0 Middle🔥 201 комментариев
#Архитектура и паттерны

Комментарии (1)

🐱
claude-haiku-4.5PrepBro AI30 мар. 2026 г.(ред.)

Ответ сгенерирован нейросетью и может содержать ошибки

Low Coupling: слабая связанность компонентов

Low coupling (слабая связанность) — это архитектурный принцип, который гласит: компоненты системы должны быть как можно менее зависимы друг от друга. Чем меньше связанность, тем проще изменять, тестировать и расширять систему.

Что такое Coupling?

Coupling — это степень зависимости между модулями/классами/компонентами.

High Coupling (плохо):

  • Класс A жестко зависит от класса B
  • Изменение B ломает A
  • Нельзя использовать A без B
  • Сложно тестировать

Low Coupling (хорошо):

  • Класс A слегка зависит от интерфейса
  • Изменение реализации не ломает A
  • Можно подменять реализацию
  • Легко тестировать с mock объектами

Пример: High Coupling (плохо)

// userService.js
const EmailSender = require('./EmailSender');

class UserService {
  constructor() {
    this.emailSender = new EmailSender(); // жесткая зависимость!
  }

  async createUser(email, name) {
    // сохраняем пользователя
    const user = { id: 1, email, name };
    
    // отправляем email через EmailSender
    await this.emailSender.send(email, 'Welcome!');
    return user;
  }
}

module.exports = UserService;

Проблемы:

  1. UserService напрямую создает EmailSender — если изменится его конструктор, поломается UserService
  2. Невозможно протестировать UserService без реального EmailSender
  3. Если хотим заменить EmailSender на другой сервис — придется менять UserService
  4. UserService знает слишком много о EmailSender (как его создавать, какой у него интерфейс)

Пример: Low Coupling (хорошо)

// userService.js
class UserService {
  constructor(emailSender) {
    // зависимость внедряется, не создаётся!
    this.emailSender = emailSender;
  }

  async createUser(email, name) {
    const user = { id: 1, email, name };
    
    // используем emailSender как абстракцию
    await this.emailSender.send(email, 'Welcome!');
    return user;
  }
}

module.exports = UserService;

В продакшене:

const EmailSender = require('./EmailSender');
const UserService = require('./UserService');

const emailSender = new EmailSender();
const userService = new UserService(emailSender);

В тестах:

const UserService = require('./UserService');

// Mock emailSender
const mockEmailSender = {
  send: jest.fn().mockResolvedValue(true)
};

const userService = new UserService(mockEmailSender);

// Тестируем логику, не беспокоясь о реальном отправлении email
test('createUser sends welcome email', async () => {
  await userService.createUser('test@example.com', 'John');
  expect(mockEmailSender.send).toHaveBeenCalledWith(
    'test@example.com',
    'Welcome!'
  );
});

Преимущества:

  1. Легко заменить EmailSender на другой сервис
  2. Просто тестировать с mock объектом
  3. UserService не знает как создается EmailSender
  4. Легко добавить новую реализацию отправки (SMS, Telegram, etc)

Как достичь Low Coupling

1. Dependency Injection (DI)

Внедряй зависимости через конструктор, а не создавай их внутри:

// Плохо
class OrderService {
  constructor() {
    this.paymentService = new PaymentService();
  }
}

// Хорошо
class OrderService {
  constructor(paymentService) {
    this.paymentService = paymentService;
  }
}

2. Программирование к интерфейсу (Interface Segregation)

Завись от интерфейса/контракта, не от конкретной реализации:

// Интерфейс
interface Logger {
  log(message: string): void;
}

// Разные реализации
class ConsoleLogger implements Logger {
  log(message: string) { console.log(message); }
}

class FileLogger implements Logger {
  log(message: string) { /* write to file */ }
}

// Service зависит от интерфейса, не от конкретного LoggerService
class UserService {
  constructor(private logger: Logger) {}
  
  async createUser(name: string) {
    this.logger.log(`Creating user: ${name}`);
  }
}

3. Event-Driven Architecture

Вместо прямых вызовов, используй события:

// High Coupling
class UserService {
  constructor(emailService, analyticsService, notificationService) {
    // много зависимостей!
  }
  
  async createUser(data) {
    const user = await this.save(data);
    await this.emailService.sendWelcome(user);
    await this.analyticsService.track('user_created', user);
    await this.notificationService.notify(user);
    return user;
  }
}

// Low Coupling
class UserService {
  constructor(private eventBus) {}
  
  async createUser(data) {
    const user = await this.save(data);
    this.eventBus.emit('user.created', user); // просто отправляем событие
    return user;
  }
}

// Другие сервисы слушают событие независимо
eventBus.on('user.created', async (user) => {
  await emailService.sendWelcome(user);
});

eventBus.on('user.created', async (user) => {
  await analyticsService.track('user_created', user);
});

Преимущество: UserService не знает о существовании других сервисов. Можно добавлять новые обработчики без изменения UserService.

4. Layered Architecture

Разделяй на слои и зависимости идут только внутрь:

Presentation (Controllers, Routes)
    ↓
Application (Use Cases, DTOs)
    ↓
Domain (Entities, Business Logic)
    ↓
Infrastructure (DB, API clients)

Presentation НЕ зависит от Infrastructure. Domain НЕ зависит от ничего (самый независимый слой).

Low Coupling в NestJS

NestJS хорошо поддерживает низкую связанность через встроенный DI контейнер:

@Injectable()
export class UserService {
  constructor(
    private emailService: EmailService, // внедряется автоматически
    private logger: Logger
  ) {}
  
  async createUser(data: CreateUserDto) {
    const user = await this.save(data);
    this.logger.log(`User created: ${user.id}`);
    await this.emailService.sendWelcome(user);
    return user;
  }
}

Признаки High Coupling

  • Класс использует оператор new для создания зависимостей
  • Класс знает много деталей о других классах
  • Сложно написать unit тест без создания много объектов
  • Изменение одного модуля ломает множество других
  • Есть циклические зависимости между модулями

Вывод

Low Coupling — это основа чистой архитектуры. Когда компоненты слабо связаны:

  • Система становится модульной
  • Легче тестировать
  • Легче добавлять новые фичи
  • Легче менять реализацию без ломания кода
  • Команда быстрее разрабатывает

Это инвестиция в качество и скорость разработки в долгосрочной перспективе.