從單體架構升級到微服務,在代碼層面應注意的一些問題

因爲近年來的移動端的發展和 2C模式 的紅利,一些在風口的企業的業務獲得爆發式增加。從架構層面來講,業務驅動技術的變革,因此微服務架構的概念獲得不少企業的青睞,由於能夠解決服務的大流量和高併發以及穩定性的要求。前端

可是任何架構設計不是一蹴而就的,不能從起步就開始使用微服務,通常都是先經過單體架構來快速實現需求和搶佔市場,而後再迭代式擴展。不能一口氣吃個胖子。java

這幾年本身有經歷從單體到微服務的架構演變,也有直接參與到已經落地的微服務架構的項目中。見過好的架構設計,也見過一些孬的設計。好的架構設計,代碼結構優雅,分層清晰,業務邊界劃分明朗,業務開發人員職責清晰。很差的設計就會致使代碼混亂難以維護,對新需求沒法快速應變,開發人員容易在補丁上打補丁,最後形成積重難返不得不重構。程序員

架構師須要從業務層面和將來業務發展有個全面的規劃,讓架構高可用,易擴展,靈活易使用,隱藏其複雜性。好的架構會讓下面的業務開發人員按照既定的模式「傻瓜式」編程。web

既然第一步是單體架構,那麼好的單體架構設計,爲咱們後期的微服務拆分會有事半功倍的效果。避免重複勞動和過多的重寫。咱們能夠從這些方面進行一些有效的設計。數據庫

劃清業務邊界

若是對將來的架構有微服務的考慮,那麼在單體架構的時候就須要理清業務邊界的問題,常見的簡單劃分就是以業務區分,例如:用戶,商品,訂單,支付,權限等等,具體的拆分程度可根據自身業務量和須要作劃分。編程

當前流行的 DDD(領域驅動設計)能夠做爲一個指導原則,可是 DDD 比較偏向於理論,須要執行人員有良好的專業能力才能實施的比較好。服務器

代碼層次結構

業務區分好以後,就是項目代碼模塊的設計。在代碼層咱們須要根據MVC的模式,建議的代碼設計層次以下:mybatis

├─demo-common
│  │  demo-common.iml
│  │  pom.xml
│  │  
│  └─src
│      ├─main
│      │  ├─java
│      │  └─resources
│      └─test
│          └─java
├─demo-dao
│  │  demo-dao.iml
│  │  pom.xml
│  │  
│  └─src
│      ├─main
│      │  ├─java
│      │  └─resources
│      └─test
│          └─java
├─demo-service
│  │  demo-service.iml
│  │  pom.xml
│  │  
│  └─src
│      ├─main
│      │  ├─java
│      │  └─resources
│      └─test
│          └─java
└─demo-web
    │  demo-web.iml
    │  pom.xml
    │  
    └─src
        ├─main
        │  ├─java
        │  └─resources
        └─test
            └─java

主要包含4個 module 模塊架構

  • demo-common:基礎模塊,枚舉,常亮類,工具類,配置類。
  • demo-dao:Dao層,mapper接口,mapper.xml。
  • demo-service:服務接口提供層,業務service接口。
  • demo-web:web層,Controller類,服務接口,與外部進行交互。

各模塊之間的依賴關係爲:併發

項目 Module 模塊設計完成以後,每一個模塊的內部 package 包如何設計呢?一般有兩種劃分模式:根據業務模塊而後內部按MVC劃分根據MVC模式而後內部按業務劃分

一、根據業務模塊劃分,就是將每一個業務模塊做爲一個 package,而後每一個package裏面有本身的 MVC,這樣就作到業務模塊的隔離。

二、根據 MVC 模式劃分,先根據 MVC模式劃分不一樣的包,service,serviceImpl,dto等,而後再是各個業務本身的模型和服務接口。

針對上述的兩個劃分模式,我的的選擇是根據業務模式劃分,這樣的包設計與後期微服務拆分有良好的匹配度,拆分的時候只須要將每一個業務包下的代碼 Copy 到新的微服務中就好了,易遷移變更小。每一個模塊中對不一樣的業務經過 package 包名進行劃分,例如:com.example.jajian.service.order、com.example.jajian.service.user等。

└─src
    ├─main
    │  ├─java
    │  │  └─com
    │  │      └─example
    │  │          └─jajian
    │  │              ├─common
    │  │              │      BaserService.java
    │  │              │      
    │  │              └─service
    │  │                  ├─order
    │  │                  │  ├─dto
    │  │                  │  │      OrderDto.java
    │  │                  │  │      
    │  │                  │  └─service
    │  │                  │      │  OrderService.java
    │  │                  │      │  
    │  │                  │      └─impl
    │  │                  │              OrderServiceImpl.java
    │  │                  │              
    │  │                  ├─pay
    │  │                  │  ├─dto
    │  │                  │  │      PayDto.java
    │  │                  │  │      
    │  │                  │  └─service
    │  │                  │      │  PayService.java
    │  │                  │      │  
    │  │                  │      └─impl
    │  │                  │              PayServiceImpl.java
    │  │                  │              
    │  │                  └─user
    │  │                      ├─dto
    │  │                      │      UserDto.java
    │  │                      │      
    │  │                      └─service
    │  │                          │  UserService.java
    │  │                          │  
    │  │                          └─impl
    │  │                                  UserServiceImpl.java
    │  │                                  
    │  └─resources
    └─test
        └─java

