Table of Contents
导言:萨阿斯的通知挑战
现代SaaS产品依赖及时的、个性化的通知来吸引用户,驱动保留,并传达关键系统事件。 用户期望通过他们喜欢的渠道——电子邮件、短信、推送通知、应用中的信息,甚至网络呼号来获取第三方服务。 随着产品的发展,渠道数量的增长,通过分散的有条件逻辑管理它们成为维护的噩梦。 需要一个干净的、可扩展的架构。
Directus,一个开源的无头CMS和后端平台,为SaaS应用程序的建设提供了灵活的基础。它的数据模型Flows引擎和可扩展的钩子,使它成为实施强健通知系统的理想环境。 通过应用经典的创造设计模式 — — 工厂模式 — — 你可以包罗频道特定创建逻辑,将你的核心业务代码与交付细节脱钩,并随着产品成熟而无心添加新的通道。
了解工厂模式
Factory Pattern是一种创造性的设计模式,它为在超级-Q类中创建对象提供了接口,但允许子类更改将要创建的对象类型。它促进松散的耦合并遵守开放/关闭的原则[]:软件实体应该开放扩展但关闭以供修改。您不要将您的应用程序与或语句混在一起,而要决定哪个通知反对即时处理,您将这一责任委托给一个专用工厂。
当您拥有一个相关对象的家族,而要即时处理的确切类型则根据动态输入确定时——例如用户的通知偏好或正在触发的事件——时,这种模式会闪耀。 在通知中,每个信道都是不同的“产品”,但它们都有一个共同的界面(例如 方法)。
SaaS 产品中的通知通道
共同的通知渠道包括:
- 电子邮件 –通过SendGrid或Mailgun等服务进行交易和营销邮件.
- SMS –通过Twilio或Vonage发送短信息.
- 推送通知 – 网页推,移动推通过Firebase云端信使或OneSignal.
- In ⁇ App通知 – UI土司或仪表板警报.
- Webhooks – HTTP POST 请求外部端点进行集成.
- Slack / Discord – team 焦點訊息.
没有规律,你的代码可能是这样的:
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执行,可以适应一个基于Directus的SaaS. 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);
}
客户端代码从不需要知道具体类型。 它只取决于[ [FLT: 8]] 接口。 这种脱钩使单位测试直接—— 您可以不接触工厂或混凝土类就嘲笑通知对象 。
将工厂模式与直接连接
Directus为触发通知提供了多个切入点:
- Flows – 视觉自动化,可以称自定义操作或webhooks.
- Hooks –或]]在CRUD事件上运行的钩子.
- 海关端点 – 注入Directus服务器的快递路线.
- Webhooks – 出行的网络hook调度器.
在Directus项目中,您可以将您的通知厂安装为自定义模块或扩展内的一项服务。例如,在一个 Flow [ 内,您可以调用一个内部API端点,根据用户存储的首选,将正确的信道即时化为元。 或者,您可以将工厂包装在 hook 中,在新用户注册后发射,并发送欢迎邮件、短信确认和向移动应用程序发送推送通知。
因为工厂住的地方只有一个,所以你可以不触摸每个钩子或流量而更新其绘图。由于Directus Flows可以通过任意的JSON数据,因此可以从数据库中用频道识别符方便地向工厂提供数据。
工厂模式办法的益处
可缩放性
添加新频道意味着写一个执行 的新类别,并在工厂的开关语句中添加一行。不需要修改客户端代码。这在多租户SaaS产品中特别有价值,因为不同租户可以允许不同的频道。
维持能力
通知发送逻辑与商业逻辑是隔离的。每个混凝土类都可以独立维护。如果短消息提供者更改了API,则您只修改。工厂和所有消费者保持不变。
可检验性
您可以单独测试每个通知类, 在测试更高水平的函数时可以模拟 接口。 工厂本身可以通过简单的测试进行验证, 测试时可以检查它返回每个信道的正确类型。
灵活性
工厂可以扩展为构建复杂的对象。 例如, 您可能想要在创建时通过配置( API 密钥, 重试策略) 。 您可以在厂内超载厂方方法或使用 [[FLT: 0]] 构造 [[FLT: 1] 模式来组装完全配置的通知对象。 Directus 的环境变量或数据库设置( 例如 [[FLT: 14]] 表格) 是这种配置的理想来源 。
潜在的缺点和考虑
虽然工厂的图案是强大的工具,但并非银弹:
- Over 工程: 如果您只有两个频道,没有计划添加更多,那么简单的条件可能很好。模式引入了额外的文件和抽象。
- 判断逻辑仍然存在: 工厂本身使用开关语句。如果有几十个频道,请考虑登记模式[,频道自行登记,或使用 交换算法而不是对象的战略模式[。两者都可以补充工厂。
- 依赖性注射: 如果您的通知类依赖于外部服务(例如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关于创建定制钩子和服务的官方文件。为了更深入地了解工厂图案本身,[ 重新构建工厂方法的Guru页面[是一个极佳的资源。你还可能有兴趣了解其他平台如何处理通知的管弦—— Twilio Notify在基础设施一级展现了类似的概念。
结论
工厂模式提供了一种干净、可维护的方法,可以管理SaaS产品中的多个通知通道。 通过封装对象创建,您可以将核心应用逻辑与交付细节脱钩,从而方便地添加、移除或修改通道,而不会在代码库中产生连锁效果。 当与Directus的灵活钩和流相结合时,您可以得到一个通知架构,以放大您的产品增长。
无论是在建立简单的电子邮件通知器还是复杂的多频道引擎, 首先是工厂模式随着功能的扩展而提前还清。 今天对少量抽象化的投资可以节省明天的重构时间 。