為什么從 MVC 到 DDD,架構(gòu)的本質(zhì)是什么?
今天要分享的是 MVC 和 DDD 的架構(gòu)本質(zhì),通過由淺入深的介紹講解和視頻帶著手把手操作創(chuàng)建工程架構(gòu)。讓無論是學(xué)習(xí) MVC 的小白碼農(nóng)還是希望了解更多關(guān)于 DDD 內(nèi)容的老白碼農(nóng),都可以學(xué)習(xí)到一點(diǎn)自己需要的內(nèi)容。
一、MVC 架構(gòu)
如果我們嘗試把編程的復(fù)雜架構(gòu)縮小到最容易理解的程度,那么編程開發(fā)其實(shí)只做3件事:”定義屬性、創(chuàng)建方法、調(diào)用展示“。但因?yàn)橥愃璧膬?nèi)容較多,如一系列的屬性,一堆的方法實(shí)現(xiàn),一組的接口封裝,那么就需要合理的把這些內(nèi)容分配到不同的層次中去實(shí)現(xiàn),因此有了分層架構(gòu)的設(shè)計(jì)。
那么本文小傅哥會向大家介紹一套MVC架構(gòu)的分層設(shè)計(jì)以及如何創(chuàng)建使用,并提供相應(yīng)的簡單的案例。你可以復(fù)制這套架構(gòu)在自己的場景中使用,也更能方便編程的小白可以更快的上手開發(fā)。
注意:此套MVC架構(gòu)模型適合提供HTTP服務(wù)的工程架構(gòu),適合簡單的小場景開發(fā)使用。特點(diǎn);輕便、簡單、學(xué)習(xí)成本低。
1. 編程三步
如果說你是一個特別小的玩具項(xiàng)目,你甚至可以把編程的3步寫到一個類里。但因?yàn)槟阕龅氖钦?jīng)項(xiàng)目,你的各種類;對象類、庫表類、方法類,就會成群結(jié)隊(duì)的來。如果你想把這些成群結(jié)隊(duì)的類的內(nèi)容,都寫到一個類里去,那么就是幾萬行的代碼了。—— 當(dāng)然你也可以吹牛逼,你一個人做過一個項(xiàng)目,這項(xiàng)目大到啥程度呢。就是有一個類里有上萬行代碼。
圖片
所以,為了不至于讓一個類撐到爆??,需要把黃色的對象、綠色的方法、紅色的接口,都分配到不同的包結(jié)構(gòu)下。這就是你編碼人生中所接觸到的第一個解耦操作。
2. 分層框架
MVC 是一種非常常見且常用的分層架構(gòu),主要包括;M - mode 對象層,封裝到 domain 里。V - view 展示層,但因?yàn)槟壳岸际乔昂蠖朔蛛x的項(xiàng)目,幾乎不會在后端項(xiàng)目里寫 JSP 文件了。C - Controller 控制層,對外提供接口實(shí)現(xiàn)類。DAO 算是單獨(dú)拿出來用戶處理數(shù)據(jù)庫操作的層。
圖片
- 如圖,在 MVC 的分層架構(gòu)下。我們編程3步的所需各類對象、方法、接口,都分配到 MVC 的各個層次中去。
- 因?yàn)檫@樣分層以后,就可以很清晰明了的知道各個層都在做什么內(nèi)容,也更加方便后續(xù)的維護(hù)和迭代。
- 對于一個真正的項(xiàng)目來說,是沒有一錘子買賣的,最開始的開發(fā)遠(yuǎn)不是成本所在。最大的開發(fā)成本是后期的維護(hù)和迭代。而架構(gòu)設(shè)計(jì)的意義更多的就是在解決系統(tǒng)的反復(fù)的維護(hù)和迭代時,如何降低成本,這也是架構(gòu)分層的意義所在。
3. 調(diào)用流程
接下來我們再看下一套 MVC 架構(gòu)中各個模塊在調(diào)用時的串聯(lián)關(guān)系;
圖片
- 以用戶發(fā)起 HTTP 請求開始,Controller 在接收到請求后,調(diào)用由 Spring 注入到類里的 Service 方法,進(jìn)入 Service 方法后有些邏輯會走數(shù)據(jù)庫,有些邏輯是直接內(nèi)部自己處理后就直接返回給 Controller 了。最后由 Controller 封裝結(jié)果返回給 HTTP 響應(yīng)。
- 同時我們也可以看到各個對象在這些請求間的一個作用,如;請求對象、庫表對象、返回對象。
4. 架構(gòu)源碼
4.1 環(huán)境
- JDK 1.8
- Maven 3.8.6 - 下載安裝maven后,本地記得配置阿里云鏡像,方便快速拉取jar包。源碼中 docs/maven/settings.xml 有阿里云鏡像地址。
- SpringBoot 2.7.2
- MySQL 5.7 - 如果你使用 8.0 記得更改 pom.xml 中的 mysql 引用
4.2 架構(gòu)
- 源碼:https://gitcode.net/KnowledgePlanet/road-map/xfg-frame-mvc
- 樹形:安裝 brew install tree IntelliJ IDEA Terminal 使用 tree
.
├── docs
│ └── mvc.drawio - 架構(gòu)文檔
├── pom.xml
├── src
│ ├── main
│ │ ├── java
│ │ │ └── cn
│ │ │ └── bugstack
│ │ │ └── xfg
│ │ │ └── frame
│ │ │ ├── Application.java
│ │ │ ├── common
│ │ │ │ ├── Constants.java
│ │ │ │ └── Result.java
│ │ │ ├── controller
│ │ │ │ └── UserController.java
│ │ │ ├── dao
│ │ │ │ └── IUserDao.java
│ │ │ ├── domain
│ │ │ │ ├── po
│ │ │ │ │ └── User.java
│ │ │ │ ├── req
│ │ │ │ │ └── UserReq.java
│ │ │ │ ├── res
│ │ │ │ │ └── UserRes.java
│ │ │ │ └── vo
│ │ │ │ └── UserInfo.java
│ │ │ └── service
│ │ │ ├── IUserService.java
│ │ │ └── impl
│ │ │ └── UserServiceImpl.java
│ │ └── resources
│ │ ├── application.yml
│ │ └── mybatis
│ │ ├── config
│ │ │ └── mybatis-config.xml
│ │ └── mapper
│ │ └── User_Mapper.xml
│ └── test
│ └── java
│ └── cn
│ └── bugstack
│ └── xfg
│ └── frame
│ └── test
│ └── ApiTest.java
└── road-map.sql
以上是整個??工程架構(gòu)的 tree 樹形圖。整個工程由 SpringBoot 驅(qū)動。
- Application.java 是啟動程序的 SpringBoot 應(yīng)用
- common 是額外添加的一個層,用于定義通用的類
- controller 控制層,提供接口實(shí)現(xiàn)。
- dao 數(shù)據(jù)庫操作層
- domain 對象定義層
- service 服務(wù)實(shí)現(xiàn)層
5. 測試驗(yàn)證
- 首先;整個工程由 SpringBoot 驅(qū)動,提供了 road-map.sql 測試 SQL 庫表語句。你可以在自己的本地mysql上進(jìn)行執(zhí)行。它會創(chuàng)建庫表。
- 之后;在 application.yml 配置數(shù)據(jù)庫鏈接信息。
- 之后就可以打開 ApiTest 進(jìn)行測試了。你可以點(diǎn)擊 Application 類的綠色箭頭啟動工程,使用 UserController 類提供接口的方式調(diào)用程序;http://localhost:8089/queryUserInfo
圖片
- 如果你正常獲取了這樣的結(jié)果信息,那么說明你已經(jīng)啟動成功。接下來就可以對照著MVC的結(jié)構(gòu)進(jìn)行學(xué)習(xí),以及使用這樣的工程結(jié)構(gòu)開發(fā)自己的項(xiàng)目。
二、DDD 架構(gòu)
從最早接觸 DDD 架構(gòu),到后來用 DDD 架構(gòu)不斷的承接項(xiàng)目開發(fā),一次次在項(xiàng)目開發(fā)中的經(jīng)驗(yàn)積累。對 DDD 有了不少的理解。DDD 是一種思想,落地的形態(tài)和結(jié)構(gòu)會有不同的方式,甚至在編碼上也會有風(fēng)格的差異。但終期目標(biāo)就一個;”提供代碼的可維護(hù)性,降低迭代開發(fā)成本。“也是康威定律所述:”任何組織在設(shè)計(jì)一套系統(tǒng)時,所交付的設(shè)計(jì)方案在結(jié)構(gòu)上都與該組織的溝通結(jié)構(gòu)保持一致。“
但 DDD 與 MVC 相比的概率較多,貿(mào)然用理論驅(qū)動代碼開發(fā),會讓整個工程變得非常混亂,甚至可能雖然是用的 DDD 但最后寫出來了一片四不像的 MVC 代碼。所以對于程序員??????來說,先能上手一個工程,在從工程了解理論會更加容易。為此小傅哥想以此文,通過實(shí)戰(zhàn)編碼的方式向大家分享 DDD 架構(gòu),并能讓大家上手的 DDD 架構(gòu)。
1. 問題碰撞
你用 MVC 寫代碼,遇到過最大的問題是什么???
簡單、容易、好理解,是 MVC 架構(gòu)的特點(diǎn),但也正因?yàn)楹唵蔚姆謱舆壿嫞谶m配較復(fù)雜的場景并且需要長周期的維護(hù)時,代碼的迭代成本就會越來越高。如圖;
圖片
- 如果你接觸過較大型且已經(jīng)長期維護(hù)項(xiàng)目的 MVC 架構(gòu),你就會發(fā)現(xiàn)這里的 DAO、PO、VO 對象,在 Service 層相互調(diào)用。那么長期開發(fā)后,就導(dǎo)致了各個 PO 里的屬性字段數(shù)量都被撐的特別大。這樣的開發(fā)方式,將”狀態(tài)”、“行為“分離到不同的對象中,代碼的意圖漸漸模糊,膨脹、臃腫和不穩(wěn)定的架構(gòu),讓迭代成本增加。
- 而 DDD 架構(gòu)首先以解決此類問題為主,將各個屬于自己領(lǐng)域范圍內(nèi)的行為和邏輯封裝到自己的領(lǐng)域包下處理。這也是 DDD 架構(gòu)設(shè)計(jì)的精髓之一。它希望在分治層面合理切割問題空間為更小規(guī)模的若干子問題,而問題越小就容易被理解和處理,做到高內(nèi)聚低耦合。這也是康威定律所提到的,解決復(fù)雜場景的設(shè)計(jì)主要分為:分治、抽象和知識。
2. 簡化理解
在給大家講解 MVC 架構(gòu)的時候,小傅哥提到了一個簡單的開發(fā)模型。開發(fā)代碼可以理解為:“定義屬性 -> 創(chuàng)建方法 -> 調(diào)用展示” 但這個模型結(jié)構(gòu)過于簡單,不太適合運(yùn)用了各類分布式技術(shù)棧以及更多邏輯的 DDD 架構(gòu)。所以在 DDD 這里,我們把開發(fā)代碼可以抽象為:“觸發(fā) -> 函數(shù) -> 連接” 如圖;
圖片
- DDD 架構(gòu)常用于微服務(wù)場景,因此也一個系統(tǒng)的調(diào)用方式就不只是 HTTP 還包括;RPC 遠(yuǎn)程、MQ 消息、TASK 任務(wù),因此這些種方式都可以理解為觸發(fā)。
- 通過觸發(fā)調(diào)用函數(shù)方法,我們這里可以把各個服務(wù)都當(dāng)成一個函數(shù)方法來看。而函數(shù)方法通過連接,調(diào)用到其他的接口、數(shù)據(jù)庫、緩存來完成函數(shù)邏輯。
接下來,小傅哥在帶著大家把這些所需的模塊,拆分到對應(yīng)的DDD系統(tǒng)架構(gòu)中。
3. 架構(gòu)分層
如下是 DDD 架構(gòu)的一種分層結(jié)構(gòu),也可以有其他種方式,核心的重點(diǎn)在于適合你所在場景的業(yè)務(wù)開發(fā)。以下的分層結(jié)構(gòu),是小傅哥在使用 DDD 架構(gòu)多種的方式開發(fā)代碼后,做了簡化和處理的。右側(cè)的連線是各個模塊的依賴關(guān)系。接下來小傅哥就給大家做一下模塊的介紹。
圖片
- 接口定義 - xfg-frame-api:因?yàn)槲⒎?wù)中引用的 RPC 需要對外提供接口的描述信息,也就是調(diào)用方在使用的時候,需要引入 Jar 包,讓調(diào)用方好能依賴接口的定義做代理。
- 應(yīng)用封裝 - xfg-frame-app:這是應(yīng)用啟動和配置的一層,如一些 aop 切面或者 config 配置,以及打包鏡像都是在這一層處理。你可以把它理解為專門為了啟動服務(wù)而存在的。
- 領(lǐng)域封裝 - xfg-frame-domain:領(lǐng)域模型服務(wù),是一個非常重要的模塊。無論怎么做DDD的分層架構(gòu),domain 都是肯定存在的。在一層中會有一個個細(xì)分的領(lǐng)域服務(wù),在每個服務(wù)包中會有【模型、倉庫、服務(wù)】這樣3部分。
- 倉儲服務(wù) - xfg-frame-infrastructure:基礎(chǔ)層依賴于 domain 領(lǐng)域?qū)樱驗(yàn)樵?domain 層定義了倉儲接口需要在基礎(chǔ)層實(shí)現(xiàn)。這是依賴倒置的一種設(shè)計(jì)方式。
- 領(lǐng)域封裝 - xfg-frame-trigger:觸發(fā)器層,一般也被叫做 adapter 適配器層。用于提供接口實(shí)現(xiàn)、消息接收、任務(wù)執(zhí)行等。所以對于這樣的操作,小傅哥把它叫做觸發(fā)器層。
- 類型定義 - xfg-frame-types:通用類型定義層,在我們的系統(tǒng)開發(fā)中,會有很多類型的定義,包括;基本的 Response、Constants 和枚舉。它會被其他的層進(jìn)行引用使用。
- 領(lǐng)域編排【可選】 - xfg-frame-case:領(lǐng)域編排層,一般對于較大且復(fù)雜的的項(xiàng)目,為了更好的防腐和提供通用的服務(wù),一般會添加 case/application 層,用于對 domain 領(lǐng)域的邏輯進(jìn)行封裝組合處理。
4. 架構(gòu)源碼
4.1 環(huán)境
- JDK 1.8
- Maven 3.8.6
- SpringBoot 2.7.2
- MySQL 5.7 - 如果你使用 8.0 記得更改 pom.xml 中的 mysql 引用
4.2 架構(gòu)
- 源碼:https://gitcode.net/KnowledgePlanet/road-map/xfg-frame-ddd
- 樹形:安裝 brew install tree IntelliJ IDEA Terminal 使用 tree
.
├── README.md
├── docs
│ ├── dev-ops
│ │ ├── environment
│ │ │ └── environment-docker-compose.yml
│ │ ├── siege.sh
│ │ └── skywalking
│ │ └── skywalking-docker-compose.yml
│ ├── doc.md
│ ├── sql
│ │ └── road-map.sql
│ └── xfg-frame-ddd.drawio
├── pom.xml
├── xfg-frame-api
│ ├── pom.xml
│ ├── src
│ │ └── main
│ │ └── java
│ │ └── cn
│ │ └── bugstack
│ │ └── xfg
│ │ └── frame
│ │ └── api
│ │ ├── IAccountService.java
│ │ ├── IRuleService.java
│ │ ├── model
│ │ │ ├── request
│ │ │ │ └── DecisionMatterRequest.java
│ │ │ └── response
│ │ │ └── DecisionMatterResponse.java
│ │ └── package-info.java
│ └── xfg-frame-api.iml
├── xfg-frame-app
│ ├── Dockerfile
│ ├── build.sh
│ ├── pom.xml
│ ├── src
│ │ ├── main
│ │ │ ├── bin
│ │ │ │ ├── start.sh
│ │ │ │ └── stop.sh
│ │ │ ├── java
│ │ │ │ └── cn
│ │ │ │ └── bugstack
│ │ │ │ └── xfg
│ │ │ │ └── frame
│ │ │ │ ├── Application.java
│ │ │ │ ├── aop
│ │ │ │ │ ├── RateLimiterAop.java
│ │ │ │ │ └── package-info.java
│ │ │ │ └── config
│ │ │ │ ├── RateLimiterAopConfig.java
│ │ │ │ ├── RateLimiterAopConfigProperties.java
│ │ │ │ ├── ThreadPoolConfig.java
│ │ │ │ ├── ThreadPoolConfigProperties.java
│ │ │ │ └── package-info.java
│ │ │ └── resources
│ │ │ ├── application-dev.yml
│ │ │ ├── application-prod.yml
│ │ │ ├── application-test.yml
│ │ │ ├── application.yml
│ │ │ ├── logback-spring.xml
│ │ │ └── mybatis
│ │ │ ├── config
│ │ │ │ └── mybatis-config.xml
│ │ │ └── mapper
│ │ │ ├── RuleTreeNodeLine_Mapper.xml
│ │ │ ├── RuleTreeNode_Mapper.xml
│ │ │ └── RuleTree_Mapper.xml
│ │ └── test
│ │ └── java
│ │ └── cn
│ │ └── bugstack
│ │ └── xfg
│ │ └── frame
│ │ └── test
│ │ └── ApiTest.java
│ └── xfg-frame-app.iml
├── xfg-frame-ddd.iml
├── xfg-frame-domain
│ ├── pom.xml
│ ├── src
│ │ └── main
│ │ └── java
│ │ └── cn
│ │ └── bugstack
│ │ └── xfg
│ │ └── frame
│ │ └── domain
│ │ ├── order
│ │ │ ├── model
│ │ │ │ ├── aggregates
│ │ │ │ │ └── OrderAggregate.java
│ │ │ │ ├── entity
│ │ │ │ │ ├── OrderItemEntity.java
│ │ │ │ │ └── ProductEntity.java
│ │ │ │ ├── package-info.java
│ │ │ │ └── valobj
│ │ │ │ ├── OrderIdVO.java
│ │ │ │ ├── ProductDescriptionVO.java
│ │ │ │ └── ProductNameVO.java
│ │ │ ├── repository
│ │ │ │ ├── IOrderRepository.java
│ │ │ │ └── package-info.java
│ │ │ └── service
│ │ │ ├── OrderService.java
│ │ │ └── package-info.java
│ │ ├── rule
│ │ │ ├── model
│ │ │ │ ├── aggregates
│ │ │ │ │ └── TreeRuleAggregate.java
│ │ │ │ ├── entity
│ │ │ │ │ ├── DecisionMatterEntity.java
│ │ │ │ │ └── EngineResultEntity.java
│ │ │ │ ├── package-info.java
│ │ │ │ └── valobj
│ │ │ │ ├── TreeNodeLineVO.java
│ │ │ │ ├── TreeNodeVO.java
│ │ │ │ └── TreeRootVO.java
│ │ │ ├── repository
│ │ │ │ ├── IRuleRepository.java
│ │ │ │ └── package-info.java
│ │ │ └── service
│ │ │ ├── engine
│ │ │ │ ├── EngineBase.java
│ │ │ │ ├── EngineConfig.java
│ │ │ │ ├── EngineFilter.java
│ │ │ │ └── impl
│ │ │ │ └── RuleEngineHandle.java
│ │ │ ├── logic
│ │ │ │ ├── BaseLogic.java
│ │ │ │ ├── LogicFilter.java
│ │ │ │ └── impl
│ │ │ │ ├── UserAgeFilter.java
│ │ │ │ └── UserGenderFilter.java
│ │ │ └── package-info.java
│ │ └── user
│ │ ├── model
│ │ │ └── valobj
│ │ │ └── UserVO.java
│ │ ├── repository
│ │ │ └── IUserRepository.java
│ │ └── service
│ │ ├── UserService.java
│ │ └── impl
│ │ └── UserServiceImpl.java
│ └── xfg-frame-domain.iml
├── xfg-frame-infrastructure
│ ├── pom.xml
│ ├── src
│ │ └── main
│ │ └── java
│ │ └── cn
│ │ └── bugstack
│ │ └── xfg
│ │ └── frame
│ │ └── infrastructure
│ │ ├── dao
│ │ │ ├── IUserDao.java
│ │ │ ├── RuleTreeDao.java
│ │ │ ├── RuleTreeNodeDao.java
│ │ │ └── RuleTreeNodeLineDao.java
│ │ ├── package-info.java
│ │ ├── po
│ │ │ ├── RuleTreeNodeLineVO.java
│ │ │ ├── RuleTreeNodeVO.java
│ │ │ ├── RuleTreeVO.java
│ │ │ └── UserPO.java
│ │ └── repository
│ │ ├── RuleRepository.java
│ │ └── UserRepository.java
│ └── xfg-frame-infrastructure.iml
├── xfg-frame-trigger
│ ├── pom.xml
│ ├── src
│ │ └── main
│ │ └── java
│ │ └── cn
│ │ └── bugstack
│ │ └── xfg
│ │ └── frame
│ │ └── trigger
│ │ ├── http
│ │ │ ├── Controller.java
│ │ │ └── package-info.java
│ │ ├── mq
│ │ │ └── package-info.java
│ │ ├── rpc
│ │ │ ├── AccountService.java
│ │ │ ├── RuleService.java
│ │ │ └── package-info.java
│ │ └── task
│ │ └── package-info.java
│ └── xfg-frame-trigger.iml
└── xfg-frame-types
├── pom.xml
├── src
│ └── main
│ └── java
│ └── cn
│ └── bugstack
│ └── xfg
│ └── frame
│ └── types
│ ├── Constants.java
│ ├── Response.java
│ └── package-info.java
└── xfg-frame-types.iml
以上是整個??工程架構(gòu)的 tree 樹形圖。整個工程由 xfg-frame-app 模的 SpringBoot 驅(qū)動。這里小傅哥在 domain 領(lǐng)域模型下提供了 order、rule、user 三個領(lǐng)域模塊。并在每個模塊下提供了對應(yīng)的測試內(nèi)容。這塊是整個模型的重點(diǎn),其他模塊都可以通過測試看到這里的調(diào)用過程。
4.3 領(lǐng)域
一個領(lǐng)域模型中包含3個部分;model、repository、service 三部分;
- model 對象的定義
- repository 倉儲的定義
- service 服務(wù)實(shí)現(xiàn)
以上3個模塊,一般也是大家在使用 DDD 時候最不容易理解的分層。比如 model 里還分為;valobj - 值對象、entity 實(shí)體對象、aggregates 聚合對象;
- 值對象:表示沒有唯一標(biāo)識的業(yè)務(wù)實(shí)體,例如商品的名稱、描述、價格等。
- 實(shí)體對象:表示具有唯一標(biāo)識的業(yè)務(wù)實(shí)體,例如訂單、商品、用戶等;
- 聚合對象:是一組相關(guān)的實(shí)體對象的根,用于保證實(shí)體對象之間的一致性和完整性;
關(guān)于model中各個對象的拆分,尤其是聚合的定義,會牽引著整個模型的設(shè)計(jì)。當(dāng)然你可以在初期使用 DDD 的時候不用過分在意領(lǐng)域模型的設(shè)計(jì),可以把整個 domain 下的一個個包當(dāng)做充血模型結(jié)構(gòu),這樣編寫出來的代碼也是非常適合維護(hù)的。
4.4 環(huán)境(開發(fā)/測試/上線)
源碼:xfg-frame-ddd/pom.xml
<profile>
<id>dev</id>
<activation>
<activeByDefault>true</activeByDefault>
</activation>
<properties>
<profileActive>dev</profileActive>
</properties>
</profile>
<profile>
<id>test</id>
<properties>
<profileActive>test</profileActive>
</properties>
</profile>
<profile>
<id>prod</id>
<properties>
<profileActive>prod</profileActive>
</properties>
</profile>
- 定義環(huán)境;開發(fā)、測試、上線。
源碼:xfg-frame-app/application.yml
spring:
config:
name: xfg-frame
profiles:
active: dev # dev、test、prod
- 除了 pom 的配置,還需要在 application.yml 中指定環(huán)境。這樣就可以對應(yīng)的加載到;application-dev.yml、application-prod.yml、application-test.yml 這樣就可以很方便的加載對應(yīng)的配置信息了。尤其是各個場景中切換會更加方便。
4.5 切面
一個工程開發(fā)中,有時候可能會有很多的統(tǒng)一切面和啟動配置的處理,這些內(nèi)容都可以在 xfg-frame-app 完成。
圖片
源碼:cn.bugstack.xfg.frame.aop.RateLimiterAop
@Slf4j
@Aspect
public class RateLimiterAop {
private final long timeout;
private final double permitsPerSecond;
private final RateLimiter limiter;
public RateLimiterAop(double permitsPerSecond, long timeout) {
this.permitsPerSecond = permitsPerSecond;
this.timeout = timeout;
this.limiter = RateLimiter.create(permitsPerSecond);
}
@Pointcut("execution(* cn.bugstack.xfg.frame.trigger..*.*(..))")
public void pointCut() {
}
@Around(value = "pointCut()", argNames = "jp")
public Object around(ProceedingJoinPoint jp) throws Throwable {
boolean tryAcquire = limiter.tryAcquire(timeout, TimeUnit.MILLISECONDS);
if (!tryAcquire) {
Method method = getMethod(jp);
log.warn("方法 {}.{} 請求已被限流,超過限流配置[{}/秒]", method.getDeclaringClass().getCanonicalName(), method.getName(), permitsPerSecond);
return Response.<Object>builder()
.code(Constants.ResponseCode.RATE_LIMITER.getCode())
.info(Constants.ResponseCode.RATE_LIMITER.getInfo())
.build();
}
return jp.proceed();
}
private Method getMethod(JoinPoint jp) throws NoSuchMethodException {
Signature sig = jp.getSignature();
MethodSignature methodSignature = (MethodSignature) sig;
return jp.getTarget().getClass().getMethod(methodSignature.getName(), methodSignature.getParameterTypes());
}
}
使用
# 限流配置
rate-limiter:
permits-per-second: 1
timeout: 5
- 這樣你所有的通用配置,又和業(yè)務(wù)沒有太大的關(guān)系的,就可以直接寫到這里了。—— 具體可以參考代碼。
5. 測試驗(yàn)證
- 首先;整個工程由 SpringBoot 驅(qū)動,提供了 road-map.sql 測試 SQL 庫表語句。你可以在自己的本地mysql上進(jìn)行執(zhí)行。它會創(chuàng)建庫表。
- 之后;在 application.yml 配置數(shù)據(jù)庫鏈接信息。
- 之后就可以打開 ApiTest 進(jìn)行測試了。你可以點(diǎn)擊 Application 類的綠色箭頭啟動工程,使用觸發(fā)器里的接口調(diào)用測試,或者單元測試RPC接口,小傅哥也提供了泛化調(diào)用的方式。
圖片
- 如果你正常獲取了這樣的結(jié)果信息,那么說明你已經(jīng)啟動成功。接下來就可以對照著DDD的結(jié)構(gòu)進(jìn)行學(xué)習(xí),以及使用這樣的工程結(jié)構(gòu)開發(fā)自己的項(xiàng)目。
三、實(shí)戰(zhàn) - DDD 項(xiàng)目
紙上得來終覺淺,碼農(nóng)學(xué)習(xí)要實(shí)戰(zhàn)!
無論是 MVC 還是各類 DDD 所呈現(xiàn)的架構(gòu),還是需要看到實(shí)際的代碼,以及參與實(shí)戰(zhàn)開發(fā)才能更好的吸收。否則都是理論仍舊難以讓人下手。
所以小傅哥為大家準(zhǔn)備了一些學(xué)習(xí)項(xiàng)目,這些項(xiàng)目都是非常具有架構(gòu)思維以及設(shè)計(jì)模式的應(yīng)用級實(shí)戰(zhàn)項(xiàng)目架構(gòu)設(shè)計(jì)和落地。對于一些小白來說,如果能早早的接觸到這樣的項(xiàng)目,就相當(dāng)于是提前進(jìn)入企業(yè)實(shí)習(xí)了。可以極大的提到編程思維以及開發(fā)能力。
這些項(xiàng)目包括:《Lottery 抽獎系統(tǒng) - 基于領(lǐng)域驅(qū)動設(shè)計(jì)的四層架構(gòu)實(shí)踐》、《API網(wǎng)關(guān):中間件設(shè)計(jì)和落地》、《ChatGPT 微服務(wù)應(yīng)用體系搭建》、《IM 仿微信》、《SpringBoot Starter 中間件設(shè)計(jì)和落地》等。這里小傅哥只列3張圖,你就知道有多牛皮了!
第1張:Lottery
架構(gòu)
圖片
工程
圖片
第2張:API網(wǎng)關(guān)
架構(gòu)
圖片
工程
圖片
第3張:ChatGPT