Java開發架構篇:領域驅動設計架構基於SpringCloud搭建微服務

小傅哥發表於2020-05-22

作者:小傅哥
部落格:https://bugstack.cn

沉澱、分享、成長,讓自己和他人都能有所收穫!

一、前言介紹

微服務不是泥球小單體,而是具備更加清晰職責邊界的完整一體的業務功能服務。領域驅動設計的思想通過Domain的功能域設計,可以把核心功能與支撐功能很好的區分開。而在MVC的設計模式嚐嚐是把所有的;資料服務、定義的屬性類、提供的功能都在一條線上,這樣是非常快速的開發方式但在做微服務部署時候確很麻煩。

按照不同的業務場景可能設計出軟體在資料庫使用上會有單庫單表或者分庫分表,如果是一個體量足夠需要分庫分表設計的系統,在擴容時候它是否能滿足你的需求包括;

  1. 核心計算不涉及庫擴容,但是系統功能都在一起怎麼辦,已擴容都擴容了很浪費
  2. 所有的擴容都涉及到資料庫連線數增加,但並不是每個行為都直達到所有庫表
  3. 持續發展的業務會帶來資料激增,將來怎麼進行擴充套件,重新洗資料並不是很好的選擇

那麼實際開發大泥球架構時,不只是會遇到上面的問題,還可能會遇到工期很趕加個人也不提升效率,反覆交接程式碼扶不過三代等等,因此我們將服務拆分為獨立單體具備此核心域完整功能的系統是非常必要的。

如圖,是微服務資料庫使用的一種思想,我們希望路由層從最開始就被執行,使用者分群動態擴充套件
微信公眾號:bugstack蟲洞棧 | 微服務資料庫路由

二、案例目標

本案例通過使用SpringCloud將我們的服務架構擴充套件為通過路由呼叫的微服務

  1. 首先通過Eureka作為服務註冊與發現中心
  2. 然後使用Feign模式作為呼叫API介面
  3. 最後依賴於zuul設定路由轉發功能

為了方便測試,本案例會在itstack-demo-ddd-03中建4個工程;
itstack-demo-ddd-case{基於DDD的微服務}
itstack-demo-ddd-eureka-server{服務註冊與發現}
itstack-demo-ddd-feign{呼叫方,通過API介面呼叫}
itstack-demo-ddd-zuul{閘道器路由元件}

三、開發環境

  1. jdk1.8
  2. springboot 2.0.6.RELEASE 以及SpringCloud相關服務
  3. idea + maven

四、程式碼示例

1. itstack-demo-ddd-case | 基於DDD的微服務 {本段程式碼在上一章節已經演示}

itstack-demo-ddd-case
└── src
    ├── main
    │   ├── java
    │   │   └── org.itstack.demo
    │   │       ├── application
    │   │       │    ├── MallRuleService.java    
    │   │       │    └── MallTreeService.java    
    │   │       ├── domain
    │   │       │    ├── rule
    │   │       │    │   ├── model
    │   │       │    │   │   ├── aggregates
    │   │       │    │   │   │   └── UserRichInfo.java    
    │   │       │    │   │   └── vo
    │   │       │    │   │       ├── DecisionMatter.java
    │   │       │    │   │       ├── EngineResult.java
    │   │       │    │   │       ├── TreeNodeInfo.java
    │   │       │    │   │       ├── TreeNodeLineInfo.java    
    │   │       │    │   │       └── UserSchool.java    
    │   │       │    │   ├── repository
    │   │       │    │   │   └── IRuleRepository.java    
    │   │       │    │   └── service
    │   │       │    │       ├── engine
    │   │       │    │       │   ├── impl    
    │   │       │    │       │   └── EngineFilter.java    
    │   │       │    │       ├── logic
    │   │       │    │       │   ├── impl    
    │   │       │    │       │   └── LogicFilter.java    
    │   │       │    │       └── MallRuleServiceImpl.java    
    │   │       │    └── tree
    │   │       │        ├── model
    │   │       │        │   ├── aggregates
    │   │       │        │   │   └── TreeCollect.java    
    │   │       │        │   └── vo
    │   │       │        │       ├── TreeInfo.java    
    │   │       │        │       └── TreeRulePoint.java    
    │   │       │        ├── repository
    │   │       │        │   └── ITreeRepository.java    
    │   │       │        └── service
    │   │       │            └── MallTreeServiceImpl.java    
    │   │       ├── infrastructure
    │   │       │    ├── common
    │   │       │    │   └── Constants.java
    │   │       │    ├── dao
    │   │       │    │   ├── RuleTreeDao.java
    │   │       │    │   ├── RuleTreeNodeDao.java    
    │   │       │    │   └── RuleTreeNodeLineDao.java    
    │   │       │    ├── po
    │   │       │    │   ├── RuleTree.java
    │   │       │    │   ├── RuleTreeConfig.java
    │   │       │    │   ├── RuleTreeNode.java    
    │   │       │    │   └── RuleTreeNodeLine.java        
    │   │       │    ├── repository
    │   │       │    │   ├── cache
    │   │       │    │   │   └── RuleCacheRepository.java
    │   │       │    │   ├── mysql
    │   │       │    │   │   ├── RuleMysqlRepository.java    
    │   │       │    │   │   └── TreeMysqlRepository.java
    │   │       │    │   ├── RuleRepository.java    
    │   │       │    │   └── TreeRepository.java    
    │   │       │    └── util
    │   │       │        └── CacheUtil.java
    │   │       ├── interfaces
    │   │       │    ├── dto
    │   │       │    │    ├── DecisionMatterDTO.java
    │   │       │    │    └── TreeDTO.java    
    │   │       │    └── DDDController.java
    │   │       └── DDDApplication.java
    │   └── resources    
    │       ├── mybatis
    │       └── application.yml
    └── test
         └── java
             └── org.itstack.demo.test
                 └── ApiTest.java

