从这到那,又回来了。——Bilbo Baggins
1. 介绍
本文解释什么是动态领域建模(dynamic domain modelling),为何需要它,以及使其成为领域驱动设计一等公民的价值。首先,我要感谢 Eric 对软件社区的开创性贡献,还要感谢他和我在下面两项工作中所进行的精彩讨论——参与挪威跨国能源公司 Equinor(前身是 Statoil 公司)的石油贸易投资组合项目,并为 OOPSLA 技术大会撰写论文。与 Eric 讨论是一段很棒的经历。
《领域驱动设计》这本书出版已经有 15 年了。那时候,没有 iPhone,没有 Facebook,没有 Netflix,亚马逊刚刚盈利两年。Windows 2000 还是微软的旗舰操作系统,Sun Microsystems 还是一家领先的科技公司,Java 已有 9 年历史,而关系数据库统治着企业的数据中心。
从那时起,云计算、大数据、移动应用、物联网、边缘计算、机器学习和人工智开始成为我们专业词汇的一部分。诸如 Swift、Scala 和 Go 之类的新编程语言开始登上舞台,而 Python 之类的旧语言开始复活,并在数据科学中占主导地位。
显而易见,我们的行业经历了深刻的变化。这些变化,使得领域驱动设计变得更加重要,同时也要求领域驱动设计本身进行改变,以适应软件定义世界的需求。
2. 领域复杂性
英国系统思想家 Derek Hitchins 认为,复杂性是多变性、连接性和无序性的函数。如果组件之间的差异越大,组件之间的连接更多,并且连接相互纠缠(而非有序),那么我们认为事物会越复杂。
复杂性的挑战,在于其中涉及了两种类型的连接,即能导致结构复杂性的稳定连接,和能导致动态复杂性的任意连接。
领域的结构复杂性常见于嵌套结构,例如下面事物中的组件层次架构——产品(飞机,船舶)、零售分类或项目计划。让这些对象变得复杂的因素,包括其内部状态模型、规则及对象之间的连通性和可变性的深度。
领域的动态复杂性,源于自治组件或自治对象之间的交互。这就是在动态系统中所见到的复杂性。对象内部可能具有高度的复杂性,而对象之间不断变化的交互作用和任意连接性,造就了动态复杂性。
来去匆匆的对象们,可能因对方行为或沟通的缺失而迷路,可能会相互协作、竞争、组建团队。一个对象所采取的行为,会直接影响其他对象的可选项。
领域驱动设计解决了结构复杂性的问题。其中的实体、值对象、聚合、存储库和服务等概念,是结构性构建块,有助于创建有序性,减少耦合性,从而简化限界上下文内部及之间可变性的管理。
而动态复杂性的问题则完全没有解决。Vernon 在他的书中引入了领域事件的概念,这是一个良好的开端。但我们需要的不仅仅是事件,还需要在企业消息软件上下文中管理事件的方式。
3. 代理
现实世界由动态系统所组成。领域的动态复杂性源于异步、并发、竞争和协作的过程。
创建面向对象编程的目的,原本是为了通过仿真技术研究和分析一个系统中各个过程,当时的仿真编程语言 Simula 提供了必要的支持。但不知为何,面向对象的软件社区失去了对动态系统的兴趣,却将关注点转向了编程语言。而动态系统的研究,则留给了控制论(cybernetics)和人工智能(AI)社区。
值得一提的是,控制论和人工智两者之间亲如手足。它们都是在 1940 年代后期的研讨会上构思出来的。两者的差异点在于,控制论学者倾向于使用微积分和矩阵代数作为工具,来解决适合这些工具的问题,比如由固定的连续变量集所描述的系统。而 AI 社群则没有这些限制,而是选择了逻辑推断与计算工具。这使他们能够处理语言、视觉和规划等问题。
在 AI 社区中,理性行动的思想催生了理性代理(agent,源自拉丁语 agere,意为做事),也称智能代理,例如可工作的计算机程序。当然,所有程序都能工作,但代理应该做得更多——包括能自主运行、持续运行、适应环境、做出改变、创造和追求目标。理性代理是能为实现最佳结果而采取行动的代理(此定义出自 Russel 和 Norvig)。
智能代理是一种能通过观察环境解决问题,并针对该环境执行操作的程序。在这期间,代理可以扮演角色,与其他代理(包括人类)协作并互动。
智能软件(Software wise)是智能代理的对象,能控制自身的执行线程,自主运作,并能做有趣的事情。但问题是,几乎无人将代理视为领域对象,我认为这必须改变。
为了说明代理也是领域对象,下面提供一段能概括代理的结构 Java 代码:
智能代理是人工智能的基石。其种类繁多,小到机器人,大到宇宙飞船。智能代理是理解其自主性(与代理的学习能力密切相关)的关键。
智能代理的难题,是代理推理方法(或称作代理功能)的实现,即将给定的感知或目标与可能的最佳动作进行映射。代理功能可以非常简单,也可能非常复杂,我尝试使用能力栈来说明这些功能:
使用诸如智能代理软件模型BDI和认知架构Soar之类的工具,实现基于推理的行为
使用数字滤波器、数学控制系统,模糊逻辑和神经网络,用以发现连续的行为
使用有限状态机,构建状态驱动的行为
使用无记忆能力的函数(例如读取测量值),表示的简单行为
要实现最复杂的代理功能,意味着要将诸如 Soar 和 BDI 这样的认知架构,与工具箱中的所有其他的可用工具结合起来使用。Soar 由 John Laired 和 Allen Newell 于 1983 年创建,现由密歇根大学 Laird 的研究小组所维护。BDI(全称 Beliefs-Desires-Intentions 信念-期望-意图)是由 Michael Bratman 于 1991 年在他的人类实用推理理论中创建。目前正在进行的有关 Soar 和 BDI 的研究,都受到美国国防部门对智能代理自主能力的需求的推动。
Soar 和 BDI 这两种领域模型,都是对人类大脑如何推理并将感知转化为行动所进行的建模。这两种架构都获得开源和商业实现的支持,例如 JACK、BDI4Jade、Gorite 和 SOAR。
4. 动态领域建模
在教授面向对象编程时,已故教授 Kristen Nygaard 使用了咖啡馆里的人物作为他的系统隐喻。在观察咖啡馆里的人物时,我们会发现做有趣事情的对象,如服务员、顾客、门卫和收银员,以及定义和描述事物的对象,如菜单、食物、账单和餐桌等。
行为建模面向那些做有趣事情的对象。这种建模从任务环境开始,并引出更详细的事件和任务模型。任务环境定义了上下文,并且定义了有哪些代理(如服务员),代理的绩效指标(如良好的用餐体验),代理的操作环境(如餐厅)以及代理的执行器(如言语、手和脚)和传感器(如眼睛和耳朵)。
事件和任务模型会使用一系列问题,以此将高层次的目标分解为更详细的任务:要执行的任务是什么?什么事件触发了某项任务?任务的预期成效是什么?发送了什么消息?以及谁是接收者?谁执行了这项任务?在执行任务时会创建哪些事件?
在开发事件和任务模型时,需要考虑两个重要因素:首先,哪些任务将同时执行?并且它们是否会争用相同的资源?如果是,那么就会面临竞态条件和可能的死锁,所以需要并发编程技能。其次,如果任务的执行时间有限,即必须在给定的时间范围内完成任务,那么我们需要有实时系统的技能。
为了支持动态系统的建模,我们需要在领域驱动设计工具箱中添加 4 个概念:
任务,即代理要执行的工作。
代理,即感知其环境并执行任务的对象。
代理功能,即代理如何将其感知映射到其所要执行的任务的能力。
事件,即发生了的事情,且因此触发了要执行的任务。
只要把这 4 个概念打造为领域驱动设计的一等公民,那么我们就有把握去构建更丰富和更强大的领域模型,从而构建物联网、工业 4.0、人工智能和一个可用软件无处不在的世界。
对于那些想要更进一步钻研的人来说,Douglas、Russel 和 Norvig 以及 Jarvis 等人的著作都是很好的读物(详见下文)。Hitchins 的书则是为特别感兴趣的人而准备的。我期望所有人都已经读了 Eric 或 Vernon 的书。
5. 感言
有人可能会问,代理和微服务有什么不同?我的答案是粒度。代理是对象,它们最终由所选语言中的构造函数来定义。
如果领域问题最需要使用认知架构来解决,那么建议去找一个成熟的框架,而不用自己构建。
为什么现在要提“领域对象的极致就是代理”?
物联网以及软件定义世界的趋势,改变了商业软件的规则。后台进程必须能够响应边缘事件,还必须能够向边缘进程和设备实时发送新指令。自动化工作始于捕获任务。
希望你现在可以理解这一点——代理是许多领域的一等公民,是领域对象发展的极致。
参考文献
Douglas, Doing hard time, Developing real-time systems with UML, Objects, Frameworks and Patterns.
Evans, Domain-Driven Design, Tackling the complexity at the heart of software.
Hitchins, Advanced systems, thinking, engineering and management.
Jarvis et al, Multiagent Systems and Applications: Volume 2: Development Using the GORITE
BDI Framework.
Russel, Norvig, Artificial intelligence, A modern approach, third edition.
Vernon, Implementing Domain Driven Design.
本文转载自公众号 ThoughtWorks 洞见(ID:TW-Insights)。
原文链接:
评论