四道闸门串成一条通道,每道闸门上挂着一块写有计数规则的牌子,通道尽头是一份统一格式的商品单

一、四段是四种代价,进入条件必须能数出来

多货源、多支付渠道、多云对接的系统最后都会撞上同一件事:每加一个来源要改几处代码。这类演进常被写成四段,但把它们读成“演进路线”是误读。四段各自解决的是四种不同的代价:分支重复、故障传染、映射散写、追踪链路断裂。每一段的进入条件都必须能被数出来,否则抽层本身会成为负债。

第一段是条件分支。它适用于货源数量极少、业务仍在验证的时候,此时过早抽象的成本高于收益,这是正确判断而不是偷懒。进入下一段的条件是“同一处改动要在三个以上分支里重复”。注意量词是同一处改动重复几次,而不是分支有几个。五个分支各改各的还不必抽层,两个分支被同一处改动牵着走就已经该抽。

第二段抽适配层。抽完之后上层只认两个字段:来源,以及来源内的商品标识。它的价值不在好看,而在把来源差异关进一个数得清的地方。

第三段是配额与限流分层。触发判据不是“想做得更细”,而是一件具体的事:某一货源被限流,导致整批同步失败。这时必须给每个来源配独立的令牌桶与独立的熔断器,共用等于没做隔离。

第四段是元数据配置化。判据同样是两个数:适配器超过五个,且改动多数是字段映射。两条同时成立才值得做配置化;只满足前一条就把映射逻辑塞进配置,通常只是换一种写法继续改代码。

段 它消除的代价 可数的进入判据 不该进时的表现
条件分支 无 来源极少且业务未定 过早抽层,接口频繁返工
适配层 分支重复 同一处改动重复于三个以上分支 抽完仍要改上层,说明抽象划错
配额与限流分层 故障传染 单来源限流曾拖垮整批同步 为一个假设中的规模做隔离
元数据配置化 映射散写 适配器超过五个且多为字段映射 配置里塞满条件判断

二、什么时候不该进下一段

业务本身还在频繁变更、映射规则尚未稳定的时候,抽层会把不确定性固化成接口。适配层是一种契约,契约的价值来自两侧都不轻易变。如果字段每周换含义,抽象就要每周返工,而每次返工都要同时动适配器与上层,变更半径没有缩小,只是从看得见的分支变成看不见的接口版本。

三个数要分开看:来源数量决定第一段能不能待住,同一处改动的重复次数决定什么时候动第二段,改动类型的稳定性决定要不要走到第四段。不要把它们合成一个“复杂度”评分,合成评分之后没有人能复现出为什么该抽这一层。

另一类反判据也要点名。如果一次抽层的理由只是这段分支看着乱,那它没有进入条件,只是审美。审美不构成为团队增加一层的理由,能被数出来的重复才构成。

三、三个抽象方法,划分理由是变更节奏不同

适配器的抽象方法固定为三项:签名规则、字段映射、错误码翻译。这三项不是随手挑的,它们分别对应三种互不重叠的差异,各有各的变更节奏。

签名是协议差异。它在鉴权方式更换、密钥轮换、上游新增时间戳与随机串要求的时候变。字段映射是数据形状差异。它在来源改字段名、拆字段、改单位的时候变。错误码翻译是语义差异。它在来源新增一类失败,或者己方对某类失败的处理策略改变的时候变。

把签名与映射写在一起,后果非常具体:换一种鉴权方式会牵动全部字段处理逻辑,一次密钥轮换就得重测所有来源的商品入库。把错误码翻译塞进映射,来源新增一个错误码就变成一次数据格式的回归。三项分开的代价是多写两个方法,好处是三类变更互不牵连。

配套一条纪律:适配器只负责把外面的东西变成里面的东西,不负责业务判断。一旦适配器里出现库存够不够、价格能不能上架的判断,三类变更又重新粘在了一起。

四、内部模型:价格按最小货币单位存整数,币种成对出现

