Skip to main content

Чому високорівневі модулі не повинні залежати від низькорівневих?

Високорівневі модулі не повинні залежати від низькорівневих, тому що це порушує стійкість, гнучкість і переюзабельність системи. Цей принцип - основа Dependency Inversion Principle (DIP) з SOLID, і його мета - зробити архітектуру незалежною від деталей реалізації.


1. Що таке високо- і низькорівневі модулі

  • Високорівневі модулі - містять бізнес-логіку, правила, основні сценарії роботи системи. Приклад: OrderService, PaymentProcessor, UserManager.
  • Низькорівневі модулі - займаються деталями реалізації: базою даних, файлами, мережею, API. Приклад: MySQLRepository, EmailSender, FileLogger.

Коли високорівневий модуль напряму використовує низькорівневий, він стає жорстко прив'язаним до конкретних технологій та інструментів, що робить систему крихкою.


2. Що відбувається при прямій залежності

python
class OrderService: def __init__(self): self.db = MySQLRepository() # низькорівнева залежність def create_order(self, order): self.db.save(order)

Проблема:

  • Якщо потрібно перейти з MySQL на PostgreSQL чи MongoDB, доведеться переписувати OrderService.
  • Неможливо протестувати бізнес-логіку без реальної бази даних.
  • Логіка предметної області "знає" про технічні деталі.

Тобто високорівневий код стає залежним від деталей інфраструктури, хоча повинен визначати лише що робити, а не як саме.


3. Після перевороту залежності

python
class Repository(Protocol): def save(self, entity): ... class OrderService: def __init__(self, repository: Repository): self.repository = repository def create_order(self, order): self.repository.save(order)

Тепер:

  • OrderService залежить від абстракції, а не від конкретного класу.
  • Низькорівневий код (MySQLRepository, MongoRepository) реалізує цю абстракцію.
  • Конкретна реалізація підставляється ззовні (через IoC-контейнер чи вручну).

Таким чином залежність перевертається: деталі залежать від абстракцій, а не навпаки.


4. Навіщо це потрібно

  1. Ізоляція бізнес-логіки - зміни в інфраструктурі (БД, логування, API) не вимагають зміни бізнес-коду.
  2. Тестованість - можна підставляти моки і фейкові реалізації без реальних залежностей.
  3. Гнучкість і масштабованість - легко замінити технології без рефакторингу ключових модулів.
  4. Чиста архітектура - верхні шари (use cases, бізнес-логіка) незалежні від нижніх (база даних, UI).

5. Формулювання принципу Dependency Inversion

  1. Модулі високого рівня не повинні залежати від модулів низького рівня. Обидва повинні залежати від абстракцій.
  2. Абстракції не повинні залежати від деталей. Деталі повинні залежати від абстракцій.

Підсумок: Високорівневі модулі не повинні залежати від низькорівневих, тому що це призводить до жорсткої зв'язаності, ускладнює підтримку і тестування. Замість цього обидві сторони повинні залежати від спільних абстракцій: бізнес-логіка описує "що має бути зроблено", а деталі реалізації підлаштовуються під неї. Такий підхід робить систему гнучкою, модульною і стійкою до змін.

Коротка відповідь

Для співбесіди
Premium

Коротка відповідь допоможе вам впевнено відповідати на цю тему під час співбесіди.