2. itstack-demo-ddd-eureka-server | 服務註冊與發現

itstack-demo-ddd-eureka-server
└── src
    ├── main
    │   ├── java
    │   │   └── org.itstack.demo
    │   │       └── EurekaServerApplication.java
    │   └── resources    
    │       └── application.yml
    └── test
         └── java
             └── org.itstack.demo.test
                 └── ApiTest.java
EurekaServerApplication.java | 啟動服務
/**
 * 微信公眾號:bugstack蟲洞棧 | 專注原創技術專題案例
 * 論壇:http://bugstack.cn
 * Create by 小傅哥 on @2019
 */
@SpringBootApplication
@EnableEurekaServer
public class EurekaServerApplication {

    public static void main(String[] args) {
        SpringApplication.run( EurekaServerApplication.class, args );
    }

}
application.yml | 服務配置
server:
  port: 8989

eureka:
  instance:
    hostname: localhost
  client:
    registerWithEureka: false
    fetchRegistry: false
    serviceUrl:
      defaultZone: http://${eureka.instance.hostname}:${server.port}/eureka/

spring:
  application:
    name: itstack-demo-ddd-eureka-server

3. itstack-demo-ddd-feign | 呼叫方,通過API介面呼叫

itstack-demo-ddd-feign
└── src
    ├── main
    │   ├── java
    │   │   └── org.itstack.demo
    │   │       ├── domain
    │   │       │    └── TreeDTO.java
    │   │       ├── service
    │   │       │    └── MallService.java
    │   │       ├── web
    │   │       │    └── FeignController.java
    │   │       └── FeignApplication.java
    │   └── resources    
    │       └── application.yml
    └── test
         └── java
             └── org.itstack.demo.test
                 └── ApiTest.java
MallService.java | 通過註冊方式呼叫API
/**
 * 微信公眾號:bugstack蟲洞棧 | 專注原創技術專題案例
 * 論壇:http://bugstack.cn
 * Create by 小傅哥 on @2019
 */
@FeignClient(value = "itstack-demo-ddd-case")
public interface MallService {

    @RequestMapping(value = "/api/tree/queryTreeSummaryInfo", method = RequestMethod.POST)
    Object queryTreeSummaryInfo(@RequestBody TreeDTO request);

}
FeignApplication.java | 啟動服務
/**
 * 微信公眾號:bugstack蟲洞棧 | 專注原創技術專題案例
 * 論壇:http://bugstack.cn
 * Create by 小傅哥 on @2019
 */
@SpringBootApplication
@EnableEurekaClient
@EnableDiscoveryClient
@EnableFeignClients
public class FeignApplication {

    public static void main(String[] args) {
        SpringApplication.run(FeignApplication.class, args);
    }

}
application.yml | 服務配置
server:
  port: 9090

spring:
  application:
    name: itstack-demo-ddd-feign

eureka:
  client:
    serviceUrl:
      defaultZone: http://localhost:8989/eureka/

4. itstack-demo-ddd-zuul| 閘道器路由元件

itstack-demo-ddd-zuul
└── src
    ├── main
    │   ├── java
    │   │   └── org.itstack.demo
    │   │       └── ZuulApplication.java
    │   └── resources    
    │       └── application.yml
    └── test
         └── java
             └── org.itstack.demo.test
                 └── ApiTest.java
ZuulApplication.java | 啟動服務
/**
 * 微信公眾號:bugstack蟲洞棧 | 專注原創技術專題案例
 * 論壇:http://bugstack.cn
 * Create by 小傅哥 on @2019
 */
@SpringBootApplication
@EnableZuulProxy
@EnableEurekaClient
@EnableDiscoveryClient
public class ZuulApplication {

    public static void main(String[] args) {
        SpringApplication.run(ZuulApplication.class, args);
    }

}
application.yml | 服務配置{本案例是靜態路由,按需可以開發為動態路由}
server:
  port: 9191

spring:
  application:
    name: itstack-demo-ddd-zuul

eureka:
  client:
    serviceUrl:
      defaultZone: http://localhost:8989/eureka/
zuul:
  routes:
    api-a:
      path: /route-a/**
      serviceId: itstack-demo-ddd-feign

五、測試驗證

按照順序啟動;itstack-demo-ddd-eureka-server、itstack-demo-ddd-case{可以模擬啟動多個}、itstack-demo-ddd-feign、itstack-demo-ddd-zuul

訪問;http://localhost:8989/ | 服務中心
微信公眾號:bugstack蟲洞棧 | 服務中心

訪問:http://localhost:9191/route-a/api/queryTreeSummaryInfo?treeId=10001 | 通過閘道器路由呼叫DDD服務介面

微信公眾號:bugstack蟲洞棧 | 呼叫閘道器介面測試

六、綜上總結

  1. DDD的設計模式加上SpringBoot與SpringCloud非常適合開發微服務
  2. 以上案例可以進行擴充套件,使不同的使用者群體在閘道器介面呼叫時就打到不同的服務上
  3. 另外目前沒有使用dubbo型別的rpc框架,也就是沒有對外提供定義介面jar包,後續會進行延展

七、推薦閱讀

相關文章