通过Kotlin DSL创建8x Flow建模图

Overview

8x Flow 业务建模图DSL生成工具

介绍

手动绘制8x Flow业务建模图是低效痛苦,特别是业务频繁变动时,更新业务建模图的成本很高。虽然有一些8x flow的建模图绘制工具,但是不支持语法检查,并且生成的图不够友好,因此我开发了此工具。此工具基于Kotlin语言,通过友好的自定义DSL生成8xFlow业务建模图。

目前此工具除了支持生成8x Flow业务建模图,还支持创建进程间(inter-process)进程内(intra-process)架构图

快速开始

环境准备

clone 8x-flow-diagram的代码到本地后,通过Intellij Idea打开工程,查看8x-flow-diagram/src/test/kotlin下的示例代码。

注意:项目是通过PlantUML API生成建模图,如果生成图例失败,请查看是否安装了GraphViz

8x Flow业务建模图

  • 创建时标对象(Evidences):包括方案征集书(RFP)、提案(Proposal)、合约(Contract)、履约请求(Fulfillment-Request)、 履约确认(Fulfillment-Confirmation)、凭证(Evidence)。
  • 创建参与者对象(Participants)包括参与方(Party)、标的物(Thing)、场所(Place)。
  • 创建角色对象(Roles):包括合约中的角色(Role)、领域逻辑角色化(Domain)、第三方系统角色化(3rd System)、凭证角色化(Evidence As Role)。
  • 创建关系关联关系(1对1、1对N、无数量);扮演角色关系。

8xflow业务建模图图例

创建Helloword建模图

  1. 8x-flow-diagram/src/main/kotlin/doxflow/samples目录下创建hello-word-diagram.kts脚本文件。

  2. 键入如下代码:

    import doxflow.dsl.diagram_8x_flow
    
    diagram_8x_flow {
        context("商品销售上下文") {
            contract("商品订单合同") {
                key_timestamps("签订时间")
            }
        }
    } export "../../../diagrams/hello-word-diagram.png"
  3. 右键该文件运行,在8x-flow-diagram/diagrams目录下查看hello-word-diagram.png文件。

    商品订单合同

语法介绍

diagram_8x_flow

用来表示生成一张8xflow业务建模图,通过export来生成最终png图片。用法如下:

diagram_8x_flow {
   ...
} export "../../../diagrams/hello-word-diagram.png"

context

用来表示一个上下文,一张建模图可以包含多个上下文,可以在上下文中定义合约中的角色(Role),提供给其他图例使用。

diagram_8x_flow {
	context("商品销售上下文") {
    val seller = role_party("卖家")
    val buyer = role_party("买家")
     ...
  }
  context("三方支付上下文") {
     ...
  }
} export "../../../diagrams/hello-word-diagram.png"

rfp(非必选)

rfp在建模图中是非必选项,如果DSL中包含rfp,后续的图例必须包裹在rfp之中(这样就可以区分一个context中多个rfp关联的propsal和contract)。

diagram_8x_flow {
    context("商品销售上下文") {
        val seller = role_party("卖家")
        val buyer = role_party("买家")

        rfp("询问商品价格", buyer) {
          	//指定关键时间,可以传入多个时间,逗号分隔
            key_timestamps("创建时间")
            //指定关键数据项,可以传入多个数据项,逗号分隔
          	key_data("报价")
        }
    }
} export "../../../diagrams/contract_with_rfp_diagram.png"

注意:不能指定rfp和proposal之间的关联关系,默认1对1。

proposal(非必选)

proposal在建模图中也是非必选,当且仅当rfp出现时proposal必选

diagram_8x_flow {
    context("商品销售上下文") {
        val seller = role_party("卖家")
        val buyer = role_party("买家")

        rfp("询问商品价格", buyer) {
            key_timestamps("创建时间")
            proposal("商品报价方案", seller) {
                key_timestamps("创建时间")
                key_data("报价金额")
								// 可以通过participant_thing来创建标的物,通过associate关键字让标的物连接到当前proposal
                participant_thing("商品") associate this
            }
        }
    }
} export "../../../diagrams/contract_with_rfp_diagram.png"

注意:不能指定proposal和contract之间的关联关系,默认1对1。

contract

contract是建模图中的核心,可以在proposal或者context下包裹。

diagram_8x_flow {
    context("商品销售上下文") {
        val seller = role_party("卖家")
        val buyer = role_party("买家")

        rfp("询问商品价格", buyer) {
            key_timestamps("创建时间")

            proposal("商品报价方案", seller) {
                key_timestamps("创建时间")
                key_data("报价金额")
                participant_thing("商品") associate this
								
              	// 合约可以设置0..N个合约签订者,推荐设置双方合约
                contract("商品订单合同", seller, buyer) {
                    key_timestamps("签订时间")
                  	// 同样,合约也可以设置关键数据
                  	//key_data("关键数据项")
                }
            }
        }
    }
} export "../../../diagrams/contract_with_rfp_diagram.png"

