Использование шаблона завода для управления различными каналами уведомлений в продукте SaaS

Введение: проблема уведомлений в SaaS

Современные продукты SaaS полагаются на своевременные персонализированные уведомления для привлечения пользователей, удержания диска и передачи критических событий системы. Пользователи ожидают получать оповещения по своим предпочтительным каналам - электронной почте, SMS, push-уведомлениям, сообщениям в приложении или даже веб-хукам к сторонним службам. По мере развития продукта количество каналов растет, и управление ими через разрозненную условную логику становится кошмаром обслуживания. Нужна чистая, масштабируемая архитектура.

Directus, бесшовная CMS с открытым исходным кодом и бэкэнд-платформа, обеспечивает гибкую основу для создания приложений SaaS. Его модель данных, движок Flows и расширяемые крючки делают его идеальной средой для реализации надежной системы уведомлений. Применяя классический шаблон креационного дизайна - Factory Pattern - вы можете инкапсулировать логику создания, специфичную для канала, отделить свой основной бизнес-код от деталей доставки и легко добавлять новые каналы по мере созревания вашего продукта.

Понимание структуры завода

Фабричный шаблон - это шаблон креационного дизайна, который обеспечивает интерфейс для создания объектов в суперклассе, но позволяет подклассам изменять тип объектов, которые будут созданы. Он способствует свободной связи и придерживается открытого / закрытого принципа: программные объекты должны быть открыты для расширения, но закрыты для модификации. Вместо того, чтобы засорять ваше приложение заявлениями или , чтобы решить, какой объект уведомления инстанцировать, вы делегируете эту ответственность специализированной фабрике.

Этот шаблон сияет, когда у вас есть семейство связанных объектов, и точный тип для инстанцирования определяется во время выполнения на основе динамического ввода, такого как предпочтения уведомления пользователя или запускаемое событие. В контексте уведомления каждый канал является различным «продуктом», но все они имеют общий интерфейс (например, метод [[FLT: 2]]).

Каналы уведомлений в SaaS-продукте

Общие каналы уведомлений включают:

Без шаблона ваш код может выглядеть так:

function sendNotification(channel, message, recipient) {
 if (channel === 'email') {
 // Email logic here
 } else if (channel === 'sms') {
 // SMS logic here
 } else if (channel === 'push') {
 // Push logic here
 } else {
 throw new Error('Unknown channel');
 }
}

Этот подход работает для нескольких каналов, но каждый новый канал заставляет вас изменять функцию отправки и перепроверять всю существующую логику.

Реализация фабричного шаблона уведомлений

Ниже мы рассмотрим реализацию TypeScript, которая может быть адаптирована для SaaS на основе Directus. TypeScript является естественным для Directus, потому что сама платформа построена с ним и обеспечивает богатую систему типов.

1.Определить интерфейс уведомлений

interface Notification {
 send(message: string, recipient: string): Promise<void>
}

2.Бетонные классы уведомлений

class EmailNotification implements Notification {
 async send(message: string, recipient: string): Promise<void> {
 // Use Directus’s internal mailer or an external SDK
 console.log(`Sending email to ${recipient}: ${message}`);
 }
}

class SMSNotification implements Notification {
 async send(message: string, recipient: string): Promise<void> {
 // Call Twilio API
 console.log(`Sending SMS to ${recipient}: ${message}`);
 }
}

class PushNotification implements Notification {
 async send(message: string, recipient: string): Promise<void> {
 // Use Firebase or OneSignal SDK
 console.log(`Sending push to ${recipient}: ${message}`);
 }
}

3. Фабрика уведомлений

type ChannelType = 'email' | 'sms' | 'push';

class NotificationFactory {
 static createNotification(channel: ChannelType): Notification {
 switch (channel) {
 case 'email':
 return new EmailNotification();
 case 'sms':
 return new SMSNotification();
 case 'push':
 return new PushNotification();
 default:
 throw new Error(`Unknown notification channel: ${channel}`);
 }
 }
}

4.Использование фабрики в вашем приложении

async function notifyUser(event: string, userId: string, channel: ChannelType) {
 const notification = NotificationFactory.createNotification(channel);
 await notification.send(event, userId);
}

Клиентский код никогда не должен знать конкретный тип. Он зависит только от интерфейса . Это разделение делает тестирование блоков простым — вы можете высмеивать объект уведомления, не касаясь завода или конкретных классов.

Интеграция фабричного шаблона с Directus

Directus предлагает несколько точек входа для запуска уведомлений:

В проекте Directus вы можете установить завод уведомлений в качестве службы внутри пользовательского модуля или расширения. Например, внутри FLT:0]Flow вы можете вызвать внутреннюю конечную точку API, которая инстанцирует правильный канал на основе сохраненных предпочтений пользователя. Альтернативно, вы можете обернуть завод в крюк ], который загорается после новой регистрации пользователя и отправляет приветственное электронное письмо, подтверждение SMS и push-уведомление в мобильное приложение.

