那个铁皮盒子原本是装丹麦曲奇的。蓝绿色,盖子上的小女孩图案磨得只剩下一双眼睛,看得人心里发毛。我妈在上世纪八十年代末把它清空,从此它就成了一份编外档案室。
我小时候一直以为盒子里全是钱。因为它锁在父母房间那个五斗柜的最底层,被几件旧毛衣压着,像一个不太成功的藏宝点。直到今年春天我妈住院,让我回家找她的医保卡,我才第二次——或者说第一次认真地——把它打开。
里面没有藏宝图。有的是:1978年我爸在苏北农场的工分记录、我三岁时的疫苗本、1992年房屋集资款的收据、十几张不同年代的公交月票,还有我小学三年级一张只考了58分的数学卷子。卷子上用圆珠笔写着“下次要细心”——那是我妈的笔迹,不是老师的。
我坐在她床边翻了整整一下午。不是感动。是一种很奇怪的错位感。我做了十一年软件,每天在系统里给字段建模、划分聚合边界、跟产品经理吵“这个属性到底属于订单还是属于用户”,结果我妈用一个铁皮盒子,早就在做同样的事。
两张几乎一样的收据
真正让我愣住的,是盒子里的一叠医疗收据。
我妈把其中几张用回形针别在一起,跟另外一叠用橡皮筋扎着的分开放。我拿起来比对了一下——都是药店打印的小票,格式一模一样,金额差不多,时间跨度也没超过两个月。

“这为什么分开?”我问她。那时候她已经出院回家了,靠在沙发上剥毛豆。
“你看嘛。”她头也没抬,“那边几张是我自己买降压药的,这边几张是给你外婆买的。你外婆那时候还没办慢病卡,全都是自费。自费的不能和报销的放一起,不然以后说起来搞不清楚。”
我说,不都是药吗?
她抬头看了我一眼,那种“你这孩子怎么这么迟钝”的眼神。“药是同一个药。账是两笔账。”
我没有再追问。但脑子里有什么东西咔嗒响了一声。
那段时间我正好在做一个很痛苦的项目。一个保险核心系统,二十多年的代码,经历了至少四轮重构,每一轮都留下了大量“临时方案”。业务方说“保单”的时候,指的是客户签的那份保险合同;技术方说“保单”的时候,指的是数据库里那张主表;运营说“保单”的时候,可能只关心它有没有缴费、有没有到期。同一个词,三个意思。
我们尝试过建一个“统一保单模型”,包含所有可能的字段。结果是那张表有将近两百个列,一半以上常年是空的。改一个字段,两个模块报错,三个报表变样,四个接口超时。谁也说不清楚哪个字段是给谁用的。
后来我们请了一位外部顾问,他推荐了领域驱动设计。核心思路不复杂——别一开始就想着“统一”,先把业务切成不同的边界。在每一个边界里面,语言是一致的;跨边界的时候,做明确的翻译。别让所有人都用同一本词典,除非你确定他们真的在说同一件事。
我当时觉得这个道理很新。现在看着我妈的回形针和橡皮筋,我觉得自己像个笑话。
我妈的限界上下文
我妈没有读过埃文斯那本书。她初中都没读完。但她区分那些收据的逻辑,和领域驱动设计里“限界上下文”的思路,本质上是一回事。
她没有试图建一个“大一统”的账本。她分了几个小的独立单元——我的医疗记录一个信封、我爸单位的事一个信封、人情往来一个笔记本、家里的水电煤一个铁夹子。每个单元内部的规则不一样:医疗的按时间排,水电煤按缴费渠道分,人情往来按对方的家庭记。
有一次我试图帮她把这些东西全部扫描进电脑,按统一的分类标准归档。我建了一套命名规则,什么“年份-类型-金额-备注”,自认为非常科学。她看了一眼,说:“这样我找不到。”
“怎么会找不到?你搜关键词就行。”
“我要找的东西,有时候不是关键词能搜出来的。”她说得很含糊,但我大概懂了。她要找的,是某个具体时刻的记忆——比如1998年那次搬家到底交了多少押金,比如我外婆最后一次住院是什么时候。这些信息在不同的“上下文”里,意义完全不同。在医疗那个文件夹里,它是一笔费用;在家庭记忆那个盒子里,它是一个时间刻度。
我们的“统一分类体系”抹掉了这种差异。正如我们那个两百列的保单表,抹掉了业务部门之间的语义差异。
我们的系统曾经像一间没有墙壁的屋子
有一个细节我记得很清楚。
项目刚开始的时候,我们确实试图跟业务部门“统一语言”。每天早上九点半,会议室的投影仪开着,白板上画着模型图,一群人在争“客户”到底包不包括投保人。

