(3) 基于領(lǐng)域分析設(shè)計(jì)的架構(gòu)規(guī)范-讀寫(xiě)隔離

思想概述

讀取操作必須是無(wú)害的,暫時(shí)不考慮大并發(fā)把服務(wù)器壓垮這種極端場(chǎng)景,就一般而言,我們可以說(shuō),一個(gè)合格的查詢接口所達(dá)到的效果應(yīng)該是: 無(wú)論你執(zhí)行多少次查詢,系統(tǒng)的數(shù)據(jù)都是不會(huì)發(fā)生變化的 所以,對(duì)于一個(gè)陌生的系統(tǒng),如果對(duì)方給了你【增刪改查】四個(gè)接口,那么再?zèng)]有深入了解業(yè)務(wù)的情況下,你首先進(jìn)行測(cè)試的接口,一定是查詢接口

所以,為了達(dá)到一個(gè)合格的查詢接口,對(duì)于系統(tǒng)的開(kāi)發(fā)者的來(lái)說(shuō),必須保證所有的查詢業(yè)務(wù)接口里,不能有任何對(duì)業(yè)務(wù)實(shí)體的修改操作,換句話說(shuō),所有的查詢操作,只是對(duì)系統(tǒng)瞬時(shí)的一個(gè)快照,不對(duì)數(shù)據(jù)產(chǎn)生修改,自然對(duì)整個(gè)系統(tǒng)的業(yè)務(wù)運(yùn)轉(zhuǎn)也不產(chǎn)生任何變動(dòng)

實(shí)現(xiàn)策略

我們常說(shuō)的讀寫(xiě)分離,那是在應(yīng)對(duì)性能問(wèn)題時(shí)的一種解決方案。而我們這里特意換成了讀寫(xiě)隔離,就是為了區(qū)分開(kāi)兩者。而這個(gè)隔離,是從更高層面來(lái)設(shè)計(jì)整個(gè)架構(gòu)規(guī)范,是在編碼剛開(kāi)始的時(shí)候,變考慮進(jìn)去的,而且,實(shí)現(xiàn)難度,不大,或者說(shuō),非常小。

即使是基于現(xiàn)有的代碼做重構(gòu),也只要挪代碼塊就行了,也沒(méi)有什么業(yè)務(wù)風(fēng)險(xiǎn),這個(gè)我們之后會(huì)再提到。

那么,很自然的,通過(guò)@Transactional(readOnly = true)的控制,可以非常好的達(dá)到這個(gè)目的

import org.springframework.stereotype.Service;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.transaction.annotation.Transactional;

/**
 * 以訂單為主體的查詢業(yè)務(wù)處理類(lèi)
 */
@Service
@Transactional(readOnly = true)
public class OrderFinder {

    @Autowired
    OrderRepository orderRepository;
    @Autowired
    UserRepository userRepository;
     
     /** 批量查詢訂單 */
     List queryOrderByIds(List<Long> orderIds)
     
     /** 運(yùn)營(yíng)控制臺(tái)訂單分頁(yè)查詢 */
     List pagingOrdersOnConsole(int pageSize,int pageNumber)
     
     /** APP端用戶訂單分頁(yè)查詢 */
     List pagingOrdersOnApp(Long userId,int pageSize,int pageNumber)
     
     // more methods and fields ..
}

·

有一些問(wèn)題直得探討:

Finder就等同于將Repository里的查詢方法挪過(guò)來(lái)嗎?

并不是,首先,Finder是一個(gè)查詢業(yè)務(wù)的處理類(lèi)。一個(gè)查詢業(yè)務(wù),意味著從調(diào)用端發(fā)起請(qǐng)求到結(jié)果返回,本次過(guò)程是進(jìn)行一次完整的查詢操作,更直觀的來(lái)說(shuō),像是下面這樣

    //..在一個(gè)入口層中,比如SpringMVC中的@Controller
    
    @Autowired
    private OrderFinder orderFinder;

    @GetMapping("/paging/")
    public CustomPagingResponse pagingOrders(int pageSize,int pageNumber){
          return orderFinder.paging(pageSize,pageNumber);
    }

而并非另外一種為了刪改某一個(gè)實(shí)體而通過(guò)主鍵或其他特定查詢SQL來(lái)做出的查詢操作,這種操作,將會(huì)在一個(gè)命令業(yè)務(wù)中直接通過(guò)Repository去做,比如

    //..假定是在訂單刪除業(yè)務(wù)OrderDeleteService中的一部分
    
    public void deleteOrder(Long orderId){
    
        //根據(jù)條件定位到需要進(jìn)行操作的Entity,這個(gè)過(guò)程,是命令操作的一部分,所以,它不是一個(gè)完整的查詢業(yè)務(wù),這個(gè)時(shí)候,不會(huì)用到Finder
        Order order = orderRepository.getById(orderId);
        
        //...接下來(lái)對(duì)order進(jìn)行操作,省略
    }

而且,往往在一個(gè)較為復(fù)雜的查詢業(yè)務(wù)中,不僅僅需要從數(shù)據(jù)庫(kù)中獲取數(shù)據(jù),往往可能還需要通過(guò)各類(lèi)協(xié)議的遠(yuǎn)程接口獲取數(shù)據(jù),進(jìn)行整合,這就更加需要Finder來(lái)進(jìn)行歸納處理了。

·

每個(gè)實(shí)體(Entity)類(lèi)都需要有一個(gè)Finder嗎?

并不一定,因?yàn)椴⒉皇敲總€(gè)實(shí)體都會(huì)有這種業(yè)務(wù)需求。比如我們很容易想到,對(duì)于訂單,會(huì)有很多終端需要通過(guò)各類(lèi)條件查詢訂單列表,也會(huì)有某一條訂單的詳細(xì)信息。但相比之下,訂單變更記錄,可能唯一會(huì)被查詢到的地方只會(huì)是在訂單詳情中的一個(gè)小列表,那么,實(shí)現(xiàn)的寫(xiě)法更傾向于如下這種:

public void OrderFinder{
    @Autowired
    private OrderTrackRepository orderTrackRepository;
    
    //它依舊出現(xiàn)在 OrderFinder 中
    public OrderDetailView queryOrderDetail(Long orderId){
    
        //首先查詢order基礎(chǔ)數(shù)據(jù)
        
        //然后補(bǔ)充查詢訂單變更數(shù)據(jù)
        List<OrderTrack> orderTracks = orderTrackRepository.getByOrderId(orderId)
        
        //然后整合,返回整個(gè)View
    }
}

所以,由于它只會(huì)存在于訂單View中的一部分,自然不需要單獨(dú)一個(gè)OrderDetailFinder。當(dāng)然如果未來(lái)OrderDetail的代碼量陡增,那是可以考慮重構(gòu)的。

下一篇 充血模型之實(shí)體

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書(shū)系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

友情鏈接更多精彩內(nèi)容