|
前言: 六邊形架構(gòu)又稱(chēng)“端口適配器架構(gòu)”,實(shí)際上也是一種分層架構(gòu),只不過(guò)由上下或者左右變成了內(nèi)部與外部。其核心理念就是應(yīng)用通過(guò)端口與外部進(jìn)行交互的。核心的業(yè)務(wù)邏輯(領(lǐng)域模型)與外部資源(數(shù)據(jù)庫(kù)等資源)完全隔離,僅通過(guò)適配器進(jìn)行交互,解決了業(yè)務(wù)邏輯與用戶(hù)數(shù)據(jù)交錯(cuò)的問(wèn)題,很好的實(shí)現(xiàn)了前后端分離。
困惑:
- 在分層架構(gòu)中是否困惑過(guò)某些邏輯處理或某些數(shù)據(jù)處理該放在哪一層?
- 在分層架構(gòu)中是否困惑過(guò)該分多少層?
- 在分層架構(gòu)中是否困惑過(guò)平層和跨層調(diào)用是否合理?
六邊形架構(gòu)
Alistair Cockburn 提出了一種具有對(duì)稱(chēng)特征的架構(gòu)風(fēng)格。在這種架構(gòu)中,不同的客戶(hù)通過(guò)平等的方式與系統(tǒng)交互。比如HTTP客戶(hù),MQ客戶(hù),它們平等對(duì)系統(tǒng)提供輸入。Redis和DB也平等的提供輸出。每個(gè)客戶(hù)都擁有自己的適配器,去理解輸入,比如gin、iris、echo就是http的適配器。那么內(nèi)部是業(yè)務(wù)系統(tǒng)(領(lǐng)域模型),外部就是輸入和輸出的適配器。重心放在內(nèi)部業(yè)務(wù)邏輯上,隔離輸入和輸出。如果非要用分層來(lái)理解,那么六邊形分為內(nèi)層和外層。
Alistair Cockburn提出的六邊形是有Application和Domain的,但現(xiàn)在微服務(wù)體系下Application已經(jīng)沒(méi)有存在的必要了,一個(gè)微服務(wù)就是一個(gè)Application。 
那么六邊形和DDD的結(jié)合是如何應(yīng)對(duì)上述困惑的。所有數(shù)據(jù)處理全部由repository適配成實(shí)體,邏輯都是領(lǐng)域服務(wù)、聚合、實(shí)體的行為。分多少層和平層、跨層調(diào)用本身也不存在。
domain - 領(lǐng)域模型
aggregate - 聚合entity - 實(shí)體dto - 傳輸對(duì)象po - 持久化對(duì)象*.go - 領(lǐng)域服務(wù) adapter - 端口適配器
controller - 輸入適配器repository - 輸出適配器 server - 服務(wù)端程序入口
conf - 配置文件main.go - 主函數(shù) infra - 基礎(chǔ)設(shè)施
domain 領(lǐng)域模型目錄
對(duì)應(yīng)六邊形的內(nèi)部,主要放領(lǐng)域服務(wù)service的代碼。子目錄分為aggregate聚合根目錄、entity實(shí)體目錄。dto子目錄是外部輸入輸出對(duì)象。po子目錄是數(shù)據(jù)庫(kù)的持久化對(duì)象,這些對(duì)象是生成的。
adapter 適配器目錄
對(duì)應(yīng)六邊形的外部,主要是輸入和輸出的適配器。controller子目錄負(fù)責(zé)http的api輸入,repository子目錄負(fù)責(zé)實(shí)體的讀寫(xiě)。
外部adapter目錄下的controller和repository依賴(lài)內(nèi)部的domain相關(guān),那么domain要使用repository處理po的讀寫(xiě)呢?這樣不就互相依賴(lài)了嗎?后續(xù)會(huì)篇幅依賴(lài)倒置講解如何外部依賴(lài)內(nèi)部,內(nèi)部依賴(lài)抽象。
代碼示例
package controller
import (
domain 'github.com/8treenet/freedom/example/fshop/domain'
)
type Cart struct {
Worker freedom.Worker
CartSev *domain.Cart //購(gòu)物車(chē)領(lǐng)域服務(wù),依賴(lài)注入
}
// GetItems 獲取購(gòu)物車(chē)商品列表, 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}
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
package domain
import (
//引用倉(cāng)庫(kù)
'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ù)到框架,框架會(huì)根據(jù)客戶(hù)的使用做依賴(lài)倒置和依賴(lài)注入的處理。
initiator.BindService(func() *Cart {
//創(chuàng)建 Cart領(lǐng)域服務(wù)
return &Cart{}
})
//控制器客戶(hù)使用需要明確使用 InjectController
initiator.InjectController(func(ctx freedom.Context) (service *Cart) {
initiator.GetService(ctx, &service)
return
})
})
}
// Cart 購(gòu)物車(chē)領(lǐng)域服務(wù).
type Cart struct {
CartRepo repository.CartRepo //購(gòu)物車(chē)倉(cāng)庫(kù),這里是依賴(lài)倒置的
}
// Items 購(gòu)物車(chē)全部商品項(xiàng)
func (c *Cart) Items(userId int) (items dto.CartItemRes, e error) {
// 使用 c.CartRepo讀取購(gòu)物車(chē)數(shù)據(jù)
return
}
// DeleteAll 清空購(gòu)物車(chē)
func (c *Cart) DeleteAll(userId int) (e error) {
return c.CartRepo.DeleteAll(userId)
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
目錄
- golang領(lǐng)域模型-開(kāi)篇
- golang領(lǐng)域模型-六邊形架構(gòu)
- golang領(lǐng)域模型-實(shí)體
- golang領(lǐng)域模型-資源庫(kù)
- golang領(lǐng)域模型-依賴(lài)倒置
- golang領(lǐng)域模型-聚合根
- golang領(lǐng)域模型-CQRS
- golang領(lǐng)域模型-領(lǐng)域事件
PS:關(guān)注公眾號(hào)《從菜鳥(niǎo)到大佬》,發(fā)送消息“加群”或“領(lǐng)域模型”,加入DDD交流群,一起切磋DDD與代碼的藝術(shù)!
|