對(duì)領(lǐng)域驅(qū)動(dòng)設(shè)計(jì)中關(guān)鍵的一些概念,大家有了更為深入的認(rèn)識(shí)是不夠的,在具體實(shí)踐中我們還會(huì)面臨諸如代碼如何分層、不同上下文之間如何集成,以及某些時(shí)候還會(huì)用到CQRS。本文就來(lái)補(bǔ)齊領(lǐng)域驅(qū)動(dòng)設(shè)計(jì)中剩余的一些內(nèi)容,希望能夠助你更游刃有余地應(yīng)對(duì)開(kāi)發(fā)中遇到的各種問(wèn)題。
作者 | 于振
責(zé)編 | 韓楠
你好,今天我想與你聊聊DDD中的應(yīng)用架構(gòu)。在過(guò)往我分享的幾篇文章中,我們介紹了領(lǐng)域驅(qū)動(dòng)設(shè)計(jì)中的一些基本概念,這里,再做一個(gè)簡(jiǎn)單的回顧。
·《基礎(chǔ)問(wèn)題不簡(jiǎn)單|怎么合理使用值對(duì)象,讓你的代碼更清晰、更安全?》
·《不想只做Cruder?實(shí)體、聚合根,還不快去了解下》
·《如何通過(guò)倉(cāng)儲(chǔ),對(duì)實(shí)體進(jìn)行持久化處理?》
·《實(shí)體表達(dá)力不夠?那你應(yīng)該試試領(lǐng)域服務(wù)》
·《如何使用工廠,進(jìn)一步解耦領(lǐng)域?qū)ο蟮穆氊?zé)》
·《領(lǐng)域模型細(xì)節(jié)太多不便使用?那就加個(gè)應(yīng)用服務(wù)吧》
·《DDD在Go中如何落地|如何在業(yè)務(wù)中使用領(lǐng)域事件?》
使用值對(duì)象和實(shí)體幫助我們構(gòu)建了具有豐富行為的領(lǐng)域模型,實(shí)體創(chuàng)建出來(lái)后需要通過(guò)倉(cāng)儲(chǔ)進(jìn)行持久化,如果領(lǐng)域模型跟數(shù)據(jù)模型存在差異,就還需要通過(guò) Converter 進(jìn)行轉(zhuǎn)換,以及通過(guò) Snapshot 對(duì)實(shí)體進(jìn)行追蹤。
如果某些行為不適合放到某個(gè)實(shí)體上,就需要使用領(lǐng)域服務(wù),同時(shí),為了一定程度地防止領(lǐng)域服務(wù)的濫用,我們規(guī)定領(lǐng)域服務(wù)在命名上必須有一個(gè)動(dòng)詞。
為了解耦領(lǐng)域?qū)ο蟮膭?chuàng)建過(guò)程和其自身行為,我們又介紹了工廠方法。
對(duì)于外部用戶來(lái)說(shuō),領(lǐng)域之內(nèi)的各個(gè)對(duì)象描述的,都是細(xì)粒度的領(lǐng)域概念,為了方便外部調(diào)用,同時(shí)屏蔽領(lǐng)域?qū)ο蟮木唧w細(xì)節(jié),就又有了應(yīng)用服務(wù)。
最后,通過(guò)領(lǐng)域事件,進(jìn)一步解耦了不同上下文之間的依賴,即使在同一邊界之內(nèi)的不同的聚合根,也可以實(shí)現(xiàn)數(shù)據(jù)的最終一致性。
至此,大家應(yīng)該對(duì)領(lǐng)域驅(qū)動(dòng)設(shè)計(jì)中關(guān)鍵的一些概念,有了更為深入的認(rèn)識(shí)。但僅僅是這些應(yīng)該是還不夠的,在具體實(shí)踐中,我們還面臨著諸如代碼如何分層、不同上下文之間如何集成,以及某些時(shí)候還會(huì)用到CQRS。
在這篇文章中,我們就來(lái)補(bǔ)齊領(lǐng)域驅(qū)動(dòng)設(shè)計(jì)中剩余的一些內(nèi)容。
首先,我們從代碼的分層開(kāi)始說(shuō)起。
01? DDD的分層架構(gòu)
分層架構(gòu)作為一種歷史悠久的架構(gòu)模式,在很多的場(chǎng)景中都得到了應(yīng)用。
大家比較熟悉的應(yīng)該就是 MVC 對(duì)應(yīng)用三層架構(gòu)的拆分。MVC 這種分層是自上而下的。
隨著業(yè)務(wù)越來(lái)越復(fù)雜,人們逐漸發(fā)現(xiàn), MVC 架構(gòu)在應(yīng)對(duì)復(fù)雜的業(yè)務(wù)問(wèn)題時(shí)會(huì)顯得力不從心。
于是,后面逐漸演化出了六邊形架構(gòu)、洋蔥架構(gòu)、整潔架構(gòu)等架構(gòu)模式。這幾種架構(gòu)也是一種分層架構(gòu),但這種分層不是由上而下的,而是由內(nèi)而外的。
我們以洋蔥架構(gòu)為例:
可以看到,最關(guān)鍵的是中心的領(lǐng)域模型,它包括了所有的應(yīng)用邏輯與規(guī)則。在這一層中不會(huì)直接引用技術(shù)實(shí)現(xiàn),這樣就能夠確保在技術(shù)層面的改動(dòng)不會(huì)影響到領(lǐng)域核心。
在領(lǐng)域?qū)又庥职祟I(lǐng)域服務(wù)層、應(yīng)用服務(wù)層,而具體的技術(shù)實(shí)現(xiàn)則是被置于最外層的。
這種架構(gòu)的好處就在于,它屏蔽掉了應(yīng)用程序在UI層、DB層,以及各種中間件層的本質(zhì)區(qū)別,所有的這些外部資源都被抽象成了對(duì)系統(tǒng)的輸入輸出,然后我們就能夠以一致的方式來(lái)處理不同的請(qǐng)求類型,并且,在與實(shí)際運(yùn)行的設(shè)備和數(shù)據(jù)庫(kù)相隔離的情況下,也可以先行開(kāi)發(fā)和測(cè)試。
在 DDD 的技術(shù)實(shí)現(xiàn)中,就用到了這種分層方式。
下圖是 Eric Evans 在其經(jīng)典著作《領(lǐng)域驅(qū)動(dòng)設(shè)計(jì)》中給出的一個(gè)典型的 DDD 系統(tǒng)所采用的分層架構(gòu):
在上圖中可以看到,整個(gè)架構(gòu)劃分成了四個(gè)層,各層所表示的含義及其職責(zé)描述如下:
1、用戶接口層
這一層主要負(fù)責(zé)直接面向外部用戶或者系統(tǒng),接收外部輸入,并返回結(jié)果。
用戶接口層是比較輕的一層,不含業(yè)務(wù)邏輯。可以做一些簡(jiǎn)單的入?yún)⑿r?yàn),也可以記錄一下訪問(wèn)日志,對(duì)異常進(jìn)行統(tǒng)一的處理。同時(shí),對(duì)返回值的封裝也應(yīng)當(dāng)在這層完成。
2、應(yīng)用層
應(yīng)用層,通常是用戶接口層的直接使用者。
但是在應(yīng)用層中并不實(shí)現(xiàn)真正的業(yè)務(wù)規(guī)則,而是根據(jù)實(shí)際的 use case 來(lái)協(xié)調(diào)領(lǐng)域?qū)犹峁┑哪芰?,也可以說(shuō),應(yīng)用層主要做的是編排工作。
另外,應(yīng)用層還負(fù)責(zé)了事務(wù)這個(gè)比較重要的功能。
3、領(lǐng)域?qū)?/strong>
領(lǐng)域?qū)邮钦麄€(gè)業(yè)務(wù)的核心層。我們一般會(huì)使用充血模型來(lái)建模實(shí)際的領(lǐng)域?qū)ο蟆?/p>
同時(shí),由于業(yè)務(wù)的核心價(jià)值在于其運(yùn)作模式,而不是具體的技術(shù)手段或?qū)崿F(xiàn)方式。因此,領(lǐng)域?qū)拥木幋a原則上不允許依賴其他外部對(duì)象。
4、基礎(chǔ)設(shè)施層
基礎(chǔ)設(shè)施層,是在技術(shù)上具體的實(shí)現(xiàn)細(xì)節(jié),它為上面各層提供通用的技術(shù)能力。
比如我們使用了哪種數(shù)據(jù)庫(kù),數(shù)據(jù)是怎么存儲(chǔ)的,有沒(méi)有用到緩存、消息隊(duì)列等,都是在這一層要實(shí)現(xiàn)的。
對(duì)于這四個(gè)層次的劃分,大家通常都沒(méi)有太多的異議。但是在層與層之間的依賴關(guān)系上,后續(xù)又衍生出了很多的改良版本。比如在 IDDD 一書(shū)中,就給出了下圖所示的分層架構(gòu):
這里最大的不同,就是將領(lǐng)域?qū)臃诺搅苏麄€(gè)架構(gòu)的最下面,也即領(lǐng)域?qū)又戮筒辉儆腥魏蔚钠渌蕾嚒_@么做是沒(méi)有問(wèn)題的,但是最上面的基礎(chǔ)設(shè)施層看起來(lái)卻怪怪的。
在實(shí)際開(kāi)發(fā)中,領(lǐng)域?qū)拥念I(lǐng)域服務(wù)往往需要訪問(wèn)持久化組件,以及基礎(chǔ)設(shè)施層中的其他組件,而對(duì)于持久化組件來(lái)說(shuō),不可避免地需要依賴領(lǐng)域?qū)拥膶?shí)體對(duì)象。如此一來(lái),領(lǐng)域?qū)雍突A(chǔ)設(shè)施層,就產(chǎn)生了雙向依賴關(guān)系。
實(shí)際的解決方式,就是讓領(lǐng)域?qū)雍突A(chǔ)設(shè)施層 都依賴一個(gè)統(tǒng)一的抽象,比如對(duì)于模型的持久化有 Repository 接口,對(duì)其他外部資源的訪問(wèn)也可以通過(guò)接口的形式來(lái)解耦合。但是 Repository 接口跟其他接口 又有些不太一樣,Repository 因?yàn)樾枰獏⑴c到實(shí)體的整個(gè)生命周期中,所以在很多時(shí)候 Repository 都被看作是領(lǐng)域?qū)又械囊粏T。而對(duì)基礎(chǔ)設(shè)施層中其他組件的抽象,是不適合定義到領(lǐng)域?qū)拥摹?/p>
?? DDD代碼模型
結(jié)合上面的描述,這個(gè)時(shí)候再來(lái)看代碼的組織形式,就比較清晰了。默認(rèn)情況下,一個(gè)上下文對(duì)應(yīng)了一個(gè)服務(wù),我們這里以包含單個(gè)上下文的情況為例,給出如下的代碼目錄結(jié)構(gòu):
對(duì)上面的代碼結(jié)構(gòu)做一個(gè)簡(jiǎn)短的說(shuō)明:
• Application,對(duì)應(yīng)到架構(gòu)里的應(yīng)用層,其內(nèi)可能包含一些 assembler 和 DTO,assembler 主要用于將領(lǐng)域?qū)ο筠D(zhuǎn)換成返回需要的數(shù)據(jù)格式,這些數(shù)據(jù)格式以DTO的形式進(jìn)行定義,這些DTO沒(méi)有任何的業(yè)務(wù)邏輯,就是單純的數(shù)據(jù)對(duì)象。
• domAIn,對(duì)應(yīng)的是領(lǐng)域?qū)?,倉(cāng)儲(chǔ)的接口也是放在這一層的。
• handler,對(duì)應(yīng)的是架構(gòu)里的用戶接口層,但其本質(zhì)上還是屬于基礎(chǔ)設(shè)施層的一部分,這里單獨(dú)提出來(lái)也僅僅是為了凸顯它的重要性。在這一層,只可以直接訪問(wèn)應(yīng)用層。
• infra,對(duì)應(yīng)的是基礎(chǔ)設(shè)施層,根據(jù)對(duì)不同資源的繼承需求,可以在 infra 下繼續(xù)分包。
• interfaces,是對(duì)基礎(chǔ)設(shè)施層中除持久化以外的中間件的抽象,也即我們?cè)谶@里定義訪問(wèn)中間件的接口,具體的實(shí)現(xiàn)還是放在基礎(chǔ)設(shè)施層。這里將接口單獨(dú)放到一個(gè)包中,為的是避免在領(lǐng)域?qū)优c應(yīng)用層對(duì)基礎(chǔ)設(shè)施層的直接依賴,如此就通過(guò)依賴反轉(zhuǎn)解耦了具體的技術(shù)細(xì)節(jié)。
至此,我們就明確了代碼的分層組織結(jié)構(gòu),以及彼此之間的依賴關(guān)系。
我們?cè)谖恼麻_(kāi)頭提到的第二個(gè)問(wèn)題是上下文的集成,在實(shí)際工作中,相信大家都會(huì)使用到微服務(wù),這樣一來(lái),如何集成就成為我們必須要考慮的問(wèn)題。
02? 與其他上下文集成
上下文的集成無(wú)外乎兩種方式, 一種是通過(guò)RPC進(jìn)行集成,另一種是通過(guò)領(lǐng)域事件進(jìn)行集成。
通過(guò)領(lǐng)域事件集成,也就是領(lǐng)域事件的發(fā)送和消費(fèi),這個(gè)我們?cè)谇懊娴奈恼轮幸呀?jīng)做了比較詳細(xì)的介紹,這里不再贅述。
接下來(lái)主要說(shuō)說(shuō)通過(guò) RPC 進(jìn)行集成。
?? 開(kāi)放主機(jī)與發(fā)布語(yǔ)言
我們先來(lái)看一個(gè)在 DDD 中,經(jīng)常用來(lái)表示集成方式的示例圖:
其中,被集成方(A上下文,U 是 Upstream 的縮寫)采用了開(kāi)放主機(jī)和發(fā)布語(yǔ)言的方式,而集成方(B上下文,D 是 Downstream 的縮寫)則使用了防腐層。幾個(gè)縮寫的含義如下:
• OHS(Open Host Service):開(kāi)放主機(jī)服務(wù),即定義一種協(xié)議,子系統(tǒng)可以通過(guò)該協(xié)議來(lái)訪問(wèn)你的服務(wù)。
• PL(Published Language):發(fā)布語(yǔ)言,通常跟 OHS 一起使用,用于定義開(kāi)放主機(jī)的協(xié)議。
• ACL(Anticorruption Layer):防腐層,一個(gè)上下文通過(guò)一些適配和轉(zhuǎn)換,來(lái)跟另一上下文交互。
我們平時(shí)大多數(shù)時(shí)候的開(kāi)發(fā)工作,都是跟 Grpc/Kitex 等 RPC 框架打交道的,不同的框架在設(shè)計(jì)之初都會(huì)定義一份協(xié)議,只有符合協(xié)議要求的請(qǐng)求 才能被正確地識(shí)別和處理。比如 Grpc 使用 HTTP2 作為傳輸協(xié)議,而 Kitex 則主要使用自定義的 TTHeader 協(xié)議。
這些框架在使用上,一個(gè)共同特點(diǎn)就是需要通過(guò) IDL(Interface description language) 來(lái)定義服務(wù)可以提供的能力。IDL 中可以定義多個(gè)接口,每個(gè)接口都有一個(gè)方法名,同時(shí)需要指定傳遞什么參數(shù),返回什么數(shù)據(jù)。這樣的一份 IDL 就可以認(rèn)為是我們?yōu)橄到y(tǒng)定義的發(fā)布語(yǔ)言。
還是以前面多次提到的商品服務(wù)為例,商品服務(wù)作為上下文集成中的被集成方,通過(guò) thrift 定義了其可以提供的服務(wù),比如下面是對(duì) GetProductDetail 接口的定義:
所以,如果我們是一個(gè)服務(wù)的提供方,只要我們使用 Grpc/Kitex,那么就可以認(rèn)為我們是使用 OSH 和 PL 方式來(lái)進(jìn)行集成的。
?? 防腐層
防腐層一般用在下游上下文中,可以用來(lái)隔絕上游上下文中可能發(fā)生的變化。
在上面的例子中,商品服務(wù)提供了一個(gè) GetProductDetail 接口,用以返回關(guān)于 Product 的全量信息。但是對(duì)于其他集成方來(lái)說(shuō),可能只是想拿到產(chǎn)品的很少一部分信息,比如在訂單服務(wù)中要展示訂單的詳情,而詳情只需要產(chǎn)品的圖片和名稱即可。
可以看到,作為服務(wù)的提供方,其具有追求普適性和靈活性的特點(diǎn),而服務(wù)的調(diào)用方,在使用時(shí)卻想要能夠集中滿足特定需求的接口。
這種張力是導(dǎo)致在邊界上出現(xiàn)問(wèn)題的主要原因,是無(wú)法避免的,但是卻是可以解決的,應(yīng)對(duì)的方法就是使用防腐層。
從圖中可以看出,Subsystem A 和 Subsystem B 的調(diào)用關(guān)系并不是直接產(chǎn)生的,都要通過(guò)中間的一個(gè)ACL,ACL 除了負(fù)責(zé)執(zhí)行具體的技術(shù)性調(diào)用,還將 A 和 B 的領(lǐng)域模型隔離開(kāi)來(lái),并承擔(dān)了彼此模型之間的翻譯轉(zhuǎn)換功能。
除此以外,還可以在 ACL 做緩存、兜底、開(kāi)關(guān)等功能。
對(duì)于集成方來(lái)說(shuō),一般采用獨(dú)立接口的形式,接口的定義放在 interfaces 中,上面這個(gè)例子就可以這樣定義:
因?yàn)閷?shí)現(xiàn)是跟具體的技術(shù)相關(guān)的,所以實(shí)現(xiàn)需要放到基礎(chǔ)設(shè)施層。整體的目錄層級(jí)如下:
具體的實(shí)現(xiàn)可以參考下面的代碼,簡(jiǎn)單來(lái)說(shuō)就是將通過(guò) RPC 獲取到的上游模型,轉(zhuǎn)換為自己領(lǐng)域內(nèi)的模型:
在傳統(tǒng)意義的防腐層實(shí)現(xiàn)中,會(huì)有一個(gè)適配器和一個(gè)對(duì)應(yīng)的翻譯器,其中適配器的作用是適配對(duì)其他上下文的調(diào)用,而翻譯器就是將調(diào)用的結(jié)果轉(zhuǎn)換成本地上下文中的元素。
在這里,我們?yōu)榱吮3执a的簡(jiǎn)單,沒(méi)有特意聲明這樣兩個(gè)對(duì)象,rpc的方法在這里起到了適配器的作用,至于翻譯器,我們只是簡(jiǎn)單的提出了一個(gè)方法,在方法名上做了特殊的前綴修飾。
最后,ProductRpcClient 會(huì)作為 ProductClient 的實(shí)現(xiàn)類,最終被注入到服務(wù)中。
03? CQRS 簡(jiǎn)單實(shí)現(xiàn)
我們?cè)诳匆恍┵Y料時(shí),可能會(huì)看到有的地方叫CQS有的又叫CQRS。CQS 和 CQRS 都表示命令與查詢的分離,本質(zhì)上沒(méi)有太大的區(qū)別。
CQS 是在《面向?qū)ο筌浖軜?gòu)》一書(shū)中提出來(lái)的概念,作者 Bertrand Meyer 認(rèn)為,一個(gè)方法原則上不應(yīng)該既修改數(shù)據(jù)又返回?cái)?shù)據(jù),所以就有了兩類方法:
1、查詢:返回?cái)?shù)據(jù),但不修改數(shù)據(jù),不會(huì)產(chǎn)生副作用;
2、命令:修改數(shù)據(jù),但不返回?cái)?shù)據(jù),存在副作用。
CQRS 是對(duì) CQS 概念的升華,因?yàn)椴樵兌酥环祷財(cái)?shù)據(jù),完全不修改數(shù)據(jù),所以我們所有的查詢不需要走領(lǐng)域?qū)嶓w,甚至沒(méi)必要使用 ORM 框架,總之,我們可以通過(guò)各種手段來(lái)提升查詢的效率。
關(guān)于CQS與CQRS的更多信息,可以參考這篇文章,和這篇。
下圖是在各種技術(shù)文章中你會(huì)經(jīng)??吹降囊粋€(gè)非常典型的 CQRS 架構(gòu)示例:
圖中左側(cè)部分代表的是對(duì) Command 的處理,右側(cè)是對(duì) Query 的執(zhí)行。很明顯的一個(gè)區(qū)別是,在 Query 中不再?gòu)?qiáng)制必須走領(lǐng)域模型,而是在應(yīng)用層可以直接訪問(wèn)基礎(chǔ)設(shè)施層。
在實(shí)際開(kāi)發(fā)中,對(duì) Query 的處理其實(shí)是比較靈活的,其目的無(wú)外乎是提高查詢的效率,另一方面也可以保證領(lǐng)域模型職責(zé)的單一。通常在查詢相對(duì)簡(jiǎn)單的時(shí)候會(huì)復(fù)用領(lǐng)域模型,在稍微復(fù)雜時(shí),會(huì)直接訪問(wèn)底層的數(shù)據(jù)模型,如果查詢變得更加復(fù)雜,會(huì)將數(shù)據(jù)的存儲(chǔ)也獨(dú)立出來(lái)。
下面我們就依次說(shuō)說(shuō)這幾種情況要如何處理。
?? 復(fù)用領(lǐng)域模型
這種是最簡(jiǎn)單的情況,對(duì)應(yīng)的讀模型就是領(lǐng)域模型,要查詢的數(shù)據(jù)基本上都是模型里的屬性。
比如,我們有一個(gè)庫(kù)存的聚合根:
展示的數(shù)據(jù)如下:
這個(gè)時(shí)候,就可以通過(guò) assembler 直接轉(zhuǎn)成對(duì)應(yīng)的 view:
因?yàn)榫酆细蛡}(cāng)儲(chǔ)是一一對(duì)應(yīng)的,所以,在應(yīng)用服務(wù)中直接通過(guò) Repository 獲取領(lǐng)域模型即可:
?? 使用數(shù)據(jù)模型
在分頁(yè)查詢,或者是需要多個(gè)實(shí)體聚合查詢的場(chǎng)景,如果直接通過(guò) Repository 獲取領(lǐng)域模型再組裝,可能會(huì)產(chǎn)生很多無(wú)關(guān)查詢,影響效率。
這個(gè)時(shí)候,可以根據(jù)要展示的數(shù)據(jù)直接使用數(shù)據(jù)模型,或者通過(guò) sql 只獲取指定的某幾個(gè)字段。
比如,我們有 Product 和 Category 兩個(gè)聚合根,它們都包含了大量的屬性和業(yè)務(wù)邏輯,但是我們要展示的數(shù)據(jù)比較簡(jiǎn)單:
這個(gè)時(shí)候就可以通過(guò)直接 sql 的形式來(lái)繞過(guò)領(lǐng)域模型:
?? 使用獨(dú)立的讀模型
這種情況下,一般對(duì)應(yīng)的查詢場(chǎng)景都比較豐富,通常都會(huì)有一個(gè)獨(dú)立的查詢服務(wù),各種數(shù)據(jù)在聚合處理之后統(tǒng)一放到查詢服務(wù)中。
如下所示,訂單在創(chuàng)建后,會(huì)使用 EventPublisher 來(lái)發(fā)布相應(yīng)的事件:
在訂單查詢服務(wù)中,會(huì)對(duì)訂單創(chuàng)建這個(gè)事件進(jìn)行監(jiān)聽(tīng),當(dāng)收到對(duì)應(yīng)的消息時(shí),會(huì)將訂單信息存儲(chǔ)到ES里。
如此一來(lái),訂單數(shù)據(jù)就同時(shí)存在于 MySQL 以及 ES 中。而在查詢的時(shí)候會(huì)只通過(guò) ES。
04? 結(jié)語(yǔ)
在這篇文章中,我們介紹了實(shí)踐領(lǐng)域驅(qū)動(dòng)設(shè)計(jì)的時(shí)候應(yīng)該如何組織代碼結(jié)構(gòu)、如何進(jìn)行上下文的集成,以及在復(fù)雜查詢場(chǎng)景中使用CQRS。這些內(nèi)容我同樣是用腦圖的形式為你總結(jié):
希望通過(guò)今天的講解,你能夠更游刃有余地應(yīng)對(duì)開(kāi)發(fā)中遇到的各種問(wèn)題。但總地來(lái)說(shuō),DDD只是一種思想,所謂的分層架構(gòu)也并不是事實(shí)上的標(biāo)準(zhǔn),在實(shí)際應(yīng)用時(shí),還要結(jié)合自身的理解,可以適當(dāng)?shù)厝?chuàng)新或進(jìn)行改進(jìn)。
到目前為止,關(guān)于領(lǐng)域驅(qū)動(dòng)設(shè)計(jì)的所有內(nèi)容就都已經(jīng)介紹完了。在下一篇文章中,我們會(huì)結(jié)合一個(gè)虛構(gòu)的商城系統(tǒng),帶你實(shí)戰(zhàn)領(lǐng)域驅(qū)動(dòng)設(shè)計(jì)。
【技術(shù)專家】
于振
現(xiàn)于某大型互聯(lián)網(wǎng)公司,負(fù)責(zé)架構(gòu)工作
曾就職于美團(tuán)、快手等一線互聯(lián)網(wǎng)公司