2

ZenUML与服务驱动设计

 3 years ago
source link: http://zhangyi.xyz/service-driven-design-and-zenuml/
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.
neoserver,ios ssh client

ZenUML与服务驱动设计

发表于

2021-08-10 分类于 Tool

阅读次数: 12 Valine: 0 本文字数: 2.7k 阅读时长 ≈ 2 分钟

在《解构领域驱动设计》书中的领域建模阶段,我提出了以业务服务为核心进行设计与建模的方法——服务驱动设计。通过该方法可以在静态的领域设计模型基础之上,以业务服务规约为基础,通过分析需求,对业务服务进行任务分解,获得以子任务构成的任务树。这棵树以业务服务为根,组合任务为枝,原子任务为叶,既体现了业务服务的执行过程,又进行了适度的封装,建立了一定的封装层次。

一旦获得了子任务树,即可对树中的每一个子任务进行职责分配,根据其特点分别分配给远程服务、本地服务、领域服务、聚合、端口。它们是构成限界上下文的主要对象角色,我将其称之为“角色构造型”,可以和我提出的菱形对称架构结合:

rsa.jpg

分配的过程可以呈现为序列图,作为动态的领域设计模型,它与静态的领域设计模型共同组成领域设计模型。然则,绘制序列图总是不太方便,于是,我提出了编写序列图脚本的方法。以提交订单业务服务为例,分解获得的子任务树为:

task-tree.jpg

根据职责分配的规则,业务服务分配给远程服务与本地服务,组合任务分配给领域服务,原子任务分配给聚合或端口,就可以编写出如下的序列图脚本:

OrderController.placeOrder(placingOrderRequest) {  // 业务服务对应远程服务
OrderAppService.placeOrder(placingOrderRequest) {//应用服务的方法体现服务价值
OrderService.placeOrder(order) { // 领域服务对应组合任务,避免领域逻辑泄露到应用服务
OrderService.validate(order) { // 领域服务对应组合任务
Order.validate(); // 聚合承担原子任务
InventoryCheckingClient.check(order); // 客户端端口指向库存上下文的边界服务
}
OrderRepository.save(order); // 资源库端口操作订单数据表
ShoppingCartService.removeItems(customerId,cartItems) { // 领域服务对应组合任务
ShoppingCartRepository.cartOf(customerId); // 资源库端口操作购物车数据表
ShoppingCart.removeItems(cartItems); // 聚合承担原子任务
ShoppingCartRepository.save(shoppingCart); // 资源库端口操作购物车数据表
}
}
OrderPlacedPublisher.publish(orderPlacedEvent); // 发布者端口发布订单已提交的应用事件
}
}

序列图脚本的创意并非我的创举,而是ZenUML给予我的启发。我还在ThoughtWorks的时候,我的Sponser肖鹏正在打磨这个工具。我们二人都认同UML的序列图对于领域建模与设计颇有助力。一方面,序列图这一可视化方式可以提供给设计者一些特征,用于甄别设计的坏味道;另一方面,绘制序列图时,是由外向内逐层递进的,可以更好地站在调用者的角度去思考设计,消息的定义也会产生一种驱动力。我在为GitChat编写领域驱动设计课程时,就想到了这一工具,它提供的脚本语法非常接近Java语法,于是,我就采用拿来主义,将其搬到我的文章里,在融入业务服务、菱形对称架构、角色构造型后,组成了服务驱动设计,其完整过程如下图所示:

sdd.jpg

在《解构领域驱动设计》一书即将出版前,我准备修改针对菱形对称架构提供的一个案例,预备在代码库中增加提交订单的序列图脚本。忽然想起之前与肖鹏交流时,他曾提及ZenUML已经为IntelliJ IDEA开发了插件。果然在Settings -> Plugins中找到了ZenUML的插件:

zenuml-plugin.png

它的使用方式非常简单,在安装了该插件后,你可以在代码库的任意位置(建议在项目根目录下定义一个文件夹),新建一个扩展名为.zen的文件,然后在文件内根据语法编写序列图脚本,工具就可以自动生成序列图了:

zenuml-script-sequence.png

在上图右上方的View工具栏上,还可以切换视图类型,从左到右依次为:

  • 仅显示编辑器:此时只会显示时序图脚本
  • 显示编辑器和预览:如上图所示,同时显示时序图脚本和预览的时序图效果
  • 仅显示预览:此时只会显示序列图
  • 在浏览器中打开

如果将ZenUML工具运用到服务驱动设计方法中,即可在领域设计建模阶段尝试通过IDE建模,分析需求后,尝试编写序列图脚本,然后对照生成的序列图对脚本进行调整。调整的成本很低,完全可以随时修订。一旦确定了最终的序列图,即可按照测试驱动开发的流程,优先为聚合承担的原子任务和组合任务编写测试用例,通过测试驱动出该业务服务的实现代码。

ZenUML的功能当然不限于此,在驱动出最终的实现代码后,也可以将真实代码转换为序列图。例如在IntelliJ IDEA中,打开已经实现好的远程服务类OrderController,将光标移到要生成序列图的方法体内,右键弹出快捷菜单,即可看到如下的菜单项:

zenuml-menu.png

选择该菜单项,就会自动生成序列图脚本与对应的序列图,生成的文件为buffer{n}.zen

zenuml-generated.png

如果你不喜欢它默认提供的呈现样式,也可以到Languages & Frameworks中找到ZenUML,添加CSS规则,以改变呈现样式:

zenuml-css.jpg

ZenUml除了Web APP之外,还提供了Confluence插件,以便于我们编写设计文档。不出意料之外,它也为主流的浏览器提供了扩展,例如,在Microsoft Edge浏览器中可找到ZenUML Sequence扩展,安装后,工具栏会出现它的图标,打开,即可输入序列图脚本生成序列图:

zenuml-explorer.png

真的是太方便了!ZenUML简直就是为服务驱动设计量身定做的。至于该怎么实践服务驱动设计,在《解构领域驱动设计》书中你可以找到答案。该如何使用ZenUML?那就太简单了,它的脚本语法基本和Java相同,使用也非常简单,无论是通过浏览器还是IDE,实际去使用一下,很快就能理解它的价值。

如果你的开发流程和开发工具中需要序列图,也可以直接在系统中把ZenUML作为一个前端库进行集成。目前已经有多家企业在自己的构建流程中集成了ZenUML。某国内企业在构建过程中使用ZenUML在文档中嵌入序列图;某国外企业则开发了Python转ZenUML工具,从代码直接生成序列图。ZenUML的文本转序列图的功能以免费的形式发布在npm上面。ZenUML开发者提供(有限的)免费技术支持。


About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK