第 9 章 复杂系统的设计与优化
欢迎来到第三篇"进阶"。
如果说第二篇"建造"教的是一个 ToB 设计师的核心手艺,让你能把一个常规系统做扎实,那么第三篇要谈的,是把出色设计师和合格设计师区分开来的高阶能力。这一篇有四个主题:复杂系统的设计与优化、数据驱动设计、数据可视化、以及 AI 时代的设计。它们各自都是一片深水区。
这一章先谈复杂系统。第五章我们讲过复杂度治理,那一章的视角主要在"认知"层面,如何让有限的认知驾驭复杂。这一章的视角更偏"系统"层面,当一个系统本身的结构复杂度高到一定程度,设计师该如何从结构上去拆解、组织和优化它。两章是一对:第五章管"用户怎么看不晕",这一章管"系统本身怎么搭得住、长得动"。
一、先看清:复杂度从哪里来
要驾驭复杂,先得理解复杂从哪儿来。诊断不清病灶,就谈不上对症下药。ToB 系统的结构复杂度,主要有四个来源,看清它们,你才知道该从哪里下手。
一是业务规则的复杂。这是最根本的来源。真实业务往往有大量的规则、条件、例外:什么情况走什么流程、什么条件触发什么动作、什么角色有什么权限、什么时候是特例。规则越多、越交织,系统就越复杂。第二章讲的"规则是业务的法律",在这里成了复杂度的主要贡献者。一个保险理赔系统之所以复杂,很大程度上就是因为理赔规则本身千头万绪。
二是状态空间的庞大。ToB 系统里的核心对象往往有很多状态,而多个对象的状态又会组合、相互影响,形成一个巨大的状态空间。一个订单有十种状态,关联的支付、物流、库存各有自己的状态,它们交叉组合出的局面是惊人的,"订单已付款但物流异常且库存不足"这样的组合状态,可能有成百上千种。第四章讲状态机的重要性,在复杂系统里被放大了无数倍。
三是角色的交叉与协作。前面反复讲的多角色,在复杂系统里不仅是"多",还会"交叉":一个人身兼数职、一件事多方协作、信息在角色间反复流转、还有审批链条上的层层关系。角色关系的网络越密,协作的复杂度越高。
四是系统的集成。ToB 系统极少孤立存在,它要和一大堆别的系统对接:上游的、下游的、第三方的。每一个集成点,都引入了新的复杂度:数据格式要对齐、状态要同步、异常要处理、边界要划清。集成复杂度,是 ToB 区别于很多 ToC 产品的一个显著特征,也是最容易被设计师低估的一种。

把这四个来源画成一张图,你就有了诊断复杂度的工具。每当面对一个复杂系统感到无从下手,先做一次"诊断":它的复杂主要来自哪一类?是规则太多(那要在规则的梳理和呈现上下功夫),还是状态太杂(那要在状态机和状态可视化上下功夫),还是角色太交叉(那要在角色视图和协作流转上下功夫),还是集成太多(那要在边界和异步处理上下功夫)?定位了主要来源,优化才有靶子,而不是眉毛胡子一把抓。
二、拆解复杂的根本手段:模块化
驾驭复杂,人类文明最根本的智慧只有一个词:分而治之。落到系统设计上,这个智慧的名字叫模块化。
模块化的思想是:把一个庞大复杂的整体,拆解成若干个相对独立、各自内聚、彼此低耦合的模块。每个模块内部紧密相关、自成一体;模块之间则尽量减少依赖,通过清晰的接口交互。这样,人就可以一次只面对一个模块,而不必同时把整个系统装进脑子里,复杂度被"分"开了,于是变得"可治"。
"高内聚、低耦合"这六个字,是模块化的精髓,也是软件工程几十年沉淀下来的核心原则。它对设计师同样适用。第二章讲的 DDD"限界上下文",第五章讲的"角色化模块",本质上都是模块化思想的体现:沿着业务和角色的自然边界,把大系统切成内聚的小块。可以说,模块化是贯穿这本书的一条暗线。

