(3) 基于领域分析设计的架构规范-读写隔离
本系列目录:
思想概述
读取操作必须是无害的,暂时不考虑大并发把服务器压垮这种极端场景,就一般而言,我们可以说,一个合格的查询接口所达到的效果应该是: 无论你执行多少次查询,系统的数据都是不会发生变化的
所以,对于一个陌生的系统,如果对方给了你【增删改查】四个接口,那么再没有深入了解业务的情况下,你首先进行测试的接口,一定是查询接口
为了达到一个合格的查询接口,对于系统的开发者的来说,必须保证所有的查询业务接口里,不能有任何对业务实体的修改操作,换句话说,所有的查询操作,只是对系统瞬时的一个快照,不对数据产生修改,自然对整个系统的业务运转也不产生任何变动。
实现策略
我们常说的读写分离,那是在应对性能问题时的一种解决方案。而我们这里特意换成了读写隔离,就是为了区分开两者。而这个隔离,是从更高层面来设计整个架构规范,是在项目设计刚开始的时候就考虑进去的。而且,实现难度小。
即使是基于现有的代码做重构,也只要挪代码块就行了,也没有什么业务风险,这个我们之后会再提到。
那么,很自然的,通过@Transactional(readOnly = true)
的控制,可以非常好的达到这个目的,这样,即使有开发人员不小心在其中做了修改操作,也会执行报错,给予很好的安全提示,这时,我们就需要重新审视这个需求,是否需要将修改操作分离出来。
import org.springframework.stereotype.Service; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.transaction.annotation.Transactional; /** * 以订单为主体的查询业务处理类 */ @Service @Transactional(readOnly = true) public class OrderFinder { @Autowired OrderRepository orderRepository; @Autowired UserRepository userRepository; /** 批量查询订单 */ List queryOrderByIds(List<Long> orderIds) /** 运营控制台订单分页查询 */ List pagingOrdersOnConsole(int pageSize,int pageNumber) /** APP端用户订单分页查询 */ List pagingOrdersOnApp(Long userId,int pageSize,int pageNumber) // more methods and fields .. }
·
有一些问题直得探讨:
Finder就等同于将Repository里的查询方法挪过来吗?
并不是,首先,Finder
是一个查询业务的处理类。一个查询业务,意味着从调用端发起请求到结果返回,本次过程是进行一次完整的查询操作,更直观的来说,像是下面这样
//..在一个入口层中,比如SpringMVC中的@Controller @Autowired private OrderFinder orderFinder; @GetMapping("/paging/") public CustomPagingResponse pagingOrders(int pageSize,int pageNumber){ return orderFinder.paging(pageSize,pageNumber); }
而并非另外一种为了删改某一个实体而通过主键或其他特定查询SQL来做出的查询操作,这种操作,将会在一个命令业务中直接通过Repository去做,比如
//..假定是在订单删除业务OrderDeleteService中的一部分 public void deleteOrder(Long orderId){ //根据条件定位到需要进行操作的Entity,这个过程,是命令操作的一部分,所以,它不是一个完整的查询业务,这个时候,不会用到Finder Order order = orderRepository.getById(orderId); //...接下来对order进行操作,省略 }
而且,往往在一个较为复杂的查询业务中,不仅仅需要从数据库中获取数据,往往可能还需要通过各类协议的远程接口获取数据,进行整合,这就更加需要Finder来进行归纳处理了。
·
每个实体(Entity)类都需要有一个Finder吗?
并不一定,因为并不是每个实体都会有这种业务需求。比如我们很容易想到,对于订单,会有很多终端需要通过各类条件查询订单列表,也会有某一条订单的详细信息。但相比之下,订单变更记录,可能唯一会被查询到的地方只会是在订单详情中的一个小列表,那么,实现的写法更倾向于如下这种:
public void OrderFinder{ @Autowired private OrderTrackRepository orderTrackRepository; //它依旧出现在 OrderFinder 中 public OrderDetailView queryOrderDetail(Long orderId){ //首先查询order基础数据 //然后补充查询订单变更数据 List<OrderTrack> orderTracks = orderTrackRepository.getByOrderId(orderId) //然后整合,返回整个View } }
所以,由于它只会存在于订单View中的一部分,自然不需要单独一个OrderDetailFinder
。当然如果未来OrderDetail
的代码量陡增,那是可以考虑重构的。
效果
其实大家回看自己现有的项目,里面有很多诸如【报表】,【采购单】,【详细信息】等等,其实,都是系统某一个侧面的快照而已。
将这些东西都一一隔离出来,这个时候,再去审视整个系统,你会有一种云开雾散,柳暗花明又一村的感觉。
因为剩下的命令操作,就是整个系统的脉动了。
详细参看之后的章节~
下一篇 充血模型之实体
低调大师中文资讯倾力打造互联网数据资讯、行业资源、电子商务、移动互联网、网络营销平台。
持续更新报道IT业界、互联网、市场资讯、驱动更新,是最及时权威的产业资讯及硬件资讯报道平台。
转载内容版权归作者及来源网站所有,本站原创内容转载请注明来源。
- 上一篇
(2) 基于领域分析设计的架构规范-领域分析基础
本系列目录: 改变与优势 领域分析基础 读写隔离 充血模型之实体 充血模型之Service 关于重构与落地 由于整个架构规范很大程度上是基于领域驱动设计(Domain Driven Design,DDD)的思维,所以,有必要在这里和大家先介绍一下DDD的一些概念。 领域聚合 让我们用一个相对简单的小电商系统来举例,来说明几个概念,这个电商系统的大概需求如下 我们主营的商品是甜品,计划让用户能过通过微信小程序来完成下单到支付的整个流程 用户能够在我们的小程序主页选择甜品主食,然后选择详细的一些辅料搭配,最终下单,(为了简化,暂不考虑购物车,也就是一单只有一个甜品) 目前只允许堂食,不考虑配送 对了,我们偶尔还会需要派发一些优惠券,用户能在支付的时候输入优惠券进行抵扣(一次最多用一张) 我们想能够记录好每个用户从下单,支付,到制作完成的整个流程记录 然后,我们能够很快得出这样一个模型图 简单来说就是: 一个用户可以创建多个订单,当然也可以不下单 一个订单会产生至少一条订单变更记录(从创建开始) 一个订单只对应一种商品,假定商品的“辅料搭配”只作为一个“备注”属性存储到商品 一个订单最多使用...
- 下一篇
(5) 基于领域分析设计的架构规范 - 充血模型之Service
本系列目录: 改变与优势 领域分析基础 读写隔离 充血模型之实体 充血模型之Service 关于重构与落地 Entity与Service,相爱相杀 好,接上一篇。 既然采用order.cancel()这种模式,那么一个新的问题来了: 所有的命令操作都要变成这样子吗?那曾经巨大的OrderService的代码,岂不是只是单纯挪了一个位置,放在Order里面了,除了上面所谓的可读性的优势,那还有什么用? 并不是,只是一部分放在实体类,其余的命令操作,依旧会采用一种Service来做。 所以,我们必然需要一个可以清晰量化的规范,来确定这些行为该放在哪里: 如果一个命令操作,只修改了一个聚合对象内部的相关数据,那么,就归属给这个聚合 比如,订单取消这个行为,需要做的事情有: 订单状态标记为取消 订单变更记录插入一条,“订单取消” 根据我们之前的图可以知道,这些修改操作,都在这个订单聚合内,很自然的归属给order 注意,我们反复强调了这里是“修改操作”,也就是说,如果需要我们在此操作期间,查询其他聚合的信息,只要不做修改,那就是允许的!就像下面这样: @Entity public class ...
相关文章
文章评论
共有0条评论来说两句吧...
文章二维码
点击排行
推荐阅读
最新文章
- CentOS8,CentOS7,CentOS6编译安装Redis5.0.7
- MySQL8.0.19开启GTID主从同步CentOS8
- CentOS7,CentOS8安装Elasticsearch6.8.6
- Docker使用Oracle官方镜像安装(12C,18C,19C)
- Jdk安装(Linux,MacOS,Windows),包含三大操作系统的最全安装
- Linux系统CentOS6、CentOS7手动修改IP地址
- CentOS7安装Docker,走上虚拟化容器引擎之路
- CentOS7编译安装Cmake3.16.3,解决mysql等软件编译问题
- SpringBoot2全家桶,快速入门学习开发网站教程
- CentOS7设置SWAP分区,小内存服务器的救世主