🎭 Паттерн "Фасад": когда простота важнее гибкости

Видел код, где для одной операции вызываешь 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