作者:小傅哥,博客:https://bugstack.cn
大家好,我是技術(shù)UP主小傅哥。
凡是做到架構(gòu)師崗位的,都是具有一定技術(shù)思維敏感性的,不會主觀評價(jià)好和壞,但能推演出業(yè)務(wù)與技術(shù)的迭代發(fā)展被動熵增與減熵增的意識。就像拿MVC與DDD對比,也能確切的感受到,在架構(gòu)方面對比于單體應(yīng)用的分布式架構(gòu),是要額外引入非常多的技術(shù)棧使用。但這些模塊在 MVC 下并沒有設(shè)計(jì)好如何"安置"他們,就像一個(gè)老城區(qū)里突然引入了很多外來人口,不知道怎么安置一樣。不過 DDD 就像雄安新區(qū)做了整體規(guī)劃!
什么是系統(tǒng)的工程結(jié)構(gòu),工程框架的作用是什么?
其實(shí),工程結(jié)構(gòu)的存在作用目的,是為了承載工程系統(tǒng)開發(fā)的模型劃分,定義工程服務(wù)開發(fā)過程中實(shí)施標(biāo)準(zhǔn)。說白了,就是你在工程實(shí)現(xiàn)時(shí),在哪個(gè)層訪問數(shù)據(jù)庫、哪個(gè)層使用緩存、哪個(gè)層調(diào)用外部接口、哪個(gè)層做功能實(shí)現(xiàn),這就是工程框架結(jié)構(gòu)定義的目的。
但在 Service + 貧血模型
的三層結(jié)構(gòu)開發(fā)指導(dǎo)下,是沒有細(xì)分出面向?qū)ο蠊こ探Y(jié)構(gòu)設(shè)計(jì)的趨于劃分的。所以在通常意義的 MVC 下,開發(fā)過程所有需要的內(nèi)容,都會堆砌到 Service 實(shí)現(xiàn)類中。這也是為什么 DDD 領(lǐng)域驅(qū)動設(shè)計(jì)的落地工程結(jié)構(gòu),會出現(xiàn);洋蔥架構(gòu)、整潔架構(gòu)、菱形架構(gòu)、六邊形架構(gòu)等這些架構(gòu)模型。因?yàn)槲覀冃枰?xì)致的劃分,來承載 DDD 設(shè)計(jì)概念中映射的領(lǐng)域、倉儲、適配、編排、觸發(fā),并重視面向?qū)ο筮^程?!?其實(shí)你一上學(xué),學(xué)Java就開始學(xué)面向?qū)ο罅?,只不過一點(diǎn)點(diǎn)在忘記它。
本節(jié)是DDD概率系列的第3節(jié),講解 DDD 架構(gòu)設(shè)計(jì),在 bugstack.cn 路書中有完整系列內(nèi)容,可以查閱。
- DDD 是什么?—— 你以前只會用 Service + 貧血模型!DDD 建模 —— 架構(gòu)師總說的風(fēng)暴模型是什么?DDD 實(shí)戰(zhàn)項(xiàng)目 - 大營銷平臺系統(tǒng)DDD 實(shí)戰(zhàn)項(xiàng)目 - OpenAI 應(yīng)用系統(tǒng)(含支付)
一、為啥需要架構(gòu)
說到開發(fā)代碼為啥需要架構(gòu),就想買了個(gè)房子,為啥要隔出廚房、客廳、臥室、衛(wèi)生間一樣,核心目的就是讓痛不痛的職責(zé)分配到不同的區(qū)域內(nèi)。雖然在代碼中沒有沒有馬桶要放衛(wèi)生間、沙發(fā)要放客廳、床要放臥室。但他有一些列的科目信息要引入到工程。
在工程開發(fā)時(shí)會涉及到的核心科目;
如;統(tǒng)一的異常、數(shù)據(jù)庫的連接、日志的打印、外部服務(wù)的調(diào)用、消息的監(jiān)聽、任務(wù)的輪訓(xùn)以及服務(wù)的實(shí)現(xiàn)等一些列的東西要處理,分配到不同的工程包下承載。在 DDD 之前,我們一直用 MVC 的分層結(jié)構(gòu)承接這些內(nèi)容;
通用的、配置的、組件的、持久化的、內(nèi)部的、外部的,在以往的單體應(yīng)用時(shí)代開發(fā)下,其實(shí)是沒有這么多東西的,那時(shí)候的工程結(jié)構(gòu)都偏向于 Service + 貧血模型實(shí)現(xiàn)。
但隨著微服務(wù)的演進(jìn),越來越多的內(nèi)容被填充到工程中,這個(gè)時(shí)候你細(xì)心的查看架構(gòu),就會發(fā)現(xiàn)原本的 MVC 結(jié)構(gòu)其實(shí)已經(jīng)變的非?;靵y了。一個(gè) Service 中為了實(shí)現(xiàn)自己的功能,要引入一堆的東西,這些原子的功能與 Service 自身的服務(wù)耦合在一塊。也導(dǎo)致了工程的維護(hù)成本越來越大。
這樣的三層工程結(jié)構(gòu)分配方式,對于要承載龐大的分布式技術(shù)棧體系顯然是有點(diǎn)小馬拉大車,三缸機(jī)帶不動SUV一樣。
二、工程結(jié)構(gòu)設(shè)計(jì)
2004年,Eric Evans 在發(fā)表了一部名為《Domain Driven Design》的著作。2005年 Alistair Cockburn 提出的“六邊形關(guān)系圖”理論,2008年 Jeffrey Palermo 提出了洋蔥架構(gòu)。雖然這些架構(gòu)并不是專門為 DDD 而出,但巧的是這些架構(gòu)都在 DDD 一書發(fā)表之后陸續(xù)推出新的架構(gòu)模型。同時(shí)這些架構(gòu)的分層設(shè)計(jì)方式也都與 DDD 非常契合,在這些架構(gòu)下也可以很好的落地 DDD 設(shè)計(jì)方法。
無論是六邊形架構(gòu),還是洋蔥架構(gòu),或是[張毅老師](http://zhangyi.xyz/)提到的南向網(wǎng)關(guān)/北向網(wǎng)關(guān)的菱形架構(gòu),他們的目標(biāo)都是以領(lǐng)域服務(wù)為核心,隔離內(nèi)部實(shí)現(xiàn)與外部資源的耦合。
在 DDD 分層架構(gòu)下,以支撐 domain 核心領(lǐng)域?qū)崿F(xiàn)拆分出基礎(chǔ)設(shè)施(infrastructure),來承接對外部資源的調(diào)用。觸發(fā)器(trigger)向外部提供服務(wù)。之后 app 為應(yīng)用啟動、api 為接口定義、types 為通用信息、case 為編排。
在這樣一套結(jié)構(gòu)下,用于開發(fā)工程的各項(xiàng)科目也可以被優(yōu)雅的分配到各個(gè)分層結(jié)構(gòu)了。相對于 Service + 數(shù)據(jù)模型的貧血模型結(jié)構(gòu),現(xiàn)在就主要以 domain 為核心開發(fā)業(yè)務(wù)功能,不會在 domain 工程模塊下,引入其他各類外部組件了,這樣就可以更加關(guān)心業(yè)務(wù)功能開發(fā)。
之后是這樣的思想映射到工程中,常見的分層結(jié)構(gòu)會有兩套,一套是整潔分層,另外一套是六邊形分層。
1. 整潔架構(gòu) - 工程結(jié)構(gòu)
整潔架構(gòu)的分包形式,會將所有的外部依賴使用和工程內(nèi)要對外的,統(tǒng)一定義到適配器層。這里可以理解為對適配和對內(nèi)適配。
2. 六邊形架構(gòu) - 工程結(jié)構(gòu)
六邊形架構(gòu),會把本身提供到外部的放到trigger,讓接口調(diào)用、消息監(jiān)聽、任務(wù)調(diào)度,都可以統(tǒng)一一個(gè)入口處理。而對于外部同的能力統(tǒng)一放到 infrastructure 基礎(chǔ)設(shè)施層,包括;數(shù)據(jù)庫、緩存、配置、調(diào)用其他方的接口。
三、領(lǐng)域模型設(shè)計(jì)
雖然大家用的都是 DDD,也都有對應(yīng)的模塊和分包,但在細(xì)節(jié)之處還是會有一些差異。就像家里的家庭成員,衣服、褲子、鞋子,是所有人的衣服都放一起,還是每個(gè)人有獨(dú)立的衣柜只放自己的。這塊是有差異的。另外這東西沒有絕對的好和壞,就像廚房里的碗筷是是放一起的,衛(wèi)生間的馬桶也是共用的,這說明分包也是需要按照最符合自己所需來設(shè)定。
1. 分包方式
如下,是兩種分包方式;
- 方式1;DDD 領(lǐng)域科目類型分包,類型之下寫每個(gè)業(yè)務(wù)邏輯。方式2;業(yè)務(wù)領(lǐng)域分包,每個(gè)業(yè)務(wù)領(lǐng)域之下有自己所需的 DDD 領(lǐng)域科目。
比如,你現(xiàn)在一個(gè)工程下有用戶、積分、抽獎、支付,(緊湊的聚合類微服務(wù)有時(shí)候更易于維護(hù)),那么這些包一種是分為獨(dú)立的業(yè)務(wù)包方式2這種,另外一種就是大家都在一個(gè)壇子里吃飯,要啥去各個(gè)地方找。所以你更傾向于那種呢?
2. 領(lǐng)域模型
DDD 領(lǐng)域驅(qū)動設(shè)計(jì)的中心,主要在于領(lǐng)域模型的設(shè)計(jì),以領(lǐng)域所需驅(qū)動功能實(shí)現(xiàn)和數(shù)據(jù)建模。一個(gè)領(lǐng)域服務(wù)下面會有多個(gè)領(lǐng)域模型,每個(gè)領(lǐng)域模型都是一個(gè)充血結(jié)構(gòu)。一個(gè)領(lǐng)域模型 = 一個(gè)充血結(jié)構(gòu)
-
- model 模型對象;
-
- aggreate:聚合對象,實(shí)體對象、值對象的協(xié)同組織,就是聚合對象。entity:實(shí)體對象,大多數(shù)情況下,實(shí)體對象(Entity)與數(shù)據(jù)庫持久化對象(PO)是1v1的關(guān)系,但也有為了封裝一些屬性信息,會出現(xiàn)1vn的關(guān)系。valobj:值對象,通過對象屬性值來識別的對象 By 《實(shí)現(xiàn)領(lǐng)域驅(qū)動設(shè)計(jì)》
-
repository 倉儲服務(wù);從數(shù)據(jù)庫等數(shù)據(jù)源中獲取數(shù)據(jù),傳遞的對象可以是聚合對象、實(shí)體對象,返回的結(jié)果可以是;實(shí)體對象、值對象。因?yàn)閭}儲服務(wù)是由基礎(chǔ)層(infrastructure) 引用領(lǐng)域?qū)?domain),是一種依賴倒置的結(jié)構(gòu),但它可以天然的隔離PO數(shù)據(jù)庫持久化對象被引用。service 服務(wù)設(shè)計(jì);這里要注意,不要以為定義了聚合對象,就把超越1個(gè)對象以外的邏輯,都封裝到聚合中,這會讓你的代碼后期越來越難維護(hù)。聚合更應(yīng)該注重的是和本對象相關(guān)的單一簡單封裝場景,而把一些重核心業(yè)務(wù)方到 service 里實(shí)現(xiàn)。此外;如果你的設(shè)計(jì)模式應(yīng)用不佳,那么無論是領(lǐng)域驅(qū)動設(shè)計(jì)、測試驅(qū)動設(shè)計(jì)還是換了三層和四層架構(gòu),你的工程質(zhì)量依然會非常差。對象解釋
-
- DTO 數(shù)據(jù)傳輸對象 (data transfer object),DAO與業(yè)務(wù)對象或數(shù)據(jù)訪問對象的區(qū)別是:DTO的數(shù)據(jù)的變異子與訪問子(mutator和accessor)、語法分析(parser)、序列化(serializer)時(shí)不會有任何存儲、獲取、序列化和反序列化的異常。即DTO是簡單對象,不含任何業(yè)務(wù)邏輯,但可包含序列化和反序列化以用于傳輸數(shù)據(jù)。
四、分層調(diào)用鏈路
接下來我們把 DDD 的分層架構(gòu)平鋪展開,看看從一個(gè)接口的實(shí)現(xiàn)到各個(gè)模塊分層中的調(diào)用鏈路關(guān)系是什么樣的。這樣在做自己的代碼開發(fā)中也可以參考到應(yīng)該把什么的功能分配到哪個(gè)模塊中處理。
從APP層、觸發(fā)器層、應(yīng)用層,這三塊主要對領(lǐng)域?qū)拥纳舷挛倪壿嫹庋b、觸發(fā)式(MQ、HTTP、JOB)使用,并最終在應(yīng)用層中打包發(fā)布上線。這一部分的都是使用的處理,所以也不會有太復(fù)雜的操作。
當(dāng)進(jìn)入領(lǐng)域?qū)娱_始,也是智力集中體現(xiàn)的開始了。所有你對工程的抽象能力,都在這一塊區(qū)域體現(xiàn)。
五、工程架構(gòu)案例
1. 環(huán)境
- JDK 1.8Maven 3.8.6SpringBoot 2.7.2MySQL 5.7 - 如果你使用 8.0 記得更改 pom.xml 中的 mysql 引用Dubbo - https://cn.dubbo.apache.org/zh-cn/overview/mannual/java-sdk/reference-manual/registry/multicast/ 文檔&廣播模式地址說明
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
│???│???????????????????????????????│???├──?RuleTreeNodeLinePO.java
│???│???????????????????????????????│???├──?RuleTreeNodePO.java
│???│???????????????????????????????│???├──?RuleTreePO.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è)工程架構(gòu)的 tree 樹形圖。整個(gè)工程由 ?xfg-frame-app 模的 SpringBoot 驅(qū)動。這里小傅哥在 domain 領(lǐng)域模型下提供了 order、rule、user 三個(gè)領(lǐng)域模塊。并在每個(gè)模塊下提供了對應(yīng)的測試內(nèi)容。這塊是整個(gè)模型的重點(diǎn),其他模塊都可以通過測試看到這里的調(diào)用過程。
3. 領(lǐng)域
一個(gè)領(lǐng)域模型中包含3個(gè)部分;model、repository、service 三部分;
- model 對象的定義 【含有;valobj = VO、entity、Aggregate】repository 倉儲的定義【含有PO】service 服務(wù)實(shí)現(xiàn)
以上3個(gè)模塊,一般也是大家在使用 DDD 時(shí)候最不容易理解的分層。比如 model 里還分為;valobj - 值對象、entity 實(shí)體對象、aggregates 聚合對象;
- 值對象:表示沒有唯一標(biāo)識的業(yè)務(wù)實(shí)體,例如商品的名稱、描述、價(jià)格等。實(shí)體對象:表示具有唯一標(biāo)識的業(yè)務(wù)實(shí)體,例如訂單、商品、用戶等;聚合對象:是一組相關(guān)的實(shí)體對象的根,用于保證實(shí)體對象之間的一致性和完整性;
關(guān)于model中各個(gè)對象的拆分,尤其是聚合的定義,會牽引著整個(gè)模型的設(shè)計(jì)。當(dāng)然你可以在初期使用 DDD 的時(shí)候不用過分在意領(lǐng)域模型的設(shè)計(jì),可以把整個(gè) domain 下的一個(gè)個(gè)包當(dāng)做充血模型結(jié)構(gòu),這樣編寫出來的代碼也是非常適合維護(hù)的。
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)的配置信息了。尤其是各個(gè)場景中切換會更加方便。
5. 切面
一個(gè)工程開發(fā)中,有時(shí)候可能會有很多的統(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)系的,就可以直接寫到這里了?!?具體可以參考代碼。
六、工程測試驗(yàn)證
- 首先;整個(gè)工程由 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)目。
七、加入學(xué)習(xí)
星球「碼農(nóng)會鎖」?實(shí)戰(zhàn)項(xiàng)目中有非常多的運(yùn)用。還包括:DDD - 大營銷、OpenAI 應(yīng)用、API網(wǎng)關(guān)、Lottery抽獎、IM通信、SpringBoot Starter 組件開發(fā)、IDEA Plugin 插件開發(fā)、支付SDK、動態(tài)線程組件、透視業(yè)務(wù)監(jiān)控等,并還有開源項(xiàng)目學(xué)習(xí)。
如果大家希望通過做有價(jià)值的編程項(xiàng)目,提高自己的編程思維和編碼能力,可以加入小傅哥的【星球:碼農(nóng)會鎖】。加入后解鎖所有往期項(xiàng)目,還可以學(xué)習(xí)后續(xù)新開發(fā)的項(xiàng)目。
這樣成體系的全量項(xiàng)目學(xué)習(xí),放在一些平臺售賣,至少都要上千塊。但小傅哥的星球,只需要100多,就可以獲得大廠架構(gòu)師對你手把手教學(xué)!
星球「碼農(nóng)會鎖」
星球全程手把手指導(dǎo)教學(xué),遇到技術(shù)問題幫忙排查代碼。已經(jīng)有很多伙伴開始學(xué)起來了,還有大家交的作業(yè)筆記。有了的項(xiàng)目驅(qū)動學(xué)習(xí),清晰的目標(biāo)感,大家沖起來也有了更明確的方向!干干干?。。?/p>
在今年的面試中,星球幫助眾多伙伴拿到微信支付
、京東科技
、度小滿
、螞蟻金服
、Lazada(電商優(yōu)惠營銷)
、快手
、美團(tuán)到店
等Offer,還有的校招生薪資最高年包到45w!