项目概述
本项目实现多渠道通知分发器:按用户首选渠道投递,对同一事件去重,重试瞬时失败,并记录每次投递尝试。通知功能在提供商失败、用户重复收到或发到从不查看的渠道之前,看起来只是一行调用。
架构与关键决策
分发器消费通知事件,解析用户渠道偏好,按渠道渲染模板,交给对应适配器;适配器接口刻意很窄——输入已渲染消息、输出分类后的结果——使假适配器可替代真实提供商,并阻止分发器被渠道分支撑大。每次尝试持久化渠道、响应与状态,使是否已通知有可查询的答案;去重在存储层强制,因为两个实例可能并发消费同一事件,只有共享键才能把竞态收敛为一次发送。
实施与测试
用可返回瞬时或永久失败的假适配器断言:重复事件只发送一次,瞬时失败退避重试,永久失败记录且不重试;断言偏好解析选中预期适配器;断言去重键在发送确认前写入。
限制与后续
- 不支持静默时段与按通知类型的偏好。
- 无应用内通知中心或已读状态。
- 渠道内无提供商故障转移。
代码示例
pnpm init
pnpm add pg nodemailer express
pnpm add -D typescript tsx vitest
notification-dispatcher/
├── src/dispatch.ts
├── src/channels/email.ts
├── src/channels/webhook.ts
├── src/templates.ts
├── src/dedupe.ts
├── tests/dispatch.test.ts
└── package.json
interface Channel { send(msg: Rendered): Promise<{ id: string }>; }
const body = template(params).replace(/[<>&]/g, escapeHtml);
const channel = prefs[event.type] ?? user.defaultChannel;
const inserted = await db.insertIgnore('deliveries', { dedupKey });
if (!inserted) return;
await db.insertAttempt({ notificationId, channel, status, providerId });
pnpm vitest run