🎭 Паттерн "Фасад": когда простота важнее гибкости
Видел код, где для одной операции вызываешь 5 разных модулей? Это сигнал, что пора подумать о Facade.
Что такое Фасад? Это единая точка входа в сложную подсистему. Вместо того чтобы разбираться в десятке зависимостей, ты работаешь с одним простым интерфейсом.
Реальный пример: отправка уведомления
Без фасада — жесть: // ❌ Клиент знает ВСЁ import { EmailService } from './email' import { SMSService } from './sms' import { PushService } from './push' import { Logger } from './logger' import { Analytics } from './analytics'
async function notifyUser(user, message) { logger.log('Sending notification...')
if (user.preferences.email) { await emailService.connect() await emailService.send(user.email, message) await emailService.disconnect() }
if (user.preferences.sms) { await smsService.authenticate() await smsService.sendSMS(user.phone, message) }
analytics.track('notification_sent') // ...и так далее }
С фасадом — красота: // ✅ Просто и ясно import { notifyUser } from './notifications'
await notifyUser(user, 'Ваш заказ готов!')
Внутри фасада: export async function notifyUser( user: User, message: string ) { const channels = getUserChannels(user)
await Promise.allSettled( channels.map(ch => ch.send(message)) )
analytics.track('notification_sent') logger.info('Notification delivered') }
Когда Фасад — то что нужно?
✔ Интегрируешь сложную библиотеку ✔ Работаешь с легаси-кодом ✔ У подсистемы куча зависимостей ✔ Нужен единый API для разных реализаций ✔ Хочешь изолировать команду от деталей
Функциональный подход
В TS/JS фасад — это просто модуль с функциями:
// payments/facade.ts export async function processPayment( orderId: string, amount: number ) { const order = await validateOrder(orderId) const payment = await gateway.charge(amount) await notifications.send(order.userId, 'Оплата прошла') await analytics.trackPurchase(order, payment)
return { success: true, paymentId: payment.id } }
Клиенту не нужно знать про gateway, notifications и analytics — он просто вызывает processPayment().
Цена использования
⚠️ Меньше контроля Ты теряешь гибкость. Если клиенту нужна нестандартная операция, фасад может встать на пути.
⚠️ Дублирование интерфейсов Иногда приходится пробрасывать параметры, которые нужны только одному из внутренних сервисов.
Когда НЕ нужен?
❌ Подсистема простая (2-3 функции) ❌ Клиенту нужна полная гибкость ❌ Фасад становится "богом", который делает всё ❌ Уже есть хороший API
Правило
Если клиент должен знать о 3+ зависимостях для одной операции — делай фасад. Если операция простая — не усложняй.
Бонус: тестирование
С фасадом тесты проще — мокаешь один модуль вместо пяти:
vi.mock('./notifications', () => ({ notifyUser: vi.fn() }))
test('processes order', async () => { await processOrder(order) expect(notifyUser).toHaveBeenCalled() })
Вывод
Фасад — это не про "красивый код", а про заботу о клиентах твоего API. Прячь сложность, оставляй простоту.
Используете фасады в своих проектах? Поделитесь опытом! 💬
#architecture #patterns #facade #typescript #bestpractices #frontend