Куди варто виносити бізнес-логіку з контролерів?
Коротко: контролер має бути тонким - отримувати вхідні дані, викликати потрібний "use-case/сервіс" і формувати відповідь. Уся бізнес-логіка виноситься нижче по шарах. Ось куди саме:
Куди виносити логіку
- Сервіси / Use-cases (Application layer)
- Що тут: оркестрація сценаріїв, транзакції, виклики репозиторіїв, інтеграції, доменні перевірки високого рівня.
- Контролер →
userService.createUser(dto)→ результат/виняток. - Іменування за діями:
CreateUser,ChangeEmail,PlaceOrder.
- Домений шар (Domain layer)
- Entities/Value Objects з інваріантами й бізнес-правилами (без залежностей від Express/БД).
- Приклад:
User.changeEmail()валідує правило "не можна змінювати e-mail частіше N разів".
- Репозиторії / DAO (Data access layer)
- Інкапсулюють доступ до БД/ORM (Prisma, Sequelize, TypeORM), зберігають лише CRUD і прості вибірки.
- Інтерфейси в домені, реалізації - в інфраструктурі.
- Валідація схем (Validation layer)
- Схеми вводу/виводу:
zod/joi/express-validator. - Запускати до контролера (middleware) чи в use-case (якщо залежить від доменних правил).
- Контролер лишається чистим від дерев if/else.
- Авторизація/політики (Policy/ACL)
- Правила доступу:
canUpdateProfile(user, target). - Тримати окремо від контролера (policy-функції чи Casbin/Oso).
- Мапери/DTO і форматування
- Перетворення між шарами: запит → DTO → доменна сутність → DTO → відповідь.
- Прибирає витік деталей БД/ORM назовні.
- Крос-нарізні речі - в middleware/інфраструктуру
- Автентифікація, логування, трейсинг, rate-limit, кеш, CSRF, CORS - не в контролері.
- Інтеграції та побічні ефекти
- Клієнти зовнішніх API, e-mail/SMS, черги/задачі (BullMQ/RabbitMQ), кеш (Redis) - в інфраструктурному шарі, викликаються з use-cases.
Коротка відповідь
Для співбесідиPremium
Коротка відповідь допоможе вам впевнено відповідати на цю тему під час співбесіди.