利用事件风暴发现限界上下文
(注:该文计划发布到DDD China知乎,看看译文哪里不太通顺?该书电子版可以在这里免费购买,只要把价格滚轮拨到0即可。)
作者:Alberto Brandolini 译者:覃宇、黄雨青、徐培、伍斌
在全景事件风暴(Big Picture EventStorming)工作坊所带来的众多成效中,虽然集体学习看起来是其中最显而易见的,但其他成效也毫不逊色!
本文将展示,如何利用事件风暴在复杂域中,发现备选的限界上下文。
1. 为何限界上下文如此重要
在领域驱动设计的众多概念中,限界上下文很难快速掌握,至少对我而言就是如此。我着实花了一段时间,才意识到这个概念有多么的强大和关键。
2004年,那时的我太擅长于构建单块应用了,或许是由于还没见识过其他风格的架构。几年后,在看到主流架构实践的一些成果之后,我彻底改变了主意。
现在我认为,划定正确的边界,是唯一能对软件项目的整个生命周期产生重大影响的设计决策。如果在不同域之间,共享了不应共享的概念,或重叠出现了不必要的概念,那么就会引发贯穿整个社会技术栈的严重后果。
导致这种后果的过程会是这样的:
- 一个常见的概念(如网上商店的订单),对于几个业务功能都变得至关重要,使得其对可靠性和可用性的需求,突破了CAP定理所规定的极限。此时购买更昂贵的硬件也无济于事。
- 安全性和访问控制变得更加复杂——不同的角色虽然能访问相同的信息,但所看的内容又不完全一致,因此需要复杂的过滤机制。
- 当改变共享资源时,需要进行更多的协调工作——“我们必须要确保”不会破坏其他人的软件和计划。其结果通常是要开更多的会议,要做更多的权衡,要等更久才完工,同时进行适当的软件开发的时间却要更短。
- 这个所有系统都在依赖的“订单”,如果成为一张数据库表,一个微服务,甚至是一个订单管理系统,那么对其进行变更,会变得更具风险。这时,规避风险的意识开始慢慢污染团队文化,一些必要的变更总是会被推迟。
- 曾经的关键软件,开始被带着一肚子怨气的开发人员称作“遗留系统”。它已不再是他们的宝宝了,而成为半夜里唤醒他们进行修复的破烂儿。
- 在待办项列表中添加重构故事,已经变成一场闹剧——由于没有可以交付的商业价值,因此重构总会被推迟或中断。而向非技术同事解释“技术债务”的尝试,总会令人碰一鼻子灰。
- 对系统核心实施变更所需的时间,漫长得令人无法忍受。业务部门不再要求对这些领域进行变更,而是自行去找其他的变通方法。
- 工作已不再有趣,那些昔日并肩战斗的优秀开发人员,现在开始考虑跳槽了。
- 生意也在走下坡路——软件因新功能推迟发布,而丧失了一系列商业机会;而新的竞争对手,正在以难以致信的速度,占领市场。
于是,你仰天长叹:“我们究竟做错了什么?!”
一个可能的答案是:“我们没有划分正确的限界上下文。”
2. 找到限界上下文
理想情况下,限界上下文应包含针对特定目的而定制的模型——该模型应该为这个特定目的量身定制,不会为了其他目的而做出权衡。
而随着时间的推移,当另一个目的又浮现出来时,应该为此设计另一个新模型,来适应这个新目的,并找到新旧两个模型之间最佳的联系方法。
// 插入p38页图“两个不同的目的,应该分别映射到分处两个不同限界上下文中的两个不同的模型”
不幸的是,仅有单个特定目的,还不足以在模型中发现边界。“目的”的概念太过模糊,以至于无法划分可行的边界:开发人员可能正在寻找已经定义好的明确的目的,而相比之下业务干系人的目的会更加粗略,例如“我需要一个员工管理解决方案”[1]“。
一般而言,我们不能假设业务人员会了解限界上下文。限界上下文主要是软件开发要考虑的问题,其学习过程会首先在软件开发中形成闭环。
换句话说,业务干系人并不是了解限界上下文一手信息的可靠来源。要想通过询问业务人员来了解限界上下文,当然会得到一些信息,但不能盲目将这些信息当作标准答案。
作为软件架构师,我们的工作是发现领域的边界。这更像是对犯罪现场的调查,而不是一个问卷调查的谈话。
// 插入p39页图“组织中的知识分布:知识和无知的奇怪组合”
没人知道全部真相,所以不要假装如此。
3. 进入事件风暴
事件风暴是一种灵活的工作坊,可以用协作的方式对复杂域进行大规模的探索。虽然有几种不同的方法[2],但更适合发现上下文边界的方法,是全景事件风暴。这是一种大型工作坊(通常会有15至20人参与,有时会更多),其间软件开发和业务人员,会构建整个业务线的行为模型。
从根本上讲,这种方法非常简单:
- 确保所有关键人物(业务和技术干系人)都在同一个房间
- 为他们提供无限制的建模空间(通常是把一个长纸卷贴在一面很长的墙壁上,外加数百张彩色便利贴)
- 让他们以时间先后顺序,使用领域事件来再现整个业务流程
在事件风暴工作坊中,领域事件(或者简称为事件),并不是一个软件的概念。而是使用过去时态的动词,写在便利贴上的一个短句。
// 插入p40页图“领域事件的极简解释”
通过一些引导方法,在几个小时内,我们最终就能得到整个组织的一个很大的行为模型,如下图所示。
// 插入p41页图“一个有关大会组织业务场景的全景事件风暴的输出”
很明显,彩色便利贴泛滥成灾了。但是,正如在一首歌中所唱到的:“相比目的地,旅程更重要。”在所有关键干系人的积极参与下,可视化整个业务流程的过程,就能收获关键的洞见和发现。
4. 全景工作坊的组成
为了能更清楚的了解工作坊,我们来看看其主要步骤。我们主要关注的这些步骤,能帮我们在发现限界上下文的过程中,找到关键的洞见。
4.1. 第1步 混沌探索
参与工作坊的人们在这一步中探索领域,他们把过去时态的动词[3]写在便利贴上(通常用橙色),然后按照理想的时间线将它们按顺序贴在墙上。
这里有一点很关键:没有人知道完整的故事。假设,我们正在探索的是会议组织领域[4]:大家各司其职,有的了解战略和市场定位,有的专门对接“巨星”演讲者,还有各种专家或合作伙伴负责视频报道、餐饮、宣传材料等等。
如果对一种工作了解得十分深入,恐怕就没有时间做到对其它工作有着同样深度的理解。我们期望在每个业务中所发现的,正是这种因专业产生的分歧:局部专家、垂直领域的大师,以及他们对业务的其它部分不同程度的理解。
参与工作坊的人越多,就越难遵循时间轴:对于业务真正在做什么,发散的视角和专业的观点常常会导致有序的事件在局部聚集,却不会形成全局一致的整体。
虽然远非完美,但这是一个不错的开始。现在我们可以看到一些蛛丝马迹了。
// 插入p42图“全景事件风暴的混沌探索的产出”
还有,这一步通常是静悄悄的:只要有足够的空间,人们就会静静地把能想到的一股脑儿全都贴到墙上。他们大多数都会保持沉默。接下来的步骤会嘈杂一些。
4.1.1. 把发散当做线索
事实上,我们也希望这个阶段是安静的:人们现在还不应该就便利贴上写的内容达成一致。引导者应该准备足够多的马克笔和便利贴,让每个人都可以独立不受干扰地写下自己对流程的解读。
最后,我们会看到许多重复的便利贴,或貌似重复的便利贴。
// 插入p43图“不同的措辞往往就是区别深层含义的线索”
克制将措辞达成一致来消除重复的诱惑,这通常是正确的思路。不同的措辞可能体现着对同一事件的不同看法,暗示这可能与多个限界上下文相关,或者暗示两个或多个事件实际上是不同的。
回到会议场景,我们可能希望看到一些领域事件或多或少地提及同样的一件事情,但它们使用的是不同的措辞。例如:“Schedule Ready”、“Schedule Completed”、“Schedule Published”,等等。
这种不一致已经给了我们一些线索:这个事件(假设或假装这里只有一个事件)可能和参与业务流程的不同角色相关。
这样很好!这可能是多个互相重叠的上下文的提示。
// 插入p44图“不同的含义可能指向不同的模型,进而指向不同的上下文”
我没有在“上下文”前加上定语“限界”,因为现在边界并不清晰。
4.2. 第2步 强制按时间排序
在这一步,我们要求参与者确保事件都按从先到后的时间顺序来描述业务流程。
要做到这一点并不容易,因为会存在平行和替代的路径。即使像会议组织这样很久才搞一次的业务,也倾向于周而复始,通常每年要重复一次。
人们需要就整个业务视图达成一致,这会触发一系列对话,来讨论视图中不一致的地方。人们开始询问在一些不被人知的地方会发生什么事情,并能得到答案,因为我们确保专家在场!
与此同时,针对如何执行特定步骤会有不同的看法。其中一些分歧会被消除,毕竟这些分歧通过交谈就能解决。其他暂时无法消除的分歧,可以用热点便利贴(通常是红色的便利贴,如洋红色)来突出显示,以便能让探索继续进行。
贴热点便利贴,揭示了探索的基本方法——我们不会在工作坊里解决所有问题,因为没有时间。但是,我们可以尝试将所有内容可视化,包括不清楚、不确定或有争议的内容。
全景事件风暴,会带来我们当前对业务的集体理解水平的快照,其中包括漏洞和差距。
4.3. 浮现出的结构
简单地谈论问题,并把便利贴移来移去,无法有效地在这个阶段获得洞见和发现。此时,参与者经常要寻找更复杂的结构,以将他们刚刚创建的“乱象”进行收敛。
有一些策略可以使浮现的结构展现出来。发现限界上下文最有趣的是关键事件(Pivotal Events)和泳道(Swimlanes)。
4.3.1. 使用关键事件
关键事件是一些特定事件,它们与业务关系密切,且标志着不同业务阶段之间的转换。
在会议组织的业务场景中,就能发现这样一些关键事件。
-
Conference Website Launched
(大会网站已发布)或Conference Announced
(大会召开已宣布):这是大会开始卖票的时候,同时也是不能轻易取消的时候。 -
Conference Schedule Announced
(大会议程已宣布):此时,演讲者正式加入,门票销售应该已经开始。 -
Ticket Sold
(票已售出):一方面,这是一笔相关的商业交易,终于收到钱了。另一方面,我们现在有了顾客和参会者,还必须要进行后续的具体沟通。 -
Conference Started
(大会已开始):此时参会者期望从所购买的门票中获得一些价值。同样,演讲者也希望收获洞见、人脉和赞助商。 -
Conference Ended
(大会已结束):这个事件一目了然——聚会结束,处理善后。
我通常用彩色胶带来标记候选的关键事件,来让它们醒目,并提醒我们这里有各个不同的阶段。
//插入P.46的图1“一条彩色的胶带是我最喜欢的用于标记关键事件的工具”
是否挑出了正确的事件其实并不重要,所以我经常缩短讨论的时间。我会寻找4到5个似乎能起到关键作用的候选事件。而整理其他的内部事件则速度更快。如果以后需要,我们还可以改进模型。
在突出显示了关键事件之后,边界之内的事件排序就能更加迅速,一个更复杂的结构开始浮现出来。
//插入P.46的图2“关键事件是重要的信息来源”
//图字翻译 pivotal events:关键事件 candidate system boundries: 候选系统边界
4.3.2. 使用泳道
即使在最简单直白的业务场景下,流程也不是线性的。其中有分支、循环以及并行发生的事情。有时候,业务流不同部分之间的触点是定义良好的,例如记账事件只在销售事件或取消事件时触发。其他时候,这种关系可能会更复杂:公布一些知名的演讲者可以促进销售,销售可以吸引赞助商,赞助可以让组织者负担得起更多超级明星演讲者的报酬,这可以刺激更多的销售,最终引发会场升级。
水平泳道 是构造整个流程各个部分的常用方法。它通常发生在识别关键事件之后,但这里并没有严格的诀窍:业务所浮现出的形态会呈现更有效的分区策略。
在我们的会议场景中,我们可以识别几个或多或少并行发生的主题: 负责主题选择、征文、邀请、后勤和住宿的演讲者管理流程;负责门票销售、广告、物流和所有欢迎参会者工作的销售管理流程;负责其他收入来源管理的赞助商管理流程;最后是很多重要的领域,包括场地管理、餐饮、人员配备、视频录制等等。
我们的胶带可以方便地为这些并行流程命名。基础结构主干现在可能看起来像这样:
//插入P.47的图“在领域事件流之上,关键事件和泳道让结构浮现出来”
//图字翻译 time:时间
现在,每个人都看到了业务的不同阶段,以及每一步的相关问题。
4.4. 第3步 人与系统
在此阶段,我们开始探索业务环境,明确地寻找 人员: 参与者、用户、用户画像或系统中的特定角色;和 系统:从软件组件和工具到外部组织。都不排除在外。
//插入P.48的图“这是人和系统的非常简单的符号表示。”
//图字翻译 a person:一个人 playing a role in our model: 在我们的模型里扮演了一个角色 A system playing: 一个系统在我们的模型里扮演了一个角色 (no feels involved): 不涉及任何感觉
在我们的系统中可视化不同的参与者——在实践中,我们不称他们为 参与者 而只是 人 ——有助于挖掘不同的视角。我们可能会发现具体的职责和角色,或者是不同的看法:所有演讲者都在提交话题,但如果我们谈论的是一位超级明星嘉宾、受邀做主题演讲的嘉宾、一个专家或新手,那么对待他们的方式可能会有所不同。在这个探索阶段,一个困惑的表情可能最终会打开一个全新的分支,或者使不同的策略变得更加可见和可读。
系统通常触发不同类型的推理。一方面,它们明确了我们的界限。我们不可能在真空中用自包含的软件来交付价值:完成流程需要工具、集成和协作。支付管理系统、票务平台、社交媒体平台、视频流、计费软件,以及任何你想到的东西。
系统也可以是像政府机构这样的外部实体,或者像“天气”这样模糊的东西(如果您在极端地区组织会议,或者如果您幸运地在前一天遇到暴风雪,这可能是一个严重的限制)。
从软件的角度来看,外部系统需要某种集成策略(可能是我们一直最喜欢的:反腐败层),但是从业务的角度来看,外部系统常常施加约束、限制或 借口,如果出现问题,这是一种免费的替罪羊服务。
4.5. 第4步 明确的演练
每一步都会引发一些澄清,并促使编写更多的事件。即使我们添加了一些结构,关键事件和泳道,并在现场进行了一些有趣的对话,整个事情仍然让人感到混乱。可能是因为它仍然很乱。
现在是时候验证我们的发现了,挑选一位 叙述者 从左到右的来讲述整个故事。一开始,连贯的讲故事很难,因为讲述者的思路会被相互冲突的需求撕裂。叙述者会试图用现有的事件来讲述故事,但与此同时,他们会意识到,在之前的评论中看起来 足够好 的东西,对一个在公共 舞台上 讲的故事来说还不够好。
现在我们的模型需要改进,以支持讲故事。更多的事件将出现,其他事件将被移走,路径将被分割等等。
观众不应该被动。与会者们需要经常挑战叙述者和他讲故事的方式,最终提供一些特殊情况和"不那么特殊"的情况的案例。
我们在时间轴上进展得越多,流程就越清晰,而叙述者就像一个磁盘去碎片化的游标[5]一样在推进。
//插入P.49的图“明确的演练是验证我们理解的方法。”
//图字翻译 clean here:澄清这里 still messy: 依旧凌乱 narrator: 叙述者
4.6. 额外步骤
现在可以执行一些额外的步骤,通常取决于上下文,这些步骤可以提供更多的见解。我将简要地描述它们。
- 我们有时会研究业务流中应该被 产生的 或不幸被 销毁的 价值。我们探索不同的通货:钱是最明显的通货,经常发现其他通货更有趣(比如 时间 、名誉 、情感安全 、压力 、幸福 等等)。
- 我们探索现有流程中的 问题 和 机会 ,允许每个人发出在前面的步骤中没有浮现的问题的信号,或者使改进的想法可见。
- 我们可以用 可选流程 挑战现状:一旦对当前情况的理解确定下来,如果我们更改它会发生什么?哪些部分将保持不变,哪些部分将被彻底改变或摒弃?
- 我们可以 投票选出最重要的议题,将集体理解的明确性转化为政治动力,做正确的事情[6]。
所有这些东西,再加上更多,通常都很棒的!但是,如果您仔细观察,就会发现绘制上下文边界所需的大部分信息都是可用的。
从我们的工作坊中获得这些信息现在是我们作为软件架构师的工作。
5. 作业时间
一旦工作坊正式结束,参与者离开了工作室,我们就可以开始讨论软件了,……终于!
我过去常常画上下文图,作为 强迫自己在项目早期提出正确问题 的一种方法 ,然后进行事件风暴工作坊,让利益相关者参与并提供正确的答案,而不需要提相应的问题。
有很多与有界上下文相关的信息是我们讨论的副产品;我们只需要破译线索。所以,这里我们有一些启发式[7]活动,可能会派上用场。
5.1. 启发:观察业务阶段
...比如像侦探一样说:“跟着资金!” 业务通常围绕着一个定义明确的交易而发展,其中一些价值 - 通常是金钱 - 被交换为其他东西。关键事件在这个流程中起着根本作用:如果没有网站,我们将无法在线销售门票,在网站上线之前发生的一切都是库存或花费,我们只有在会议网站已上线
这一事件后才能开始赚钱。
同样,在票已售
事件之后,我们将成为与会者资金的临时所有者,但他们将开始仅在会议已开始
事件中获得一些价值。不过 设计 会议所需的工具和心智模型与 运行 会议所需的并不相同。
// 插入p51图“放大到关键事件”
有趣的是,边界事件往往也是措辞会有冲突的事件,同时也是对限界上下文的感知通常重叠的地方。这里的一个重要建议是,你不必就语言达成一致意见! 通过提出分歧可以发现更多内容。
此外,请记住,当两个模型进行交互时,通常会涉及 三个 模型:两个限界上下文的内部模型和用于在它们之间交换信息的 通信模型 。
// 插入p52图“两个相互作用的模型通常意味着三种语言”
一个简单的例子:我尝试使用英语与读者沟通,但我的母语不是英语。我的心智模型有时也是英语,有时是意大利语。但读者不应该能识别出(我希望)。同时,本文不仅适用于英国和美国人,每个读者都可以用他们的母语翻译成他们的心智模型。
一般来说,不同的阶段通常意味着不同的问题,这通常会导致不同的模型。
关键事件通常是各方之间共享的更为通用的 公共语言 的一部分。
// 插入p52图2“在事件风暴全景图形成后显露的限界上下文”
上面的图片或多或少地显示了,当我思考限界上下文时在这个流程中所看到的内容。
5.2. 启发:观察泳道
泳道通常显示涉及不同模型的不同路径。
不是每个泳道都是限界上下文,有时候它们只是某个地方的 if 语句,但是当为了突出一个独立的过程而出现泳道时,可能是 在不同的时间线上 ,那么你可能想要一个独立的模型。
// 插入p53图“泳道通常是可能存在不同限界上下文的可靠线索”
5.3. 启发:观察纸卷上的人
当处理不同的 角色 时,可能会发生有趣的变化。显然,流程应该是相同的,但事实往往并非如此。
会议组织者或专题出品人可以邀请一些发言者,而其他人则在论文征集中提交他们的话题提案。流可以独立于其上游部分(比如您可能想要跳过针对明星演讲者的繁琐的评审过程)。而下游可能不行(比如在会议日程上,您希望得到相同的数据,无论如何获得)。
// 插入p54图“两个并行流可能需要独立的模型”
有些组织能够很好地考虑 角色:他们会认识到演讲者和主讲人是不同的,但是二者会有一个类似的标记在注册流程中,并且在午饭期间和普通与会者没有不同,那时他们的角色将会是一个简单的mouth to feed
。
5.4. 启发:观察屋子里的人
有一点太显而易见,我都懒得说,那就是:人。人们在探索过程中所处的位置也许就能给出关于不同模型分布的最简单、最有力的线索。
专家们倾向于把大部分时间花在他们更了解的区域,要么解疑答惑,要么纠正他们在纸卷上发现的错误的便利贴[8]。或者,他们围绕着自己关心的区域高谈阔论,这可能是因为当前的实现实在是差强人意。
不同的人有着不同的需求,这意味着不同的模型。
有意思的是,这些信息——人们所在的位置——从来都不会被白纸黑字地记录下来,但往往会残留在奇怪的记忆空间里。
5.5. 启发:观察肢体语言
肢体语言是另一个信息来源:并不是所有的异议都能通过语言表达。表面上看似一个问题,但不同层级的人有着不同的看法,这种情况屡见不鲜。摇头或着翻白眼都是观点冲突尚未解决的线索。
领域驱动设计有一个很好的办法来解决这些冲突:不要说“我们需要一个模型来处理这些事项”,而是说“我们需要一个模型来解决你的问题,我们还需要一个模型来解决老板的问题”,完美的交流方式得靠软件架构师去发掘。
再强调一次:不同的需求意味着不同的模型。
事情还没有结束。下图描绘的是一场典型的对话模式,它就发生在关键事件或边界事件周围。
// 插入p55图“这是一次典型的能力冲突,指定步骤左边的人往往很了解所有它涉及到的技术性细节,而右边的人只关心它的成效”
完成一件事情需要做的一切所涉及到的知识会很复杂,这些知识就像是一张任务清单。而在关键事件的下游,这种复杂性应该消失不见:人们通常不关心做事的方式,只关心事情的结果。
在我们的场景中,情况可能是这样:“我不关心是不是用WordPress来发布网站,我只需要知道 URL 是不是可以访问”或者“我不关心这些定价是如何决定的,我只想知道每一种票的定价是多少”。
5.6. 启发:倾听真实的语言
这可能是最难掌握的技巧,因为语言会欺骗你。几十年来,语言一直在愚弄我们,这也是领域驱动设计存在的原因之一。
如果探讨像“演讲”这样的核心术语,你会发现它们被用在许多不同的地方。
- “演讲”可以在论文征集的过程中提交,被接受或者被拒绝。
- “演讲”可以安排到指定议题的指定时间段之中。
- “演讲”可以分给指定的主持人或工作人员,由他们来介绍演讲人。
- “演讲”可以由与会者评分。
- “演讲”可以被拍摄并记录下来。
- “演讲”可以发布在会议的YouTube频道上。
...你确定我们说的是同一个“演讲”吗?
这里的关键是,愚弄我们的通常是名词。人们总是倾向于观察事物的静态数据结构来对其名称达成一致,比如:“演讲有一个标题”,这是很简单的一句话,非常容易达成一致,但这并不能说明我们实际上谈论的是同一件事。
就上面列表里的“演讲”来说,我们谈论的是不同的模型:选择、日程、人事安排,等等。它的名字只有一个,而且不同的模型也确实需要一些共同的数据...但是模型是不一样的!
观察动词可以围绕一个特定的目的建立健壮得多的一致性。
6. 聚沙成塔
通过事件风暴工作坊获得的信息量明显优于传统的正式的需求收集活动,简直是大规模压倒性的优势。
人们的行为和肢体语言不合适用标准的文档记录下来。然而,这种非正式的信息往往就会辗转指向良好的模型,因为……我们一直待在这些模型周围!我们见过人们围绕着他们的问题和一些蠢事采取行动,仅仅因为名字碰巧一样就混为一谈的事情绝对不会发生!
这用不着太多的纪律或规则。把不该搞混的东西搅和在一起会让人觉得非常愚蠢,因为它们就不会被放在一起。它们在墙上隔着好几米远呢!
希望上面描述的这些启发能帮你勾勒模型,但更重要的是,这是在做正确的事的强烈感召之下,去理解软件的深层目的甚至还有组织的深层目的良机。
整个思路其实很简单,可以用一句话来概括:
合并人员,拆分软件
回顾过去,我耿耿于怀,我们浪费了这么多年的时间南辕北辙,究竟是为什么?
- 管理本身不是目的。这一点体现在很多方面,对于限界上下文尤其如此:规划、设计、跟踪、运行、支持、选择,这些都是更细粒度的目的,通常都需要自己的模型。 ↩
- 探索事件风暴的最佳入口是官方网站:https://www.eventstorming.com/。 ↩
- 英文中使用过去时态的动词描述事件,如:“Schedule Published”。而中文对事件的描述多采用副词“已”来连接名词和动词,如:“日程已发布”——译者注 ↩
- 会议组织这个话题有点混乱,但很有趣。会议主办方的雇员往往身兼数职:这个小团队在长达几个月的时间里,要负责各种活动,其中会议期间和会议前几天的活动最为密集。专业化与尽可能保持团队精简这两点之间的矛盾始终存在。但是,我从未见过一模一样的两个会议,所以本文的内容并不会透露特殊的商业机密。 ↩
- 如果岁数已经足够老,就能知道以前的磁盘碎片整理。;-) ↩
- 这个区域可能就是目前的核心域所呈现的样子。 ↩
- 我在这里使用“启发式”这个词,只是为了让Mathias Verraes高兴。 ↩
- 没人禁得住这种诱惑:“有人犯错了!我必须马上指出来!”事件风暴利用了人类这种本能的行为,将它变成了促进建模活动的推进器。 ↩