Як впровадити DI у багатошаровій архітектурі?
DI (Dependency Injection) - це спосіб впровадження залежностей між шарами так, щоб кожен шар не створював об'єкти сам, а отримував їх «ззовні»: через конструктор, параметри або контейнер залежностей.
У багатошаровій архітектурі DI потрібен, щоб послабити зв'язки між шарами і зробити систему гнучкою, тестованою та керованою.
1. Проблема без DI
Без впровадження залежностей код виглядає так:
class UserService:
def __init__(self):
self.repository = UserRepository() # залежність створюється всерединіТепер UserService жорстко прив'язаний до конкретної реалізації UserRepository.
Якщо ти захочеш замінити базу або протестувати сервіс - доведеться переписувати код.
2. Принцип DI у багатошаровій архітектурі
Ідея в тому, щоб кожен шар не створював залежність, а отримував її «готовою» від верхнього рівня або контейнера:
class UserService:
def __init__(self, repository):
self.repository = repositoryТепер можна передати будь-яку реалізацію:
repo = PostgresUserRepository()
service = UserService(repo)→ UserService більше не залежить від того, яка база використовується.
→ Тести можуть підставити FakeRepository.
3. Де впроваджувати залежності по шарах
| Шар | Що впроваджується | Куди |
|---|---|---|
| Презентаційний шар | Сервіси бізнес-логіки | У контролери / view |
| Бізнес-логічний шар | Репозиторії, провайдери | У сервіси / менеджери |
| Шар даних | Підключення, драйвери | У репозиторії |
| Інфраструктурний шар | Кеш, логери, API-клієнти | У сервіси або обробники подій |
4. Як це виглядає в реальності
Приклад на Python (без фреймворків):
class UserRepository:
def get_user(self, id):
...
class UserService:
def __init__(self, repository):
self.repository = repository
def get_user_info(self, id):
user = self.repository.get_user(id)
return {"name": user.name, "email": user.email}
class UserController:
def __init__(self, service):
self.service = service
def handle_request(self, id):
return self.service.get_user_info(id)
# Впровадження залежностей на верхньому рівні:
repository = UserRepository()
service = UserService(repository)
controller = UserController(service)5. DI-контейнери (автоматизація)
У великих проєктах використовують DI-контейнер - компонент, який сам створює і передає залежності в потрібні місця:
- Python:
dependency-injector,injector - Java: Spring Framework (
@Autowired) - C#: .NET Core Dependency Injection
- JavaScript/TS: NestJS, InversifyJS
Контейнер керує всім деревом залежностей:
container = Container()
container.wire(modules=[controllers, services, repositories])6. Головне правило
Залежності завжди спрямовані вниз - верхній шар знає про нижній, але не навпаки.
DI дозволяє змінити реалізацію нижнього шару, не чіпаючи верхній.
Підсумок: DI у багатошаровій архітектурі - це механізм, який впроваджує зв'язки між шарами через інтерфейси, а не через жорстке створення об'єктів. Він робить код гнучким, тестованим і готовим до масштабування без «ефекту доміно».
Коротка відповідь
Для співбесідиКоротка відповідь допоможе вам впевнено відповідати на цю тему під час співбесіди.