把「一对多」压成主表的一列。
| 我写的 | 为什么错 |
|---|---|
users.lark_open_id |
一个人可以同时有 Lark 和 Teams —— 本来就是一对多。 压进列的后果:每加一家 IM 就要 ALTER TABLE 一次,这才是真耦合 |
customers.sales_owner_id |
一个客户可以两个销售一起跟,或者销售 + 产品各一个 —— 也是一对多。 而且这是倒退:现有 profile.md 已经支持
sales_owner/pm_owner/fde_owner 三种角色、每种逗号分隔多人 |
而且 lark_open_id 直接打了我自己的脸 —— 上一轮刚说完
「接口边界不能漏任何 Lark 的东西」,转手就在核心表上加了一个叫 lark 的列。
user_channels —— 那个论证是错的我当时说:「多一张表就多一处会漂移的地方」。听起来像原则,其实是把话说错了:
| 真正的原则 | 我说成了 |
|---|---|
| 同一个事实不要存两处 | 不要多建表 |
user_channels 存的是主表没有的事实(这个人在 Lark 里的 id),
不是副本 —— 它不制造漂移。
我把「避免副本」错误地泛化成了「避免建表」,然后为此放弃了正确的结构。
| 表 | 解决 | 加 Teams 时要改什么 |
|---|---|---|
user_channels |
「这个人在各家 IM 里分别是谁」 | 什么都不用改 —— 多插一行 channel='teams' |
customer_owners |
「这家客户谁在跟」 可多人 · 带角色 |
无关 |
deliveries |
「这条消息到底送到没有」 | 无关 —— channel 本来就是一个值 |
notify 仍留在 users:「这个人在这个组织收不收消息」是单值,不是一对多。
将来若要「Lark 收、邮件不收」,那是 user_channels 上加一列的事。
| 场景 | 怎么表达 |
|---|---|
| 两个销售一起跟一家客户 | customer_owners 两行,role='sales'。推送时两个人都在收件人里 |
| 一个产品 + 一个销售 | 两行,role 分别是 pm / sales。角色不同,但都该知道 |
| 一个人同时用 Lark 和 Teams | user_channels 两行。投递层按 status='active' 挑 |
| 某人 Lark 绑定坏了 | 那一行 status='broken',其他人照发;deliveries 里留一条 failed |
customer_owners 建起来之后,「归属」从 profile.md 的一行文本
变成了带 FK 的行。这正是 7 月客户那边做过的事:
| 7 月 · 客户 | 现在 · 人 |
|---|---|
用名字引用 → アトレ / アトレ_GBaseSupport 漂移 → pipeline fail-close |
用名字引用 → Alex (彦序) / Alex(彦序) → 20 人收不到消息 |
migration 0039 改用 customers.id | 这次改用 users.id |
同一个病,同一个药。客户那边已经吃过了,人这边补上。