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

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

作者: 凝枫 | 来源:发表于2019-07-09 15:55 被阅读0次

    思想概述

    读取操作必须是无害的,暂时不考虑大并发把服务器压垮这种极端场景,就一般而言,我们可以说,一个合格的查询接口所达到的效果应该是: 无论你执行多少次查询,系统的数据都是不会发生变化的 所以,对于一个陌生的系统,如果对方给了你【增删改查】四个接口,那么再没有深入了解业务的情况下,你首先进行测试的接口,一定是查询接口

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

    实现策略

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

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

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

    下一篇 充血模型之实体

    相关文章

      网友评论

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

          本文链接:https://www.haomeiwen.com/subject/idoxkctx.html