文档章节

(3) 基于领域分析设计的架构规范-读写隔离

不知道自己什么不知道
 不知道自己什么不知道
发布于 07/09 15:08
字数 1418
阅读 571
收藏 7

本系列目录:

  1. 改变与优势
  2. 领域分析基础
  3. 读写隔离
  4. 充血模型之实体
  5. 充血模型之Service
  6. 关于重构与落地

思想概述

读取操作必须是无害的,暂时不考虑大并发把服务器压垮这种极端场景,就一般而言,我们可以说,一个合格的查询接口所达到的效果应该是: 无论你执行多少次查询,系统的数据都是不会发生变化的

所以,对于一个陌生的系统,如果对方给了你【增删改查】四个接口,那么再没有深入了解业务的情况下,你首先进行测试的接口,一定是查询接口

为了达到一个合格的查询接口,对于系统的开发者的来说,必须保证所有的查询业务接口里,不能有任何对业务实体的修改操作,换句话说,所有的查询操作,只是对系统瞬时的一个快照,不对数据产生修改,自然对整个系统的业务运转也不产生任何变动。

实现策略

我们常说的读写分离,那是在应对性能问题时的一种解决方案。而我们这里特意换成了读写隔离,就是为了区分开两者。而这个隔离,是从更高层面来设计整个架构规范,是在项目设计刚开始的时候就考虑进去的。而且,实现难度小。

即使是基于现有的代码做重构,也只要挪代码块就行了,也没有什么业务风险,这个我们之后会再提到。

那么,很自然的,通过@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的代码量陡增,那是可以考虑重构的。

效果

其实大家回看自己现有的项目,里面有很多诸如【报表】,【采购单】,【详细信息】等等,其实,都是系统某一个侧面的快照而已。

将这些东西都一一隔离出来,这个时候,再去审视整个系统,你会有一种云开雾散,柳暗花明又一村的感觉。

因为剩下的命令操作,就是整个系统的脉动了。

详细参看之后的章节~

下一篇 充血模型之实体

© 著作权归作者所有

不知道自己什么不知道
粉丝 27
博文 6
码字总数 11878
作品 0
长沙
高级程序员
私信 提问
亿级流量电商详情页系统的大型高并发与高可用缓存架构实战

对于高并发的场景来说,比如电商类,o2o,门户,等等互联网类的项目,缓存技术是Java项目中最常见的一种应用技术。然而,行业里很多朋友对缓存技术的了解与掌握,仅仅停留在掌握redis/memca...

登录404
2017/06/05
1K
0
谈谈MySQL支持的事务隔离级别,以及悲观锁和乐观锁的原理和应用场景?

在日常开发中,尤其是业务开发,少不了利用 Java 对数据库进行基本的增删改查等数据操作,这也是 Java 工程师的必备技能之一。做好数据操作,不仅仅需要对 Java 语言相关框架的掌握,更需要对...

架构师springboot
01/02
0
0
基于PaaS和SaaS研发的商业云平台实战

背景介绍 SaaS、PaaS、IaaS等云服务模式已经被大家普遍认可,在研发过程中借力一些PaaS和SaaS平台,能够提升产品的研发速度和功能稳定程度。本文将通过真实的商业平台案例-MaxWon研发实战,分...

小白白白白白白
2016/09/06
1K
0
全球架构师峰会 | 基于PaaS和SaaS研发的商业云平台实战 - 精华总结

摘要 本篇文章是力谱宿云技术总监秦鹏参与 2016年全球架构师峰会时,应邀参加 “基础云服务应用案例” 主题演讲的精华总结。 背景介绍 SaaS、PaaS、IaaS等云服务模式已经被大家普遍认可,在研...

力谱宿云
2016/09/06
298
1
张升:农业银行的分布式架构应用实践与展望

近年来,以阿里为代表的互联网企业提出的“去IOE”,在业界引起了广泛的讨论。“去IOE”直接含义是不使用传统IT巨头的产品,这些厂商产品虽然好,但基本处于市场垄断地位,用户议价能力较弱,...

天天顺利
2015/07/16
0
0

没有更多内容

加载失败,请刷新页面

加载更多

Spark中map、mapPartitions、foreach、foreachPartitions算子

map 与 mapPartitions /** * Return a new RDD by applying a function to all elements of this RDD. */ def map[U: ClassTag](f: T => U): RDD[U] = withScope { val cleanF......

dreamness
8分钟前
0
0
spring boot 启动常见错误记录

1.错误: Description:Failed to configure a DataSource: 'url' attribute is not specified and no embedded datasource could be configured.Reason: Failed to determine a suit......

qimh
29分钟前
1
0
拥有有趣灵魂的程序员们,程序员访谈(一)

点击上方关注我们,让小care关爱你! 程序员群体一直都是低调多金的代表,而近段时间以来,程序员在网络上除了高薪之外,总是会和屌丝、苦逼、格子衫、没情趣...联系在一起。黑程序员的段子也...

ITCare
今天
33
0
Linux输入法fcitx的安装问题

Fcitx 总共要安装的包如下 fcitxfcitx-binfcitx-config-commonfcitx-config-gtk | fcitx-config-gtk2fcitx-datafcitx-frontend-allfcitx-frontend-gtk2fcitx-frontend-gtk3......

CHONGCHEN
今天
19
0
网络基础

前言: 最近整理一些以前的学习笔记(有部分缺失,会有些乱,日后再补)。 过去都是存储在本地,此次传到网络留待备用。 计算机网络的功能: 1.数据通信; 2.资源共享; 3.增加数据可靠性; 4....

迷失De挣扎
今天
21
0

没有更多内容

加载失败,请刷新页面

加载更多

返回顶部
顶部