這樣劃分有什麼好處?咱們單體架構的時候這樣開發,當須要拆分紅微服務的時候就能夠直接將業務包拆分出去,由於每一個業務包裏面就已經包含了全部的當前業務的關聯業務類。

避免多邊界業務的關聯查詢

單表關聯因爲業務須要並且簡單方便易使用,因此多表關聯查詢在單體服務中是廣泛存在的,若是咱們後期不須要作服務拆分則能夠不須要考慮這方面的限制。

可是若是後期有微服務的規劃,那麼單體服務的時候若是沒有作這個方面的限制,mybatis 的 mapper.xml中有過多的多表關聯查詢,這些關聯查詢會嚴重影響服務拆分的進度和複雜度。

若是同屬於一個業務領域則可使用關聯查詢,而那些微服務拆分後屬於不一樣領域的業務則應避免使用多表關聯查詢,由於不一樣的業務領域後期會被隔離拆分到不一樣的服務當中,即數據庫表都是分佈在不一樣的服務器上,全部服務之間都是經過RPC方式進行通訊,關聯查詢這時是沒法處理的。

Controller層儘可能不作業務邏輯處理

常看到不少 coder 會在Controller 層作一些業務處理,我的認爲這是很不規範的。Controller層是控制層,是和前端進行數據轉換的,這裏咱們應該只作請求的接受和返回,也無需作一些異常的try...catch...的捕獲,異常能夠經過全局通用攔截器統一進行攔截而後返回給前端異常提示語,提高代碼的簡潔性。

全部的參數校驗也放到 service層,由於若是服務內部調用也可使用提升代碼的共用度。固然分層領域模型最好也能區分開,

  • DO(Data Object):此對象與數據庫表結構--對應,經過DAO層向上傳輸數據源對象。
  • DTO(Date Transfer Object):數據傳輸對象,service或Manager向外傳輸的對象。
  • VO(View Object):顯示層的對象,一般是Web向模板渲染引擎層傳輸的對象。

這樣區分開的好處是,當你須要對展現層數據進行特殊定製化的時候能夠靈活變通,例如針對用戶隱私信息身份證號,手機號碼脫敏處理,或者用戶ID加密顯示等。

最後就是統一通用返回類了,經過這種格式的封裝咱們將數據格式進行全局格式化,這裏的狀態碼能夠本身設計的更詳細一點。

public class CommonResult<T> {

    public static final String CODE_SUCCESS = "0";
    public static final String CODE_FAILED = "9999";

    private String code;
    private T data;

    private String msg;

    private CommonResult(String code, T data, String msg) {
        this.code = code;
        this.data = data;
        this.msg = msg;
    }

    public boolean isSuccess() {
        return CODE_SUCCESS.equals(code);
    }

    public static <T> CommonResult<T> success() {
        return new CommonResult<>(CODE_SUCCESS, null, null);
    }

    public static <T> CommonResult<T> success(T data) {
        return new CommonResult<>(CODE_SUCCESS, data, null);
    }

    public static <T> CommonResult<T> success(T data, String msg) {
        return new CommonResult<>(CODE_SUCCESS, data, msg);
    }

    public static <T> CommonResult<T> failed() {
        return new CommonResult<>(CODE_FAILED, null, null);
    }

    public static <T> CommonResult<T> failed(String errorCode, String msg) {
        return new CommonResult<>(errorCode, null, msg);
    }

    public static <T> CommonResult<T> failed(String msg) {
        return new CommonResult<>(CODE_FAILED, null, msg);
    }

    public static <T> CommonResult<T> failed(T data, String msg) {
        return new CommonResult<>(CODE_FAILED, data, msg);
    }

    public static <T> CommonResult<T> failed(String errorCode, T data, String msg) {
        return new CommonResult<>(errorCode, data, msg);
    }
   
   // 省略 setter、getter
}

後記

以上只是列舉了單體服務將來規劃作微服務時須要注意的一部分簡單內容,每一個人在作單體架構拆分紅微服務的時候都會踩到各類各樣的坑,這些坑成了咱們的開發經驗,有了這些坑就會造成注意點,在咱們下次開發時就會具備指導意義。也許咱們程序員就是在踩坑和填坑的過程當中成長壯大起來的。

相關文章
相關標籤/搜索