可视化与领域驱动设计

简介: 从DDD的角度,领域逻辑的分析可以运用战略方法Bounded Context。问题是:如何获得Bounded Context ?
序言:  张逸者,70年代生人。软件开发生涯经历了从程序员、项目经理、测试经理、开发部长、技术总监到架构师的一个循环,而后程序员,现在是创业公司CTO。三大爱好是开发、写作与阅读。著译作包括《软件设计精要与模式》、《WCF服务编程》、《Java设计模式》与《恰如其分的软件架构》。张逸现居于锦官城,与中生代诸君多有往来。张君于设计模式、软件架构、DDD以及clean code等方面研究尤深。开公众号而不刷粉,则多数文字并不显于街市,中生代编辑之以飨读者,使佳作流传,善莫大焉!

张逸-BigEye联合创始人与架构师

从DDD的角度,领域逻辑的分析可以运用战略方法Bounded Context。可是,一个问题是:如何获得Bounded Context ?


我查看了许多关于Bounded Context的书籍与文章,虽然都着重强调了它的重要性,也给出了一些实例,却对如何从需求——>Boundex Context这一点上语焉不详。

一个初步设想

我的初步设想是通过绘制场景图(但并不成熟)。我认为有三种绘制场景图的方式:商业画布,体验地图和流程图。我认为,商业画布可以作为需求分析(尤其针对初创产品)的起点。商业画布如下图所示:

采用这种规范化的方式来推导商业模型,可以激发我们的灵感,理清我们的思路,以便我们思考为何要做这个产品,产品应该具备哪些功能。结合优点和缺点、成本等因素,我们可以藉此判断和决策功能的优先级,从而得到MVP。这个过程需要大量运用即时贴,让整个商业模型呈现。经过取舍后,就可以针对产品绘制场景图。此时,场景图可以采用Experience Map或流程图来体现。Experience Map的例子如下图所示:


由于商业画布本身提供了“客户”项,我们应该创建Persona,找准人物角色的特征来“搜寻”需求。绘制了场景图后,就能够确定用例了,此时,可辅以ATDD帮助确定Story。在确定了用例后,可以识别Bounded Context,并通过Context Map确定上下文之间的关系。

就我个人感觉,体验地图还是从Persona的角度设想系统如何使用,考虑它的用户体验。它其实符合“场景”的概念。这里可能还是要考虑:在一个完整的场景中,需要哪些参与者?但是,即使从粗粒度的角度出发,场景都可能存在多个,可能需要绘制多个场景图来逐步提炼Bounded Context。

关于如何运用Persona,熊子川在他的博客《XD关键字5:Persona》中已有详细介绍,同样在他的博客《Agile UX内容策略工作坊》中提出的“消费者建模”实践,指出:

为了更好的理解我们选择的目标消费者,我们需要对消费者进行完整的建模,即Persona。越接近于真实的Persona帮助我们更好的理解其用户目标……Persona的重要产出物是一系列用户目标,对于同一个Persona,用户目标可能有不同,有些目标是基础核心目标,有些则是衍生性的,例如一个访问网站潜在投资者的核心目标可能是了解成为投资者的过程,而衍生性目标可能是获得一些关于公司历史信息增加信任度。



说明:本图摘自熊子川博客

获得Context并划分领域


假设我们要开发一个电子商务网站,我们就可以通过商业画布来驱动出这个产品应该具有哪些功能,它的客户有哪些等,在绘制了场景图后,可以初步得到这样的Bounded Context:


然后,我利用Context Map得到了各个上下文之间的关系:


这样,一个包图的获得就水到渠成了:


六边形架构

在识别了Bounded Context以及Context之间的关系后,我们可以运用Hexagon架构(Cockburn提出的六边形架构)来展现系统的整体架构。Hexagon架构并不深入关注内部边界中领域部分,仅仅是简单的划分为Application与Domain两层。但它有助于我们获得基础设施层以及相关集成点的包结构。我们要合理地运用六边形架构。它更贴近应用逻辑架构,并可以驱动我们去发现诸多集成点,寻找集成模式。内外边界的分离也有助于我们将业务逻辑与应用逻辑分离开。这实际上符合“关注点分离”的架构原则。下图展现了六边形架构中常见的Port与Adapter:


所谓“可视化架构”,是一种利用多种交流方式实现架构知识共享的方法,因而需要团队成员均参与进来,并以Workshop的形式,更多通过即时贴、白板等工具实现可视化,而非通过绘图。至少,绘图不应该成为主要的驱动力,否则,开发人员很难接受。例如,下图就是我运用Hexagon架构,并结合可视化手段分析该电子商务系统得到的应用逻辑架构,它很好地一个展现了Hexagon架构的可视化手法。


在这个图中,直观地展现了如何与外部的支付系统以及物流系统的集成。例如,图中展现的Port实际上为防腐层(ACL)。为何要建立这样的一个防腐层呢,原因在于:支付与物流常常存在多个供应商,因而需要解除对供应商的绑定,并避免供应商系统的变化造成对电子商务系统的腐蚀。这是切合实际的决策。