fulfillment

fulfillment表示一组履约项,包含requestconfirmation。我们可以通过AssociationType来指定contract和fulfillment之间的对应关系。

import doxflow.common.AssociationType.*
...
 context("信息推广上下文") {
   			// 可以通过played关键字让参与方party扮演角色party
        val advertiser = role_party("广告主") played participant_party("预充值用户")
        val promoter = role_party("推广商") played participant_party("思沃租房")
   ...
   contract("信息推广服务合同", advertiser, promoter) {
		key_timestamps("签订时间")
      fulfillment("推广重启", ONE_TO_N) {
        // request可以指定或者忽略履约角色,但是不建议省略(三方合同可以省略)
        request(advertiser) {
          key_timestamps("创建时间", "过期时间")
        }
        // confirmation可以指定或者忽略履约角色,但是不建议省略(三方合同可以省略)
        confirmation(promoter) {
          key_timestamps("启动时间")
        }
      }
		}
 }
...

participant_xxx

participant_xxx可以用来创建参与者,包括如下DSL:

  • participant_party
  • participant_thing
  • participant_place

role_xxx

participant_xxx可以用来创建角色,包括如下DSL:

  • role_party
  • role_domain
  • role_3rd_system

凭证创建和凭证角色化

凭证是通过evidence语法创建,目前仅仅支持在confirmation中创建凭证。通过role方法完成凭证角色化。

diagram_8x_flow {
  // 凭证角色化是跨上下文的,因此需要在diagram_8x_flow下定义需要角色化的fulfillment(其实是fulfillment下的confirmation角色化)
    lateinit var refundInPrepaidContext: fulfillment
    
    context("预充值协议上下文") {
        val houseAgent = participant_party("房产经纪人")
        val prepaidUser = role_party("预充值用户") played houseAgent
        val rentingPlatform = role_party("思沃租房")

        contract("预充值协议", prepaidUser, rentingPlatform) {
            key_timestamps("签订时间")
            participant_place("预充值账户") associate this

          	// 这里是重点因为该履约项的确认是通过其他上下文的凭证来扮演的,所以需要临时保存
            refundInPrepaidContext = fulfillment("余额退款") {
                request(prepaidUser) {
                    key_timestamps("创建时间", "过期时间")
                    key_data("金额")
                }

                confirmation(rentingPlatform) {
                    key_timestamps("创建时间")
                    key_data("金额")
                }
            }
        }
    }

    context("三方支付上下文") {
        contract("XXX支付协议") {
            key_timestamps("签订时间")
            fulfillment("代付") {
                request {
                    key_timestamps("创建时间", "过期时间")
                    key_data("金额")
                }

                confirmation {
                    key_timestamps("创建时间")
                    key_data("金额")
										// confirmation完成生成一个凭证(evidence),该凭证将扮演其他上下文履约项的角色
                    val evidence = evidence("支付凭证") {
                        key_timestamps("支付时间")
                        key_data("金额")
                    }
                  	//通过evidence的role关键字指定该evidence需要扮演哪个履约项的角色(这里指定了之前临时保存的fulfillment下的confirmation)
                    evidence role refundInPrepaidContext.confirmation
                }
            }
        }
    }
} export "../../../diagrams/prepaid_contract_diagram.png"

图例

  • 预充值协议 预充值协议
  • 信息推广服务合同 信息推广服务合同
  • 商品订单合同 商品订单合同

进程间(Inter-process)架构图

  • 创建服务层(Service)服务层用来表示不同服务所处的层级,例如:前端,BFF,应用服务层,领域服务等等。
  • 创建服务(Process)服务用来表示处于独立进程的业务模块,例如:微信客户端,MobileBFF,鉴权认证服务等等。
  • 创建进程内组件(Component)进程内组件用来表示业务模块内的关键组件,例如:信息推广服务中包含推广报价引擎组件。

它们三者的关系式:Service包含Procss包含Component。

  • 进程间架构图

  • 一部分进程间架构交互图

