導入:SaaSの通知チャレンジ

現代のSaaS製品は、ユーザーをエンゲージメントし、ユーザーをドライブ保持し、重要なシステムイベントを通信するために、タイムリーでパーソナライズされた通知に依存しています。ユーザーは、希望するチャネルを介してアラートを受信することを期待しています。電子メール、SMS、プッシュ通知、アプリ内メッセージ、またはサードパーティサービスへのWebhookさえ。製品が進化するにつれて、チャネルの数が成長し、それらを管理することは維持ナイトマーレになります。クリーンでスケーラブルなアーキテクチャが必要です。

オープンソースのヘッドレスCMSとバックエンドプラットフォームであるDirectusは、SaaSアプリケーションの構築に柔軟に基盤を発揮します。そのデータモデル、フローエンジン、および拡張可能なホックにより、堅牢な通知システムを実行するための理想的な環境を実現します。クラシックな作成設計パターンを適用することで、ファクトリーパターンは、チャネル固有の作成ロジックをカプセル化し、コアビジネスコードを配信詳細からデカップリングし、製品が成熟するにつれて新しいチャネルを楽に追加することができます。

工場パターンを理解する

Factory Pattern は、オブジェクトをスーパークラスで作成するためのインターフェイスを提供する作成設計パターンです。しかし、サブクラスは生成されるオブジェクトの種類を変更することができます。それは緩いカップリングを促進し、オープン/クローズド原則に付着します。ソフトウェアエンティティティは、拡張のために開いている必要がありますが、修正のために閉じられます。あなたのアプリケーションをまたは[を通知する代わりに、あなたは、工場を専任する通知に指示します。

関連するオブジェクトの家族やインスタンス化の正確なタイプが、ユーザーの通知の好みやトリガーされたイベントなど、動的入力に基づいてランタイムで決定されると、このパターンは輝きます。通知コンテキストでは、すべてのチャネルは異なる「製品」ですが、すべての一般的なインターフェイス(例えば、]メソッド)を共有しています。

SaaS製品における通知チャネル

一般的な通知チャンネルには、以下が含まれます。

  • メール] – トランザクションおよびマーケティングメールは、SendGridやMailgunなどのサービスを介して。
  • SMS - TwilioまたはVonageによるショートメッセージ。
  • []プッシュ通知[]] - FirebaseクラウドメッセージングまたはOneSignalによるWebプッシュ、モバイルプッシュ。
  • []In-App 通知[] - UI のトーストまたはダッシュボードのアラート。
  • []Webhooks] – 統合のための外部エンドポイントへのHTTP POSTリクエスト。
  • Slack/Discord – チームフォーカスメッセージング.

パターンがなければ、コードはこのように見えます。

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');
 }
}

このアプローチは、いくつかのチャネルで動作しますが、すべての新しいチャネルは、送信機能を変更し、既存のすべてのロジックを再テストすることを可能にします。 工場パターンは、この脆弱性を排除します。

工場パターンの通知の実装

以下は、Directus ベースの SaaS に適応できる TypeScript 実装を説明します。プラットフォーム自体がそれと組み込まれ、豊富なタイプ システムを提供するため、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 は、通知をトリガーするための複数のエントリ ポイントを提供しています。

  • []Flows] – カスタム操作やWebhookを呼び出すことができるビジュアルオートメーション。
  • []Hooks – [ または[]] クラウドイベントで実行されるホック。
  • []カスタムエンドポイント] - エクスプレス・ルートは、Directusサーバーに注入されます。
  • ]Webhooks – アウトゴング・ウェブホック・ディスパッチャ。

Directus プロジェクトでは、通知工場をカスタムモジュールまたは拡張機能のサービスとしてインストールできます。例えば、[]]]の内側にある]の内側に、新しいユーザーサイン後に発火して、SMS通知や通知を送受信する内部APIエンドポイントを呼び出すことができます。また、工場をhookにラップして、新しいユーザーサイン後に火を呼び、通知を送ったり、通知を送ったり、通知をしたり、通知を送ったりすることができます。

