掃二維碼與項(xiàng)目經(jīng)理溝通
我們在微信上24小時(shí)期待你的聲音
解答本文疑問/技術(shù)咨詢/運(yùn)營咨詢/技術(shù)建議/互聯(lián)網(wǎng)交流
今天要分享的是 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)容。

創(chuàng)新互聯(lián)專注于集賢網(wǎng)站建設(shè)服務(wù)及定制,我們擁有豐富的企業(yè)做網(wǎng)站經(jīng)驗(yàn)。 熱誠為您提供集賢營銷型網(wǎng)站建設(shè),集賢網(wǎng)站制作、集賢網(wǎng)頁設(shè)計(jì)、集賢網(wǎng)站官網(wǎng)定制、小程序設(shè)計(jì)服務(wù),打造集賢網(wǎng)絡(luò)公司原創(chuàng)品牌,更為您提供集賢網(wǎng)站排名全網(wǎng)營銷落地服務(wù)。
如果我們嘗試把編程的復(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ì)。
那么本文小傅哥會(huì)向大家介紹一套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í)成本低。
如果說你是一個(gè)特別小的玩具項(xiàng)目,你甚至可以把編程的3步寫到一個(gè)類里。但因?yàn)槟阕龅氖钦?jīng)項(xiàng)目,你的各種類;對象類、庫表類、方法類,就會(huì)成群結(jié)隊(duì)的來。如果你想把這些成群結(jié)隊(duì)的類的內(nèi)容,都寫到一個(gè)類里去,那么就是幾萬行的代碼了?!?當(dāng)然你也可以吹牛逼,你一個(gè)人做過一個(gè)項(xiàng)目,這項(xiàng)目大到啥程度呢。就是有一個(gè)類里有上萬行代碼。
圖片
所以,為了不至于讓一個(gè)類撐到爆,需要把黃色的對象、綠色的方法、紅色的接口,都分配到不同的包結(jié)構(gòu)下。這就是你編碼人生中所接觸到的第一個(gè)解耦操作。
MVC 是一種非常常見且常用的分層架構(gòu),主要包括;M - mode 對象層,封裝到 domain 里。V - view 展示層,但因?yàn)槟壳岸际乔昂蠖朔蛛x的項(xiàng)目,幾乎不會(huì)在后端項(xiàng)目里寫 JSP 文件了。C - Controller 控制層,對外提供接口實(shí)現(xiàn)類。DAO 算是單獨(dú)拿出來用戶處理數(shù)據(jù)庫操作的層。
圖片
接下來我們再看下一套 MVC 架構(gòu)中各個(gè)模塊在調(diào)用時(shí)的串聯(lián)關(guān)系;
圖片
.
├── 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è)工程架構(gòu)的 tree 樹形圖。整個(gè)工程由 SpringBoot 驅(qū)動(dòng)。
圖片
- 如果你正常獲取了這樣的結(jié)果信息,那么說明你已經(jīng)啟動(dòng)成功。接下來就可以對照著MVC的結(jié)構(gòu)進(jìn)行學(xué)習(xí),以及使用這樣的工程結(jié)構(gòu)開發(fā)自己的項(xiàng)目。
從最早接觸 DDD 架構(gòu),到后來用 DDD 架構(gòu)不斷的承接項(xiàng)目開發(fā),一次次在項(xiàng)目開發(fā)中的經(jīng)驗(yàn)積累。對 DDD 有了不少的理解。DDD 是一種思想,落地的形態(tài)和結(jié)構(gòu)會(huì)有不同的方式,甚至在編碼上也會(huì)有風(fēng)格的差異。但終期目標(biāo)就一個(gè);”提供代碼的可維護(hù)性,降低迭代開發(fā)成本?!耙彩强低伤觯骸比魏谓M織在設(shè)計(jì)一套系統(tǒng)時(shí),所交付的設(shè)計(jì)方案在結(jié)構(gòu)上都與該組織的溝通結(jié)構(gòu)保持一致。“
但 DDD 與 MVC 相比的概率較多,貿(mào)然用理論驅(qū)動(dòng)代碼開發(fā),會(huì)讓整個(gè)工程變得非?;靵y,甚至可能雖然是用的 DDD 但最后寫出來了一片四不像的 MVC 代碼。所以對于程序員來說,先能上手一個(gè)工程,在從工程了解理論會(huì)更加容易。為此小傅哥想以此文,通過實(shí)戰(zhàn)編碼的方式向大家分享 DDD 架構(gòu),并能讓大家上手的 DDD 架構(gòu)。
你用 MVC 寫代碼,遇到過最大的問題是什么?????
簡單、容易、好理解,是 MVC 架構(gòu)的特點(diǎn),但也正因?yàn)楹唵蔚姆謱舆壿?,在適配較復(fù)雜的場景并且需要長周期的維護(hù)時(shí),代碼的迭代成本就會(huì)越來越高。如圖;
圖片
在給大家講解 MVC 架構(gòu)的時(shí)候,小傅哥提到了一個(gè)簡單的開發(fā)模型。開發(fā)代碼可以理解為:“定義屬性 -> 創(chuàng)建方法 -> 調(diào)用展示” 但這個(gè)模型結(jié)構(gòu)過于簡單,不太適合運(yùn)用了各類分布式技術(shù)棧以及更多邏輯的 DDD 架構(gòu)。所以在 DDD 這里,我們把開發(fā)代碼可以抽象為:“觸發(fā) -> 函數(shù) -> 連接” 如圖;
圖片
接下來,小傅哥在帶著大家把這些所需的模塊,拆分到對應(yīng)的DDD系統(tǒng)架構(gòu)中。
如下是 DDD 架構(gòu)的一種分層結(jié)構(gòu),也可以有其他種方式,核心的重點(diǎn)在于適合你所在場景的業(yè)務(wù)開發(fā)。以下的分層結(jié)構(gòu),是小傅哥在使用 DDD 架構(gòu)多種的方式開發(fā)代碼后,做了簡化和處理的。右側(cè)的連線是各個(gè)模塊的依賴關(guān)系。接下來小傅哥就給大家做一下模塊的介紹。
圖片
.
├── 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è)工程架構(gòu)的 tree 樹形圖。整個(gè)工程由 xfg-frame-app 模的 SpringBoot 驅(qū)動(dòng)。這里小傅哥在 domain 領(lǐng)域模型下提供了 order、rule、user 三個(gè)領(lǐng)域模塊。并在每個(gè)模塊下提供了對應(yīng)的測試內(nèi)容。這塊是整個(gè)模型的重點(diǎn),其他模塊都可以通過測試看到這里的調(diào)用過程。
一個(gè)領(lǐng)域模型中包含3個(gè)部分;model、repository、service 三部分;
以上3個(gè)模塊,一般也是大家在使用 DDD 時(shí)候最不容易理解的分層。比如 model 里還分為;valobj - 值對象、entity 實(shí)體對象、aggregates 聚合對象;
關(guān)于model中各個(gè)對象的拆分,尤其是聚合的定義,會(huì)牽引著整個(gè)模型的設(shè)計(jì)。當(dāng)然你可以在初期使用 DDD 的時(shí)候不用過分在意領(lǐng)域模型的設(shè)計(jì),可以把整個(gè) domain 下的一個(gè)個(gè)包當(dāng)做充血模型結(jié)構(gòu),這樣編寫出來的代碼也是非常適合維護(hù)的。
源碼:xfg-frame-ddd/pom.xml
dev
true
dev
test
test
prod
prod
源碼:xfg-frame-app/application.yml
spring:
config:
name: xfg-frame
profiles:
active: dev # dev、test、prod
一個(gè)工程開發(fā)中,有時(shí)候可能會(huì)有很多的統(tǒng)一切面和啟動(dò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.使用
# 限流配置
rate-limiter:
permits-per-second: 1
timeout: 5
圖片
紙上得來終覺淺,碼農(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í)了??梢詷O大的提到編程思維以及開發(fā)能力。
這些項(xiàng)目包括:《Lottery 抽獎(jiǎng)系統(tǒng) - 基于領(lǐng)域驅(qū)動(dòng)設(shè)計(jì)的四層架構(gòu)實(shí)踐》、《API網(wǎng)關(guān):中間件設(shè)計(jì)和落地》、《ChatGPT 微服務(wù)應(yīng)用體系搭建》、《IM 仿微信》、《SpringBoot Starter 中間件設(shè)計(jì)和落地》等。這里小傅哥只列3張圖,你就知道有多牛皮了!
圖片
圖片
圖片
圖片
圖片

我們在微信上24小時(shí)期待你的聲音
解答本文疑問/技術(shù)咨詢/運(yùn)營咨詢/技術(shù)建議/互聯(lián)網(wǎng)交流