创建一个简单进程间架构图

  1. 8x-flow-diagram/src/main/kotlin/architecture/samples目录下创建inter_process_diagram.kts脚本文件。

  2. 键入如下代码:

    import architecture.diagram_inter_process
    
    diagram_inter_process {
      	// 颜色可以省略,也可以通过#ffffff方式自定义颜色
        service("应用服务", "#LightSeaGreen") {
            process("租赁信息应用服务")
            process("推广服务应用服务")
            process("后台管理应用服务")
        }
    
        service("核心业务能力", "#HotPink") {
            process("信息推广服务") {
                component("推广报价引擎", "#orange")
            }
            process("预充值服务")
        }
    
        service("领域服务", "#orange") {
            process("房屋信息管理系统")
            process("用户账户管理系统")
        }
    
        service("第三方系统", "#gray") {
            process("微信支付")
            process("支付宝支付")
            process("银联支付")
            process("ADX数据监测系统")
            process("发票代开服务")
            process("短信发送服务")
        }
    } export "./diagrams/inter_process_diagram.png"
  3. 右键该文件运行,在8x-flow-diagram/src/main/kotlin/architecture/samples/diagrams目录下查看inter_process_diagram.png文件。

    inter_process_diagram

语法介绍

diagram_inter_process

用来表示生成一张进程间架构图,通过export来生成最终png图片。用法如下:

diagram_inter_process {
   ...
} export "../../../diagrams/inter_process_diagram.png"

service

用来表示不同服务所处的层级。内部必须包含process

process

用来表示处于独立进程的业务模块可以和其他process进行交互。用法如下:

diagram_inter_process {
		service("前端", "#Cyan") {
      	// 可以让前端的中的“思沃租房通用版Web端”组件通过call方法调用BFF的“思沃租房WebBF”组件
        process("思沃租房通用版Web端").call("思沃租房WebBFF","1. GET /web-bff/ads")
    }
  	service("BFF", "#RoyalBlue") {
        process("思沃租房WebBFF")
				process("思沃租房MobileBFF")
    }
  } export "./diagrams/tw_renting_inter_process_communication_diagram.png"

component

用来表示业务模块内的关键组件必须包含在process内部。用法如下:

diagram_inter_process {
    service("核心业务能力", "#HotPink") {
        process("信息推广服务") {
          	// 在业务模块内部定义关键组件
            component("推广报价引擎", "#orange")
        }
        process("预充值服务")
    }
} export "./diagrams/inter_process_diagram.png"

图例

进程内(Intra-process)架构图

  • 创建分层(Layer)分层用来表示进程内架构中的层。例如:应用层,业务逻辑层,数据层等
  • 创建组件(Component)组件用来表示每一层包含的组件。
  • 创建进程外服务(Process)用来表示进程外服务。
  • 进程内架构图

    intra-process-sample

语法介绍

diagram_intra_process

用来表示生成一张进程内架构图,通过export来生成最终png图片。用法如下:

diagram_intra_process {
   ...
} export "../../../diagrams/intra_process_diagram.png"

layer

用来表示进程内架构分层。内部必须包含component

diagram_intra_process {
  	//	定义应用层包含哪些组件
     layer("应用层", "#HotPink") {
        component("Activity")
        component("ViewModel")
        component("Service")
    }
} export "./diagrams/intra_process_diagram.png"

component

用来表示进程内的组件必须包含在layer内部组件可以和其他组件(component)以及进程外服务(process)交互。用法如下:

diagram_inter_process {
     layer("应用层", "#HotPink") {
       // 通过call方法完成组件的调用,call方法参数1是组件名,参数2是调用的描述(optional)
        component("Activity").call("ViewModel", "方法调用")
        component("ViewModel").call("Presenter", "方法调用")
        component("Service").call("Presenter", "方法调用")
    }
      layer("业务层", "#orange") {
        component("Presenter")
    }
} export "./diagrams/intra_process_diagram.png"

process

用来表示进程外服务可以和进程的内的component进行交互。来表示进程边界的调用关系。用法如下:

diagram_intra_process {
    layer("应用层", "#HotPink") {
        component("Activity").call("ViewModel", "方法调用")
        component("ViewModel").call("Presenter", "方法调用")
        component("Service").call("Presenter", "方法调用")
    }

    layer("业务层", "#orange") {
        component("Presenter").call("Repository", "方法调用")
    }

    layer("数据层", "#LightSeaGreen") {
        val repo = component("Repository")
        repo.call("DBDataSource", "方法调用")
        repo.call("RemoteDataSource", "方法调用")
        // DBDataSource组件会调用系统的DB来读写数据
        component("DBDataSource").call("SqliteDB", "读写")
        // RemoteDataSource组件会调用进程外的BFF来获取数据
        component("RemoteDataSource").call("MobileBFF", "Http请求")
    }

  	// 进程外的push服务会调用进程内的service来推送消息
    process("PushService").call("Service", "消息推送")
    // 进程外的BFF服务
    process("MobileBFF")
    // 进程外的系统数据库
    process("SqliteDB")

} export "./diagrams/intra_process_diagram.png"

图例

后续开发计划

  • 集成内架构测试策略。TODO
  • 通过8xFlow建模图的履约项生成API文档。TODO

其他

如果有问题,可以邮箱联系

You might also like...
Releases(v1.7.0)
Owner
behring
behring