对设计师而言,模块化体现在产品结构的方方面面:把系统切分成职责清晰的功能模块、把一个复杂页面拆成相对独立的区块、把交互流程拆成可复用的片段。好的模块化拆分,有两个标准。
一是切口要切在业务的自然关节上。沿着业务本来的结构去切,而不是硬切。这样切出来的模块才内聚、才符合用户心智(回到第二章"让设计结构反映业务结构")。怎么判断切口对不对?一个简单的检验:如果两个模块之间需要频繁地来回传递信息、彼此牵扯不清,那很可能是切错了,本该在一起的东西被你硬拆开了。好的切口,切完之后模块之间的"接触面"是小的、清晰的。
二是模块之间的接口要清晰。一个模块的输出如何成为另一个模块的输入,衔接处要定义明确。这样模块才能真正解耦,改一个模块不会牵连一片。
切口切得好不好、接口理得清不清,是检验一个设计师系统功力的试金石。新手往往按"页面"来想问题(这个页面放什么),高手则按"模块"来想问题(系统由哪些内聚的部分组成、它们如何衔接)。
三、应对集成:让系统边界清晰
四大复杂度来源里,"集成"是设计师最容易忽视、却最容易出问题的一个。因为集成发生在系统的边界处,而边界往往是设计的盲区,大家都盯着自己系统内部,没人认真管交界的地方。
ToB 设计师要有"边界意识"。当你的系统要和别的系统打交道时,要在设计上认真回答几个问题。数据从那边过来,可能是什么样子?包括各种异常、缺失、格式不规范的情况,别假设对方传来的数据永远是干净、完整的,它常常不是。那边的系统挂了、慢了、返回了错误,我这边怎么体面地应对?是直接崩给用户看一个技术报错,还是给一个用户能理解的提示和可行的下一步?这直接关系到第一章讲的"可靠性",你内部跑得再好,边界上一漏,用户感受到的就是"这破系统又出问题了"。两个系统的状态如何保持同步,不一致时以谁为准、怎么提示用户?比如你这边显示"已支付",对方支付系统那边还没确认,这种不一致怎么处理?
集成场景里,有一个特别考验设计的点是"异步"。很多跨系统的操作不是即时完成的,你提交了,对方要处理一会儿才有结果(比如发起一笔跨行转账、调用一个第三方的风控审核)。这种异步,要求设计认真处理"中间态":操作发出后、结果回来前,用户看到的是什么?

回到第一章的"确定性"和第六章的反馈:是让他干等(然后焦虑地反复刷新,甚至重复提交),还是明确告诉他"已提交,正在处理,预计 X 分钟,结果会通知你"?异步状态的设计质量,是复杂集成系统体验好坏的分水岭。一个处理得好的异步流程,用户提交后就能安心去做别的事,结果好了系统会找他;一个处理得差的,用户只能盯着转圈的图标,不知道是成了、卡了还是死了。
四、多角色协作:同一对象的不同面孔
复杂系统里,多角色协作的设计是一门大学问。前面讲了很多次"多角色",这里把它在复杂系统中的核心挑战点透。
挑战的本质是:同一个业务对象,要在不同角色面前,呈现出不同的面孔。
我们把那个订单的例子彻底展开。一个"订单",流转在一家公司里。在销售眼里,它是待跟进的业绩,他关心客户是谁、金额多少、有没有按时回款、能不能促成复购。在仓管眼里,它是待出库的任务,他关心要拣哪些货、在哪个库位、什么时候要发、有没有货。在财务眼里,它是一笔应收账款,他关心金额、税、发票、回款状态、对账。在客服眼里,它是一个可能被投诉的对象,他关心当前状态、历史记录、有没有异常、客户之前说过什么。
它们是同一个订单(背后是同一份数据),但每个角色看到的字段、能做的操作、关心的信息,完全不一样。
设计的任务,是为每个角色提供一个"恰到好处"的视图:既给他足够的、与职责相关的信息和操作,又不用无关的东西增加他的负担(这是第五章角色化在数据对象层面的应用)。
难点在于,这些视图背后是同一个对象、同一份数据,你要保证两件看似矛盾的事。一是彼此一致:同一个订单的状态、金额这些核心事实,在所有角色那里都必须对得上,不能销售看到的是"已发货"、客服看到的还是"待发货",那会造成灾难性的混乱和扯皮。二是各有侧重:每个角色看到的是为他裁剪、为他重点突出过的那一面。