工場は一箇所に住んでいるため、すべてのホクやフローに触れずにマッピングを更新できます。また、Directus Flow は任意の JSON データを渡すことができるため、簡単にデータベースからチャネル識別子で工場に供給することができます。

工場パターンアプローチの利点

スケーラビリティ

新規チャネルを追加すると、 を実装し、工場のスイッチステートメントに 1 行を追加した新しいクラスを書くことを意味します。クライアントコードの変更は必要ありません。これは、異なるテナントが異なるチャネルのセットを有効にすることができるマルチテナント SaaS 製品に特に価値があります。

メンテナンス性

通知配信ロジックは、ビジネスロジックから分離されます。各コンクリートクラスは独立して維持できます。SMSプロバイダがAPIを変更した場合、のみを変更します。工場とすべての消費者は変更されません。

試験可能性

分離中の通知クラスをそれぞれテストし、 インターフェイスをモックして、より高いレベルの関数をテストすることができます。 工場自体は、各チャネルの正しいタイプのインスタンスを返すことを確認する簡単なテストで検証することができます。

柔軟性

工場は複雑なオブジェクトをビルドするために拡張することができます。例えば、作成時に設定(APIキー、再試行ポリシー)を渡したいかもしれません。工場メソッドをオーバーロードしたり、Builderパターンを工場内で使用して、完全に設定された通知オブジェクトを組み立てることができます。Directusの環境変数またはデータベース設定(例えば、)は、そのような設定のための理想的なソースです。

潜在的な欠点と考慮事項

工場パターンは強力なツールですが、銀製の弾丸ではありません。

  • []オーバーエンジニアリング:[]]]]]) 2つのチャネルしか持っていなければ、より単純な条件がうまくいくかもしれません。 パターンは追加のファイルと抽象化を紹介します。
  • [ 決定ロジックはまだ存在します:[ 工場自体は、スイッチステートメントを使用します。 チャンネルの数十を持っている場合は、オブジェクトではなくアルゴリズムを交換する[レジストリパターン[]]チャネルの自己登録、または[戦略パターン]を検討してください。 どちらが工場を補完することができます。
  • [ 依存症例:[]]] 通知クラスが外部サービスに依存している場合(HTTPクライアントやロガーなど)、それらの依存関係を注入する必要があります。 工場は、DIコンテナを受け入れるか、パラメータとして依存性を受け取る必要があります。

ダイレクトスエクステンションでは、プラットフォームの組み込み依存注入([関数とサービス)を利用して、ロガーやデータベースなどの共有インスタンスを工場に渡すことができます。

実世界例:B2B SaaSにおけるマルチチャネル通知

直接に構築されたプロジェクト管理ツールを検討してください。タスクが割り当てられた場合、システムに通知しなければなりません。

  • 電子メールによる代入(希望する)
  • 入出力アプリのトーストによるアサイン
  • 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の公式ドキュメントを参照してください。 工場パターン自体の深い理解のために、 工場メソッドのGruページをRefactoring は優れたリソースです。 また、他のプラットフォームが通知オーケストレーションを処理する方法に興味があるかもしれませんTwilio [FLT] 同様のコンセプトを提示します。 [FLT: 同様のコンセプト]

コンテンツ

Factory Pattern は、SaaS 製品で複数の通知チャネルを管理するための、清潔でメンテナンス可能な方法を提供します。オブジェクトの作成をカプセル化することで、デリバリーの特異からコアアプリケーションロジックをデカップリングし、コードベースを渡るripple効果なしでチャネルを追加、削除、または変更するのを簡単にします。Directus の柔軟なホックとフローと組み合わせると、製品の成長をスケールする通知アーキテクチャを得ることができます。

シンプルなメール通知や複雑なマルチチャネルエンジンを組み込む場合でも、工場パターンから始めて、機能セットが拡大するので、初期に支払います。 少量の抽象化への投資は、明日の再構築の日を節約します。