Поскольку фабрика живет в одном месте, вы можете обновлять ее отображения, не касаясь каждого крючка или потока. А так как Directus Flows может передавать произвольные данные JSON, вы можете легко подавать заводу идентификаторы каналов из своей базы данных.

Преимущества подхода Factory Pattern

Масштабируемость

Добавление нового канала означает написание нового класса, который реализует и добавление одной строки в коммутационное заявление завода. Никаких изменений клиентского кода не требуется. Это особенно ценно в продуктах SaaS с несколькими арендаторами, где разные арендаторы могут включать разные наборы каналов.

Устойчивость

Логика доставки уведомлений изолирована от бизнес-логики. Каждый конкретный класс может поддерживаться независимо. Если поставщик SMS меняет свой API, вы меняете только . Завод и все потребители остаются неизменными.

Проверяемость

Вы можете по отдельности тестировать каждый класс уведомлений, а при тестировании функций более высокого уровня вы можете высмеять интерфейс . Сама фабрика может быть проверена простым тестом, который проверяет, что она возвращает экземпляры правильного типа для каждого канала.

Гибкость

Завод может быть расширен для создания сложных объектов. Например, вы можете передать конфигурацию (ключи API, политики повторного использования) во время создания. Вы можете перегрузить заводской метод или использовать шаблон Строитель внутри завода для сборки полностью настроенных объектов уведомлений. переменные среды Directus или настройки базы данных (например, таблица ) являются идеальными источниками для такой конфигурации.

Потенциальные недостатки и соображения

Хотя заводской образец является мощным инструментом, это не серебряная пуля.

  • Сверхинженерия: Если у вас есть только два канала и нет планов добавить больше, простое условие может быть хорошим.
  • Логика принятия решений все еще существует: Сама фабрика использует коммутационное заявление. Если у вас есть десятки каналов, рассмотрите схему реестра , где каналы саморегистрируются, или используйте схему стратегии , которая меняет алгоритмы, а не объекты.
  • Впрыск зависимости: Если ваши классы уведомлений зависят от внешних служб (например, HTTP-клиента или регистратора), вам нужно ввести эти зависимости. Фабрика должна либо принять контейнер DI, либо получить зависимости в качестве параметров — что-то, что нужно планировать на ранних этапах.

В расширении Directus вы можете использовать встроенную функцию и службы впрыска зависимостей платформы (функция и услуги ), чтобы передавать общие экземпляры, такие как регистратор или доступ к базе данных на завод.

Пример из реального мира: многоканальные уведомления в B2B SaaS

Рассмотрим инструмент управления проектами, построенный на Directus. При задании системы необходимо уведомить:

  • По электронной почте (если они предпочитают это)
  • Для того чтобы сделать тост in-app
  • Канал проекта на Slack

Используя завод, код под крюком Directus становится:

import { NotificationFactory } from './services/NotificationFactory';

async function onTaskCreate(payload, { accountability }) {
 const { assigneeId } = payload;
 const userPreferences = await getUserNotificationPreferences(assigneeId);
 for (const channel of userPreferences.channels) {
 const notifier = NotificationFactory.createNotification(channel);
 await notifier.send(`You have a new task!`, assigneeId);
 }
}

Каждый класс уведомлений обрабатывает свою собственную идиоматическую доставку. Завод остается единственным источником истины для отображения каналов.

Чтобы увидеть полную реализацию такой системы, обратитесь к официальной документации Directus по созданию пользовательских крючков и услуг. Для более глубокого понимания самой фабричной модели страница Refactoring Guru на Factory Method является отличным ресурсом. Вы также можете быть заинтересованы в том, как другие платформы обрабатывают оркестровку уведомлений — Twilio Notify демонстрирует аналогичную концепцию на уровне инфраструктуры.

Заключение

Фабричный шаблон обеспечивает чистый, поддерживаемый способ управления несколькими каналами уведомлений в продукте SaaS. Инкапсулируя создание объекта, вы отделяете логику вашего основного приложения от спецификаций доставки, что позволяет легко добавлять, удалять или изменять каналы без волновых эффектов по всей кодовой базе. В сочетании с гибкими крючками Directus и потоками вы получаете архитектуру уведомлений, которая масштабируется с ростом вашего продукта.

Независимо от того, создаете ли вы простой уведомитель электронной почты или сложный многоканальный движок, начиная с заводского шаблона, рано окупается по мере расширения набора функций. Инвестиции в небольшое количество абстракции сегодня экономят дни рефакторинга завтра.