1. 程式人生 > 程式設計 >六邊形架構和分層架構的區別

六邊形架構和分層架構的區別

作為一個後端程式設計師,MVC三層架構的模式相信大家都不會陌生,三層分別從上而下排布,只能由上層呼叫下層。一般越往下層越通用,越上層越細節。

隨著某些核心業務的訪問量發展,通常我們需要去進行優化的措施,比如加快取,加MQ,換資料來源

1.快取可選redis,memcache
2.MQ可選kafka,rocketmq,rabbitmq
3.資料來源可選:mysql,mongodb,elasticsearch
複製程式碼

當然,我們在做這些優化的時候,會將mq,mongodb等看成基礎設施層。 由此衍生出四層的架構,infrastructure裡封裝redis和mq的通用呼叫邏輯

這些優化的動作,通常不會改變原有的業務邏輯。但是為了做優化,我們會將它寫在service層,比如:

1.執行成功就發個MQ
2.執行某個事件的時候,同步一下快取
3.依賴關係為,domain依賴infrastructure
複製程式碼

問題點:

按道理來說,domain層是寫業務邏輯的,優化不會涉及到業務邏輯的改動,但是卻改動了domain層。由於domain層依賴了infrastucture的原因,導致業務依賴於具體的實現技術。所以,為了將業務與具體實現做分離,我們採用依賴倒置的手段去重構

依賴倒置原則的包含如下的三層含義:
1.高層模組不應該依賴低層模組,兩者都應該依賴其抽象
2.抽象不應該依賴細節
3.細節應該依賴抽象
複製程式碼

高層模組不依賴低層模組:那就可以在domain層定義儲存的介面,如AARepository,但是不寫具體的技術實現

抽象不依賴細節:在domain層裡,不依賴其他包的類,如用到資料儲存時,直接呼叫domain的抽象介面即可

高層通過依賴注入的方式,將基礎設施的實現傳到domain層中

如此一來,我們的架構就不再是分層的結構(從上往下呼叫)。而是將抽象全部堆在domain層,將細節全部往application和infrastructure去推。而越抽象越穩定,所以通過這種做法能夠有效減少業務的變更。

架構變成了一種從內而外的邏輯,越往內越抽象,越往外越細節。在北向閘道器,可以使用rest和dubbo去呼叫業務邏輯,南向閘道器可以將資料寫到redis或mq。

具體程式碼實現

如下業務:傳送課程成功了,要傳送訊息給裝置IOT,傳送MQ,更新快取

傳統做法:

void sendCourse(){
	//執行業務邏輯
    //傳送訊息給裝置IOT
    //傳送MQ
    //更新快取
}
複製程式碼

隨著優化方案的不斷增加,業務邏輯會越堆越多 六邊形架構+EDA做法

public SendCourseService sendCourseService{
     void sendCourse(SendCourseCommand command){
	    //執行業務邏輯併發布釋出課程事件
        eventBus.push(SendCourseEvent())
     } 
}
複製程式碼

當我們要做更新快取操作的時候,實際上與業務邏輯沒有什麼關係,可以定義一個監聽者去監聽釋出課程事件。這個SendCourseCacheHandler類要寫到哪裡呢?

public class SendCourseCacheHandler{
   
    private Jedis jedis;

    public void deleteCache(SendCourseEvent event){
         //刪除快取
    }
}
複製程式碼
  1. 按照抽象往domain寫,細節往外寫的劃分,寫到基礎設施層不合適,因為與業務強相關。

  2. 寫到domain不合適,因為與redis這些具體實現強相關。

所以這個類應該寫在application層,其實是domain去呼叫application,所以說。。。這並不是分層架構。我們思考的時候是按抽象程度去判斷應該寫在哪裡,而不是從上往下呼叫。

那這個監聽者如何與業務結合起來呢?這時就發揮了application層的作用了,我們可以將application作為業務組裝的邏輯。

如我們在業務開始之前,將監聽者註冊上去,這樣在業務執行的時候,就可以回撥這些監聽者了

public class CourseAppService{
   
    private SendCourseCacheHandler sendCourseCacheHandler;

    private SendCourseMQHandler sendCourseMQHandler;

    private SendCourseService sendCourseService;

    private EventBus eventBus;

    public void sendCourse(SendCourseCommand command){
         //刪除快取
         eventBus.register(sendCourseCacheHandler);
         //發MQ
         eventBus.register(sendCourseMQHandler);
         sendCourseService.sendCourse(command);
    }
}
複製程式碼

適合場景:

1.讀/寫比較大的場景
2.對查詢實時性要求不高的場景
3.內部狀態改變會觸發各種資料的同步,如課程完成,課程釋出等等等等。。。
4.外部實現可替換,如mq可以隨意替換實現
複製程式碼

不適合場景:

只有簡單CRUD的業務,沒有重的業務邏輯,不適合搞那麼複雜,因為沒必要抽出domain層
複製程式碼