仓库管理流程控制系统

这个电子商务系统需要与仓库管理系统集成。恰好在《面向模式的软件架构》卷四的第35页,给出了一个仓库管理流程控制系统的案例。书中描述的非功能性需求,即所谓质量属性包括:

  • 分布性。仓库管理流程控制系统天生就是分布式的。

  • 性能。仓库管理流程控制系统不是一个“绝对的”实时系统,但性能仍与业务息息相关。对系统有整体的吞吐量要求,因此系统必须确保所有的运输指令能够被及时而有效地运行。

  • 可伸缩性。不同仓库其大小可能会有很大的不同,因此仓库管理流程控制系统必须能既支持只有几千个箱子的小仓库,又要支持超过一百万个箱子的大仓库。

  • 可用性。许多仓库操作采用三班倒的24/7模式工作,因此可用性是仓库管理流程控制系统对业务案例支持的关键因素。

假设要设计这样的系统以支持这些质量属性。对于分布式而言,书中提出的解决方案是传统的分布式系统解决方案,即引入Broker模式,在本地建立对远程对象的代理。而对于支持并发的领域对象访问而言,则采用了Active Object模式,并引入Leader/Followers并发模型来获得可扩展。


我没有打算引入这么复杂的模式,而仅仅是通过引入消息队列,并为消息队列引入路由的方式,来实现系统的分布式。这其中当然会用到经典的Publisher-Subscriber模式。我对领域逻辑进行了识别,将整个仓库管理流程控制系统的领域逻辑分为三个Bounded Context:

  • 库存管理

  • 物流控制

  • 拓扑管理

整个架构如下图所示:


对于库存管理而言,我认为它主要支持商品存放信息的数据管理,即获得商品数量、存放位置以及更新这些信息。对于该上下文而言,操作本身比较简单,且耗时较短。若出现大规模并发,其瓶颈也不在于获取或更新仓库信息(当然需要通过测试数据验证),而在于客户下订单后向仓库管理流程控制系统发起的发货请求。

我将发货请求放到了物流控制上下文中,除此之外,它还包括收货以及订单管理等。同时,对于物流控制与拓扑管理功能,基本上与具体的仓库形成了一一对应关系。此外,对于发货请求(或收货请求),并不要求很强的实时性,这使得对这些请求的异步处理成为可能。

物流控制由于牵涉到收货和运货,需要控制仓库的相关设备,并按照仓库的拓扑结构设定设备的路由。这说明物流控制与拓扑控制存在上下游关系,拓扑控制是上游。这两个上下文可以是Customer-Provider的关系。但它们之间不应该存在物理边界。因此,我将这两个上下文放到了同一个六边形中,而将库存管理放到了另一个单独的六边形中,以便于它们各自独立的可伸缩。

在库存管理与物流控制六边形之间,我引入消息队列来应对从库存管理子系统中转发而来的发货请求(发货请求实则又来自于E-Commerce的订单请求)。原则上,我针对一个物理的仓库建立一个单独的消息队列,因此库存管理在发送发货请求时,会根据商品的存放位置以及用户请求的IP地址,获得最优的仓库信息,然后通过Router将消息转发到正确的消息队列中。

一旦收到消息,物流控制系统作为消息队列的订阅者(或侦听器)就可以即使处理信息,进行后续的处理。

针对库存管理而言,我认为它是一个独立的物理边界,因此在可视化手段中,我展现为一个单独的库存管理六边形,如下图所示:


库存管理建立了如下端口:

  • 建立了针对REST服务的端口,对应的适配器为Controller,其目的是支持E-Commerce系统。事实上,我们对E-Commerce系统进行过分析,获得的六边形架构正好与此对接。

  • 建立了针对DB的端口,对应的适配器为DB Gateway,它负责访问库存管理自身的数据库。数据库持久化的消息包括商品的基本信息如SKU、商品名、数量等,以及商品存放的仓库名。

  • 建立了针对Queue的端口,对应的适配器为Message Router,负责将发货请求消息路由到正确的消息队列。


物流控制与拓扑管理放在同一个边界中,它是高度可伸缩的独立系统,为展现它的可伸缩性以及它与库存管理之间的集成,我在可视化手段中,展现出两个独立的六边形,如下图所示:


物流控制与拓扑管理建立了如下端口:

  • 针对Queue的侦听器端口,对应的适配器为Message Handler。若有必要,如为了更好的支持并发,也可以在此引入Active Object甚至Leader/Followers。

  • 针对REST的端口,对应适配器为Controller。它主要是为了支持移动终端设备、Web应用,以便于相关人员直接发出发货或收货请求。

  • 提供了DB的端口。这个数据库是对应仓库的专有数据库,与库存管理数据库无关。

  • 提供了针对设备(指仓库的设备,如叉车,箱子,运输车等)的端口,对应适配器为South Gateway。

  • 提供了针对配置文件的端口,对应适配器为Configurer。此功能是为了支持拓扑信息的动态配置。

  • 提供了针对外部物流系统的端口,这里为其建立了Shipping的防腐层,使其能够更好地支持各个不同的物流供应商。