内部标准模型的字段是来源、来源内标识、标题、价格与币种、库存。字段本身平常,关键在两条约束。

第一条,价格一律以最小货币单位的整数存储,避免浮点误差。这不是精度洁癖,它决定了对账能不能自动化。换算位置固定两处。四则运算阶段全程整数,加减直接做,乘除只允许出现在明确的摊分与汇率场景,而且必须显式写出取整方向和取整步骤,因为方向不同会让同一批订单在两侧差出最小单位的一档。展示层是唯一允许把整数还原成带小数位金额形态的地方,且小数位数由币种决定,不能由前端自行猜测。

第二条,金额与币种必须成对出现,结构里不允许存在只有数字没有币种的字段。落地方式是两者在同一层结构中共同声明,序列化、日志、导出全部成对携带。一旦某处只带数字,另一个币种的数据混进来时不会报错,只会静默算错,而静默算错是最贵的一类缺陷。

库存与价格同构:单位口径要写进元数据。不同来源的“一件”未必是同一种计量,把这句话写进模型注释比写进对接文档有用。

五、未注册即报错,限流必须配熔断

注册表在查到未注册来源时直接报错,而不是返回空或者降级。这个取舍要讲清楚。静默降级的后果,是少配一个来源变成线上少一批商品,而且没有任何人被通知,等业务方发觉时已经跑过好几轮同步。显式失败的后果,是配置缺失变回它本来的样子:一次发布问题,在发布阶段就暴露,回滚动作明确,影响面清楚。前者把工程错误伪装成业务波动,后者把波动留在工程阶段。宁可让一次发布失败,也不要让一批商品静默消失。

限流与熔断必须成对,缺哪一边都有明确的失效路径。只有令牌桶而没有熔断器时,慢来源仍然会占满连接与线程,配额挡住了请求数量,挡不住请求时长,最后整批同步被一个反应迟钝的来源拖死。只有熔断器而没有令牌桶时,突发流量仍然会打穿配额,因为熔断是在故障发生之后才动作的。

隔离还要分三层看,只做其中一层等于没做。

层次 隔离对象 只做这一层时漏掉了什么
连接池 每个来源独占的连接资源 并发度与配额仍共用,慢来源仍能占满线程
并发度 同时在飞的请求数 上游配额仍被超出,限流照旧触发
配额 单位时间的请求量 连接仍共用,一个来源耗尽连接会波及其他

六、把变更半径做成度量,把同步字段定全,把验收压成两件事

“同一处改动要改几处”不能只当进入判据,它应该是常规度量。做法很轻:每一次跨来源变更,统计两个数,被修改的方法数,以及事后发现的遗漏方法数。两个数写进变更单,不必精确,只要有人在看。

一旦出现过“漏改一个”,处置动作也是明确的。有一起真实事故是这样:给多个方法统一加上超时重试与链路标识,六个方法改完漏了一个,上线后这一类请求的日志缺少链路标识。漏掉的是横切逻辑而不是业务逻辑,这正是该把它上移到拦截层或者统一包装的信号。判据可以写死成一句话:同一项横切逻辑需要手工改到第三个方法时,就不再改第四个,改为在拦截层做一次覆盖。

可观测字段每次同步都要带五项:来源、请求体量、上游耗时、错误码翻译前后的两个值、映射失败时的原始字段名。第四项决定故障归因能不能落到“是上游改了还是我们改了”;缺第五项的时候,来源改字段只能靠人工比对报文样本,一次改十个字段根本比不完。

最后一条给做中台的人。适配层的价值不在于有这一层,而在于让接入一个新来源变成一次配置加一个适配器的有界变更。验收只看两件事:新增一个来源要改几处;能不能在不发版的情况下停用某个来源。第一件测抽象是否真的成立,第二件测开关与注册表是否把来源当成可配置对象。两件事里任何一件答不上来,就说明抽层还没做完。洹拓把这两问当成这类改造的唯一交付判据,原因很简单,它们没法靠“代码看起来更清楚了”蒙过去。