争了两个星期,没有结论。
后来顾问说了一句话:“你们不需要统一。你们需要承认,他们在不同的地方,说的本来就是不一样的东西。承认它,然后给每个地方一个名字。”
这句话我现在都记得。因为当时所有人——包括我——都沉默了好几秒钟。不是因为震惊,是因为大家其实早就知道。只是没人愿意说出口。承认“同一个公司里不同部门用同一个词指不同的事”,在很多团队里,等于承认沟通失败。
我们把系统重新划分了上下文。承保一个、理赔一个、客服一个、对账一个。每个上下文里,有自己的一套模型、自己的一套语言。跨上下文调用的时候,通过明确的接口做转换。不完美。但至少我们知道了,哪一堵墙立在哪里,门开在哪里。
有一个同事说,这不就跟老小区一样嘛。每栋楼有自己的门禁,但楼和楼之间有路连着。以前我们想做的事,是把所有单元打通成一个大通间。结果就是没有一面墙能挂东西。
盒子底下的那张收据
我把铁皮盒子里的东西整理完之后,在盒底最下面发现了一张很薄的纸片。是我出生那年的住院费收据。1982年,南京市妇幼保健院。金额是十四块六毛。
手写的字迹已经洇开了,但数字还能辨认。我妈说她当时不太舍得用这张纸夹在别的东西里,怕折坏了,就一直放在最底下。“反正也不报账。”她说,“就记着你是在这儿来到我们家的。”
这张收据不属于任何“上下文”。它不参与报销、不用于核对、不构成任何业务逻辑。它只是存在在那里。我把它翻过来,背面有一行铅笔字:“七斤二两,哭声很大。”
我后来想,如果我们做的系统里,也能容得下这样一张“不参与任何流程”的记录,那可能比任何花哨的架构图都难。但也许,那才是真正重要的东西。
说不清楚但确实存在
我现在的项目仍然有问题。边界划分不是一劳永逸的,语言在变,业务在变,团队在换人。每隔几个月,就会有新的词语冒出来,或者说旧词语被赋予了新的意思。
但我不再那么焦虑了。因为我慢慢意识到,领域驱动设计与其说是一种技术方案,不如说是一种承认。承认人的认知是局部的,承认同一件事在不同人眼里本来就是不同的事,承认试图用一套模型解释一切,是一种傲慢。
我妈的铁皮盒子至今没有数字化。我给她买过一个扫描仪,她用了几次就搁下了。“有些东西还是得用手翻。”她说。
我不知道该怎么反驳她。因为在软件系统里,我做的事情——划分边界、定义语言、维护模型——最终不也是为了让某些东西可以被理解、可以被找到、可以在需要的时候被重新读一遍吗?
那张住院费收据现在还在盒子最底下。我偶尔会拿出来看一眼。十四块六毛。七斤二两。哭声很大。这些数字和描述,不在任何流程里,不被任何系统引用,但它是整个盒子里最沉的一张纸。
也许好的系统设计也是这样。不是把所有东西都管起来,而是知道哪些东西应该被放在最底下,让它安安静静地待着,不被任何上下文打扰。
作者|fawen
排版|fawen
审核|满满
大讲堂