entitybuilder--一个简单的业务通用框架
关于业务通用框架的思考
业务系统是千差万别的,例如,保存、更新和删除订单,或者保存订单和保存客户,走的根本不是一个流程。但是,它们还是有共同点,它们的流程大致可以分成下面的几个部分:
- 拿到增删改等操作所需的基础数据;
- 初始化基础数据;
- 对基础数据进行校验;
- 利用基础数据,构建出要进行增删改等操作的对象;
- 持久化或其他操作。
基于这一点,我试着抽取出一套适用于不同业务、不同用例、不同场景的通用业务框架。刚好,去年部门开始重构订单系统,我试着将自己的想法付诸行动。经过几次调整后,总算形成了一个简单的业务通用框架--entitybuilder。
当然,我更多想表达的,是一种思想、一种规范,而非工具本身。如果真要说是框架,entitybuilder 就太简陋了。
entitybuilder 的结构
entitybuilder 包含三个主要部分,基础数据 base data、构建器 entity builder 和结果对象 result entity。我拿到了 base data,把它丢进 entity builder,entity builder 就会帮我构建出 result entity,拿到 result entity 后,我要持久化也行,直接返回给更上层调用者也行。
entity builder 构建 result entity 的过程被定义为:初始化->校验->构建。
entitybuilder1.0--规范流程
基于上面的模型,也就有了 entitybuilder1.0,它的结构如下。
对调用者来说,只需要设置好基础数据,调用build
方法就能完成初始化、校验、构建,当然,EntityBuilder
还支持仅作为校验器使用,因为有时我们并不需要结果对象,只需要校验基础数据就行。
对实现者来说,用户需要继承AbstractEntityBuilder
,并实现初始化、校验和构建方法。
以订单保存为例,下面展示如何使用 entitybuilder。代码的调用非常简单,这里需要注意,EntityBuilder
对象必须是多例的。
public String save(DefaultOrderSaveCmd cmd) {
// 获取构建器(多例的)
EntityBuilder<BaseOrderSaveCmd, OrderSaveE> entityBuilder = getSaveEntityBuilder();
// 设置基础数据
entityBuilder.setBaseData(cmd);
// 构建保存实体
OrderSaveE entity = entityBuilder.build();
// 持久化操作
orderDao.save(entity);
return entity.getId();
}
entitybuilder2.0--多场景支持
entitybuilder1.0 只是规范了业务流程,在多场景方面还是存在问题。
一个业务用例可能会有不同的场景,例如,客户保存可能就不只一个入口,按照 entitybuilder1.0 的设计,我们需要将所有场景的逻辑都堆积到构建器中。显然,这是不合理的。
参考 spring 的 postprocessor,我在构建器中引入了校验器和处理器的支持。构建器中定义了用例的主流程,不同场景可以通过注册校验器和处理器来对主流程进行修饰。在 entitybuilder1.0 的基础上修改,得到以下结构:
和 entitybuilder 1.0 相比,对调用者来说,可以通过注册验器和处理器来影响构建器的主流程,对构建器实现者来说,改为继承AbstractFlexibleEntityBuilder
。
那么校验器和处理器如何影响主流程呢?下面通过一张图来说明。在 entitybuilder1.0 中,调用者无需知道构建器中的逻辑,现在却需要知道(有好有坏吧)。
以订单保存为例,代码示例如下。
public String save(DefaultOrderSaveCmd cmd) {
// 获取构建器
AbstractFlexibleEntityBuilder<BaseOrderSaveCmd, OrderSaveE> entityBuilder = getSaveEntityBuilder();
// 设置基础数据
entityBuilder.setBaseData(cmd);
// 对构建器进行部分更改,例如注册处理器或检验器
entityBuilder.registerValidator(myOrderSaveValidator);
entityBuilder.registerEntityBuilderPostProcessor(myOrderSavePostProcessor);
// 构建保存实体
OrderSaveE entity = entityBuilder.build();
// 持久化操作
orderDao.save(entity);
return entity.getId();
}
基础数据的组成
entitybuilder 的可用性极大依赖于基础数据的规范。在 entitybuilder 中,基础数据的成员属性应该包含两个部分:主体属性和关联对象。例如,订单的基础数据就包括了订单本身以及它的关联对象,如客户、操作人等。
为什么要包含这两个部分呢?
构建器中包含了业务的大部分逻辑,我们需要调用各种通用方法,这些方法的入参对象无非就是主体属性或关联对象属性。基础数据对象将在 EntityBuilder 的整个生命周期中传递,通过它来传递关联对象,可以保证关联对象只需要初始化一次,从而减少重复 IO。
如果一开始放入构建器的基础数据对象中已经有关联对象了,那么,构建器也不会再去初始化它。这一点在批量构建时将非常有用。
事务控制
在 entitybuilder 的规范中,结果对象的持久化是在一个事务/方法中完成主体对象和关联对象的持久化,但是,在某个场景下,我们需要在事务中进行某些自定义操作,例如,订单保存完成后,向某个外部系统推送数据,推送失败,事务跟着回滚。
针对这种场景,也是可以支持的。事务中的自定义操作将作为函数的形式传递,在基础数据中设置好,持久化时就会执行它。
@Override
public String save(DefaultOrderSaveCmd cmd) {
// 获取构建器
AbstractFlexibleEntityBuilder<BaseOrderSaveCmd, OrderSaveE> entityBuilder = getSaveEntityBuilder();
// 设置基础数据
entityBuilder.setBaseData(cmd);
// 对基础数据进行部分更改,例如设置保存事务中需要进行的操作
cmd.addSaveConsumer(order -> {
// 有的自定义操作需要放入保存事务,如果失败,订单数据也会回滚
// 省略代码······
});
// 构建保存实体
OrderSaveE entity = entityBuilder.build();
// 持久化操作
orderDao.save(entity);
return entity.getId();
}
// @Transactional
public String save(OrderSaveE entity) {
// 保存订单
// 省略代码······
// 保存产品
// 省略代码······
// 保存附件
// 省略代码······
// 执行保存事务中的函数
entity.getSaveConsumers().forEach(x -> x.accept(entity));
return entity.getId();
}
以上基本介绍完 entitybuilder。这里还是强调一点,我更多的是想表达一种思想、一种规范,因为作为工具,entitybuilder 还有很多需要改进的地方。
最后,感谢阅读。
参考资料
相关源码请移步:https://github.com/ZhangZiSheng001/zzs-code-thought/01-entitybuilder-demo
本文为原创文章,转载请附上原文出处链接:https://www.cnblogs.com/ZhangZiSheng001/p/14472782.html
entitybuilder--一个简单的业务通用框架的更多相关文章
- Node.js简单介绍并实现一个简单的Web MVC框架
编号:1018时间:2016年6月13日16:06:41功能:Node.js简单介绍并实现一个简单的Web MVC框架URL :https://cnodejs.org/topic/4f16442cca ...
- 一个简单的通讯服务框架(大家发表意见一起研究)JAVA版本
最近研究下java语言,根据一般使用的情况,写了个连接通讯服务的框架: 框架结构 C-Manager-S; 把所有通讯内容抽取成三个方法接口:GetData,SetData,带返还的Get; 所有数据 ...
- 从零构建一个简单的 Python Web框架
为什么你想要自己构建一个 web 框架呢?我想,原因有以下几点: 你有一个新奇的想法,觉得将会取代其他的框架 你想要获得一些名气 你遇到的问题很独特,以至于现有的框架不太合适 你对 web 框架是如何 ...
- Directx11学习笔记【十一】 画一个简单的三角形--effect框架的使用
这里不再介绍effect框架的具体使用,有关effect框架使用可参考http://www.cnblogs.com/zhangbaochong/p/5475961.html 实现的功能依然是画一个简单 ...
- 一个简单的CPP处理框架
好久没有在csdn上写过东西了,这么多年,一方面是工作忙,下班到家也没有开过电脑了,要陪小孩玩: 下面分享一段代码,是用CPP做的一个简单的消息(协议)处理框架: 是通过成员函数指针+map来实现的: ...
- JavaScript 实现一个简单的MVVM前端框架(ES6语法)
前言 随着前端各大框架的崛起,为我们平时的开发带来了相当的便利,我们不能一直停留在应用层面,今天就自己动手实现一个乞丐版的MVVM小框架 完整代码github地址 效果 html代码 <div ...
- 一个简单 Go Web MVC 框架实现思路
需要的知识点 为了防止你的心里不适,需要以下知识点: Go 基本知识 Go 反射的深入理解 使用过框架 Go Web 服务器搭建 package main import ( "fmt&quo ...
- 用werkzeug实现一个简单的python web框架
使用工具 Pycharm , Navicat , WebStorm等 使用库 Werkzeug用于实现框架的底层支撑,pymysql用于实现ORM,jinja2用于模板支持,json用于返回json数 ...
- [51单片机] 以PWM控制直流电机为例建一个简单的51工程框架
目录 1)功能概述 2)引脚连接 3)框架介绍 4)模块说明 5)复用规则 6)工程链接 1)功能概述 名称:独立按键控制直流电机调速 内容:对应的电机接口需用杜邦线连接到uln2003电机控制端; ...
随机推荐
- 牛客NC15879 A Simple Problem
传送门:A Simple Problem 题意 给定两个序列s1和s2,同样的数字可以用相同的别的数字代替(并且也可以是出现过的数字),问s2在s1中出现了几次. 题解 首先预处理一下这两个序列,因为 ...
- Codeforces Round #550 (Div. 3) E. Median String (思维,模拟)
题意:给你两个字符串\(s\)和\(t\),保证\(t\)的字典序大于\(s\),求他们字典序中间的字符串. 题解:我们假设题目给的不是字符串,而是两个10禁止的正整数,那么输出他们之间的数只要把他两 ...
- C# TCP应用编程二 同步TCP应用编程
不论是多么复杂的TCP 应用程序,双方通信的最基本前提就是客户端要先和服务器端进行TCP 连接,然后才可以在此基础上相互收发数据.由于服务器需要对多个客户端同时服务,因此程序相对复杂一些.在服务器端, ...
- 2.hello rabbitmq
作者 微信:tangy8080 电子邮箱:914661180@qq.com 更新时间:2019-07-22 22:49:50 星期一 欢迎您订阅和分享我的订阅号,订阅号内会不定期分享一些我自己学习过程 ...
- codeforces 6D
D. Lizards and Basements 2 time limit per test 2 seconds memory limit per test 64 megabytes input st ...
- 手撕 part1
1.宏定义三个数最大值 挺有意思 max((a), (b), (c)) (a) > (b)? ((a) > (c)? (a) : (c)) ((b) > (c)? (b) : (c) ...
- c# 类(4)
原文链接:https://csharp.net-tutorials.com/classes/visibility/ 可见性 Visibility 可见性 控制的是 访问权限的问题.最常见的就是priv ...
- 仿射加密与S-DES加密算法的实现
仿射加密 #include <iostream> #include <cstdio> using namespace std; char letter[30]; char _l ...
- 输入函数input()、运算符
一.input()函数的基本使用 present = input('大圣想要什么礼物') 作用:接受来自用户的输入 返回值类型:输入值的类型为str 值的存储:使用 = 对输入的值进行存储 name= ...
- webpack 5 new features All In One
webpack 5 new features All In One Webpack 5 release (2020-10-10) https://webpack.js.org/blog/2020-10 ...