前言:六邊形架構(gòu)又稱“端口適配器架構(gòu)”,實際上也是一種分層架構(gòu)深夯,只不過由上下或者左右變成了內(nèi)部與外部阿趁。其核心理念就是應(yīng)用通過端口與外部進行交互的。核心的業(yè)務(wù)邏輯(領(lǐng)域模型)與外部資源(數(shù)據(jù)庫等資源)完全隔離处硬,僅通過適配器進行交互小槐,解決了業(yè)務(wù)邏輯與用戶數(shù)據(jù)交錯的問題拇派,很好的實現(xiàn)了前后端分離。
困惑:
- 在分層架構(gòu)中是否困惑過某些邏輯處理或某些數(shù)據(jù)處理該放在哪一層凿跳?
- 在分層架構(gòu)中是否困惑過該分多少層件豌?
- 在分層架構(gòu)中是否困惑過平層和跨層調(diào)用是否合理?
六邊形架構(gòu)
Alistair Cockburn
提出了一種具有對稱特征的架構(gòu)風(fēng)格。在這種架構(gòu)中控嗜,不同的客戶通過平等的方式與系統(tǒng)交互茧彤。比如HTTP
客戶,MQ
客戶疆栏,它們平等對系統(tǒng)提供輸入曾掂。Redis
和DB
也平等的提供輸出。每個客戶都擁有自己的適配器壁顶,去理解輸入珠洗,比如gin
、iris
若专、echo
就是http
的適配器许蓖。那么內(nèi)部是業(yè)務(wù)系統(tǒng)(領(lǐng)域模型),外部就是輸入和輸出的適配器调衰。重心放在內(nèi)部業(yè)務(wù)邏輯上膊爪,隔離輸入和輸出。如果非要用分層來理解嚎莉,那么六邊形分為內(nèi)層和外層米酬。
Alistair Cockburn
提出的六邊形是有Application和Domain
的,但現(xiàn)在微服務(wù)體系下Application已經(jīng)沒有存在的必要了趋箩,一個微服務(wù)就是一個Application
赃额。
那么六邊形和DDD的結(jié)合是如何應(yīng)對上述困惑的琼懊。所有數(shù)據(jù)處理全部由repository
適配成實體,邏輯都是領(lǐng)域服務(wù)爬早、聚合哼丈、實體的行為。分多少層和平層筛严、跨層調(diào)用本身也不存在醉旦。
項目目錄
-
domain
- 領(lǐng)域模型-
aggregate
- 聚合 -
entity
- 實體 -
dto
- 傳輸對象 -
po
- 持久化對象 -
*.go
- 領(lǐng)域服務(wù)
-
-
adapter
- 端口適配器-
controller
- 輸入適配器 -
repository
- 輸出適配器
-
-
server
- 服務(wù)端程序入口-
conf
- 配置文件 -
main.go
- 主函數(shù)
-
-
infra
- 基礎(chǔ)設(shè)施-
*go
- 基礎(chǔ)設(shè)施組件
-
domain 領(lǐng)域模型目錄
對應(yīng)六邊形的內(nèi)部,主要放領(lǐng)域服務(wù)service
的代碼桨啃。子目錄分為aggregate
聚合根目錄车胡、entity
實體目錄。dto
子目錄是外部輸入輸出對象照瘾。po
子目錄是數(shù)據(jù)庫的持久化對象匈棘,這些對象是生成的。
adapter 適配器目錄
對應(yīng)六邊形的外部析命,主要是輸入和輸出的適配器主卫。controller
子目錄負責(zé)http
的api
輸入,repository
子目錄負責(zé)實體的讀寫鹃愤。
外部adapter
目錄下的controller
和repository
依賴內(nèi)部的domain
相關(guān)簇搅,那么domain
要使用repository
處理po
的讀寫呢?這樣不就互相依賴了嗎软吐?后續(xù)會篇幅依賴倒置講解如何外部依賴內(nèi)部瘩将,內(nèi)部依賴抽象。
代碼示例
package controller
import (
domain "github.com/8treenet/freedom/example/fshop/domain"
)
type Cart struct {
Worker freedom.Worker
CartSev *domain.Cart //購物車領(lǐng)域服務(wù)凹耙,依賴注入
}
// GetItems 獲取購物車商品列表, GET: /cart/items route.
func (c *Cart) GetItems() freedom.Result {
userId, err := c.Worker.IrisContext().URLParamInt("userId")
if err != nil {
return &infra.JSONResponse{Error: err}
}
//適配http的輸入?yún)?shù)userId后調(diào)用領(lǐng)域模型目錄的入口領(lǐng)域服務(wù)
dto, err := c.CartSev.Items(userId)
if err != nil {
return &infra.JSONResponse{Error: err}
}
return &infra.JSONResponse{Object: dto}
}
package domain
import (
//引用倉庫
"github.com/8treenet/freedom/example/fshop/adapter/repository"
"github.com/8treenet/freedom/example/fshop/domain/aggregate"
)
func init() {
freedom.Prepare(func(initiator freedom.Initiator) {
//綁定創(chuàng)建領(lǐng)域服務(wù)函數(shù)到框架姿现,框架會根據(jù)客戶的使用做依賴倒置和依賴注入的處理。
initiator.BindService(func() *Cart {
//創(chuàng)建 Cart領(lǐng)域服務(wù)
return &Cart{}
})
//控制器客戶使用需要明確使用 InjectController
initiator.InjectController(func(ctx freedom.Context) (service *Cart) {
initiator.GetService(ctx, &service)
return
})
})
}
// Cart 購物車領(lǐng)域服務(wù).
type Cart struct {
CartRepo repository.CartRepo //購物車倉庫肖抱,這里是依賴倒置的
}
// Items 購物車全部商品項
func (c *Cart) Items(userId int) (items dto.CartItemRes, e error) {
// 使用 c.CartRepo讀取購物車數(shù)據(jù)
return
}
// DeleteAll 清空購物車
func (c *Cart) DeleteAll(userId int) (e error) {
return c.CartRepo.DeleteAll(userId)
}
目錄
- golang領(lǐng)域模型-開篇
- golang領(lǐng)域模型-六邊形架構(gòu)
- golang領(lǐng)域模型-實體
- golang領(lǐng)域模型-資源庫
- golang領(lǐng)域模型-依賴倒置
- golang領(lǐng)域模型-聚合根
- golang領(lǐng)域模型-CQRS
- golang領(lǐng)域模型-領(lǐng)域事件
項目代碼 https://github.com/8treenet/freedom/tree/master/example/fshop
PS:關(guān)注公眾號《從菜鳥到大佬》备典,發(fā)送消息“加群”或“領(lǐng)域模型”,加入DDD交流群虐沥,一起切磋DDD與代碼的藝術(shù)熊经!