目前,我针对可视化架构与设计的手段仍在完善之中,并已经尝试在真实项目中实践以进行验证,并希望能够找到足够简单的方法,为架构师与开发者提供直观而又具有体验价值的沟通方式,并能形成行之有效的设计手段。

相关文章阅读:初窥Bounded Context


目录
相关文章
|
监控 供应链 物联网
ERP系统中的在制品管理与工艺路线规划
ERP系统中的在制品管理与工艺路线规划
487 2
|
JSON JavaScript 前端开发
Go语言中json序列化的一个小坑,建议多留意一下
在Go语言开发中,JSON因其简洁和广泛的兼容性而常用于数据交换,但其在处理数字类型时存在精度问题。本文探讨了JSON序列化的一些局限性,并介绍了两种替代方案:Go特有的gob二进制协议,以及msgpack,两者都能有效解决类型保持和性能优化的问题。
479 7
|
弹性计算 Ubuntu Linux
Ubuntu操作系统配置阿里云镜像方法一
Ubuntu操作系统配置阿里云镜像方法一
Ubuntu操作系统配置阿里云镜像方法一
|
JSON JavaScript 前端开发
Golang深入浅出之-Go语言JSON处理:编码与解码实战
【4月更文挑战第26天】本文探讨了Go语言中处理JSON的常见问题及解决策略。通过`json.Marshal`和`json.Unmarshal`进行编码和解码,同时指出结构体标签、时间处理、omitempty使用及数组/切片区别等易错点。建议正确使用结构体标签,自定义处理`time.Time`,明智选择omitempty,并理解数组与切片差异。文中提供基础示例及时间类型处理的实战代码,帮助读者掌握JSON操作。
453 1
Golang深入浅出之-Go语言JSON处理:编码与解码实战
|
存储 数据可视化 数据挖掘
想提升电商业务效率?这 6 款团队协作软件千万别错过!
在电商旺季,订单量激增,团队需高效协调运营、营销、客服、物流等环节。可视化协作办公软件成为必备工具,提升业务效率与客户满意度。本文推荐6款优秀软件:板栗看板(国产)、Trello、Asana、Wrike、Monday.com和Basecamp。这些软件具备简洁易用的操作界面、强大的可视化功能、定制化任务管理及便捷的跨团队协作,帮助电商团队应对商品上架、促销推广、订单处理等挑战。J人主导的电商公司可根据自身需求选择最适配的工具,实现高效运营与业务增长。
419 16
|
存储 前端开发 Java
Java后端如何进行文件上传和下载 —— 本地版(文末配绝对能用的源码,超详细,超好用,一看就懂,博主在线解答) 文件如何预览和下载?(超简单教程)
本文详细介绍了在Java后端进行文件上传和下载的实现方法,包括文件上传保存到本地的完整流程、文件下载的代码实现,以及如何处理文件预览、下载大小限制和运行失败的问题,并提供了完整的代码示例。
5737 2
|
Unix Linux Shell
groups 命令
`groups` 命令在类 Unix 系统(如 Linux 或 macOS)中非常有用,它用于显示用户所属的所有组。每个用户都可以是零个或多个用户组的成员,这些组用于管理对系统资源和文件的访问权限。 ### 基本用法 ```bash groups [用户名] ``` - 如果不指定用户名,`groups` 命令将显示当前用户所属的所有组。 - 如果指定了用户名,它会显示该用户名所属的所有组。 ### 示例 1. **显示当前用户所属的所有组** ```bash groups ``` 假设输出是 `user1 adm dialout cdrom su
709 14
|
存储 编译器 C语言
C语言程序设计——字符输入函数getchar()
C语言程序设计——字符输入函数getchar()
|
程序员
带你读《电路基础》之一:基本概念
本书内容采用简明易懂的风格,介绍了六步解决问题的方法,并在实践中的问题和实践问题,结合了超过468个新的或改变家庭作业问题。涵盖了全面的线性电路分析的方法,并保留了“设计一个问题”的功能,这有助于学生发展他们的设计技能,有学生发展的问题,以及解决方案。
|
弹性计算 Java 芯片
技术分享 | 软件跨架构迁移(X86->ARM)的原理及实践
针对阿里云倚天实例的软件迁移,阿里云为开发者提供了迁移工具EasyYitian和性能调优工具KeenTune,能够帮助用户解决软件迁移评估分析过程中人工分析投入大、准确率低、代码兼容性人工排查困难、迁移经验欠缺、反复依赖编译调错定位等痛点,实现业务在ARM ECS的快速适配。EasyYitian支持主流开发语言,通过系统自动化扫描可以一键生成分析报告。KeenTune通过AI算法与专家知识库的有效结合,为软件应用提供动态和静态协同调优的能力。
技术分享 | 软件跨架构迁移(X86->ARM)的原理及实践