一致与差异的这种平衡,是多角色协作设计的核心功夫。做好它的前提,是第四章讲的清晰的信息模型:你得清楚地知道"订单"这个对象有哪些属性、哪些是所有角色共享的事实(必须一致)、哪些是各角色特有的关注点(可以差异化呈现)。
更进一步,协作不只是"各看各的",还有"交接"。一个角色的工作完成后,如何顺畅地流转到下一个角色?信息怎么传递、责任怎么交接、上一环的产出怎么成为下一环的输入?这些"角色之间的接力"设计得顺不顺,直接决定了整个业务流程的效率。一个常见的、价值很高却常被忽略的体验断点,就发生在角色交接处:这一环的人干完了,系统却没有清楚地通知下一环的人,任务就这么悬着,直到有人想起来去问;或者下一环的人接到任务,却看不到上一环的关键信息和上下文,得重新问一遍、查一遍。把交接处设计顺滑,干完自动流转并通知、接手时带齐上下文,是复杂协作系统里投入产出比很高的功课。

五、为演进而设计:复杂系统不是一次建成的
最后,谈一个心态,也是一种能力:为演进而设计。
复杂的 ToB 系统,几乎从来不是一次性建成的。它是长出来的,随着业务发展,不断地增加功能、调整流程、接入新系统。这意味着,你今天做的设计,明天就要面对新的需求;你画的每一个结构,都要考虑它将来如何生长。
为演进而设计,意味着几件事。
一是预留扩展的余地。你的信息架构、你的模块划分,要能容纳未来的增长,而不是把结构定死、一加新东西就崩。一个正面例子:某中台系统,设计之初业务还很简单,但设计师有远见,提前为"组织 / 团队 / 项目"这样的多级结构预留了空间。后来业务从单租户发展到多租户、从单团队到多团队,系统平滑地长上去了,没有伤筋动骨。一个反面例子:另一个系统,设计时图省事,把"公司"这个层级写死成了唯一的顶层,假设"一个部署只服务一家公司"。等业务要做多租户、一套系统服务多家客户时,这个写死的假设成了灾难,几乎要推倒重来。两者的差距,就在设计时有没有"为演进留余地"的远见。
二是保持结构的清晰。一个结构混乱的系统,改一点就牵一发动全身,演进举步维艰;一个模块化良好、边界清晰的系统,才能在演进中保持灵活,加一个新模块,不会把老的搞乱。这又回到了模块化:模块化不仅让当下的复杂可治,更让未来的演进可行。低耦合的系统,才改得动。
三是接受不完美。演进的心态,要求你放下"一次做到完美"的执念,接受系统是逐步迭代、逐步逼近理想的。你要做的,是保证每一步都走在正确的方向上,并为下一步留好接口,而不是试图一口气把所有未来都设计进去(那往往是过度设计,既浪费又僵化)。在"为未来留余地"和"别为想象中的未来过度设计"之间,也有一个需要拿捏的平衡。
为演进而设计是一种成熟的系统观。它把设计师从"交付一个静态方案"的心态,提升到"培育一个生长系统"的心态。拥有这种心态的设计师,做出来的东西经得起时间,这正是序章说的,那个让"好"不再稀缺的方向上,很重要的一环:好的东西,往往是那些能持续生长、而不是很快就显得残破过时的东西。

本章要点清单
- 先诊断复杂度来源:规则 / 状态 / 角色交叉 / 集成。定位主因再对症下药。
- 模块化是根本手段(高内聚低耦合):切口切在业务自然关节(检验:模块间接触面是否小而清晰),接口要明确。
- 集成要有"边界意识":防脏数据、应对对方故障、状态同步;尤其设计好异步的"中间态"(别让用户干等)。
- 多角色协作:同一对象不同面孔,核心事实必须一致、关注点可差异化(靠清晰的信息模型);重点设计角色"交接处"(自动流转 + 带齐上下文)。
- 为演进而设计:预留扩展余地(多级结构别写死)、保持结构清晰(模块化才改得动)、接受不完美但别过度设计。
小结
这一章,我们从"系统"的层面谈了如何驾驭复杂。我们先诊断了复杂度的四个来源:规则、状态、角色、集成;然后用模块化这个根本手段去"分而治之",关键在切口切在业务关节、接口理得清晰;接着重点谈了两个 ToB 复杂系统的难点:集成的边界设计(尤其是异步中间态)和多角色协作(同一对象的不同面孔与角色交接);最后讲了"为演进而设计"这种成熟的系统观。
复杂系统讲的是"结构"。但一个系统做出来之后,怎么知道它好不好、怎么持续优化它?这就要靠数据。下一章,我们谈数据驱动设计,以及 ToB 场景下用数据时那些特有的陷阱。
