日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

商品领域ddd_为 Gopher 打造 DDD 系列:领域模型-资源库

發布時間:2025/3/12 编程问答 37 豆豆
生活随笔 收集整理的這篇文章主要介紹了 商品领域ddd_为 Gopher 打造 DDD 系列:领域模型-资源库 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

前言: 作為領域模型中最重要的環節之一的Repository,其通過對外暴露接口屏蔽了內部的復雜性,又有其隱式寫時復制的巧妙代碼設計,完美的將DDD中的Repository的概念與代碼相結合!

Repository

資源庫通常標識一個存儲的區域,提供讀寫功能。通常我們將實體存放在資源庫中,之后通過該資源庫來獲取相同的實體,每一個實體都搭配一個資源庫。

如果你修改了某個實體,也需要通過資源庫去持久化。當然你也可以通過資源庫去刪除某一個實體。

資源庫對外部是屏蔽了存儲細節的,資源庫內部去處理 cache、es、db。

數據操作流程

Repository解除了client的巨大負擔,使client只需與一個簡單的、易于理解的接口進行對話,并根據模型向這個接口提出它的請求。要實現所有這些功能需要大量復雜的技術基礎設施,但接口卻很簡單,而且在概念層次上與領域模型緊密聯系在一起。

隱式寫時復制

通常我們通過資源庫讀取一個實體后,再對這個實體進行修改。那么這個修改后的持久化是需要知道實體的哪些屬性被修改,然后再對應的去持久化被修改的屬性。

注意商品實體的changes,商品被修改某個屬性,對應的Repository就持久化相應的修改。這么寫有什么好處呢?如果不這么做,那只能在service里調用orm指定更新列,但是這樣做的話,Repository的價值就完全被舍棄了!

可以說寫時復制是Repository和領域模型的橋梁!//商品實體type Goods struct {changes map[string]interface{} //被修改的屬性Name string//商品名稱Price int// 價格Stock int// 庫存}// SetPrice .func (obj *Goods) SetPrice(price int) {obj.Price = priceobj.changes["price"] = price //寫時復制}// SetStock .func (obj *Goods) SetStock(stock int) {obj.Stock = stockobj.changes["stock"] = stock //寫時復制}//示例func main() {goodsEntity := GoodsRepository.Get(1)goodsEntity.SetPrice(1000)GoodsRepositorySave(goodsEntity) //GoodsRepository 會內部處理商品實體的changes}工廠和創建

創建商品實體需要唯一ID和已知的屬性名稱等,可以使用實體工廠去生成唯一ID和創建,在交給資源庫去持久化,這也是<>的作者推薦的方式,但這種方式更適合文檔型數據庫,唯一ID是Key和實體序列化是值。

“底層技術可能會限制我們的建模選擇。例如,關系數據庫可能對復合對象結構的深度有實際的限制"(領域驅動設計:軟件核心復雜性應對之道 Eric Evans)

但我們更多的使用的是關系型數據庫,這樣資源庫就需要創建的行為。實體的唯一ID就是聚簇主鍵。一個實體或許是多張表組成,畢竟我們還要考慮垂直分表。我認為DDD的范式和關系型數據庫范式,后者更重要。有時候我們還要為Repository 實現一些統計select count(*)的功能。

根據所使用的持久化技術和基礎設施不同,Repository的實現也將有很大的變化。理想的實現是向客戶隱藏所有內部工作細節(盡管不向客戶的開發人員隱藏這些細節),這樣不管數據是存儲在對象數據庫中,還是存儲在關系數據庫中,或是簡單地保持在內存中,客戶代碼都相同。Repository將會委托相應的基礎設施服務來完成工作。將存儲、檢索和查詢機制封裝起來是Repository實現的最基本的特性。

實踐

https://github.com/8treenet/freedom/tree/master/example/fshop/adapter/repository

實體的緩存

這個是緩存組件的接口,可以讀寫實體,實體的key 使用必須實現的Identity 方法。一級緩存是基于請求的,首先會從一級緩存查找實體,生命周期是一個請求的開始和結束。

二級緩存是基于redis。

組件已經做了冪等的防擊穿處理。

SetSource設置持久化的回調函數,當一、二級緩存未命中,會讀取回調函數,并反寫一、二級緩存。// freedom.Entitytype Entity interface {DomainEvent(string, interface{},...map[string]string)Identity() stringGetWorker() WorkerSetProducer(string)Marshal() []byte}// infra.EntityCachetype EntityCache interface {//獲取實體GetEntity(freedom.Entity) error//刪除實體緩存Delete(result freedom.Entity, async ...bool) error//設置數據源SetSource(func(freedom.Entity) error) EntityCache//設置前綴SetPrefix(string) EntityCache//設置緩存時間,默認5分鐘SetExpiration(time.Duration) EntityCache//設置異步反寫緩存。默認關閉,緩存未命中讀取數據源后的異步反寫緩存SetAsyncWrite(bool) EntityCache//設置防擊穿,默認開啟SetSingleFlight(bool) EntityCache//關閉二級緩存. 關閉后只有一級緩存生效CloseRedis() EntityCache}以下實現了一個商品的資源庫package repositoryimport ("time""github.com/8treenet/freedom/infra/store""github.com/8treenet/freedom/example/fshop/domain/po""github.com/8treenet/freedom/example/fshop/domain/entity""github.com/8treenet/freedom")func init() {freedom.Prepare(func(initiator freedom.Initiator) {initiator.BindRepository(func() *Goods {return &Goods{}})})}// Goods .type Goods struct {freedom.Repository //資源庫必須繼承,這樣是為了約束 db、redis、http等的訪問Cache store.EntityCache //依賴注入實體緩存組件}// BeginRequestfunc (repo *Goods) BeginRequest(worker freedom.Worker) {repo.Repository.BeginRequest(worker)//設置緩存的持久化數據源,旁路緩存模型,如果緩存未有數據,將回調該函數。repo.Cache.SetSource(func(result freedom.Entity) error {return findGoods(repo, result)})//緩存30秒, 不設置默認5分鐘repo.Cache.SetExpiration(30 * time.Second)//設置緩存前綴repo.Cache.SetPrefix("freedom")}// Get 通過id 獲取商品實體.func (repo *Goods) Get(id int) (goodsEntity *entity.Goods, e error) {goodsEntity = &entity.Goods{}goodsEntity.Id = id//注入基礎Entity 包含運行時和領域事件的producerrepo.InjectBaseEntity(goodsEntity)//讀取緩存, Identity() 會返回 id,緩存會使用它當keyreturn goodsEntity, repo.Cache.GetEntity(goodsEntity)}// Save 持久化實體.func (repo *Goods) Save(entity *entity.Goods) error {_, e := saveGoods(repo, entity) //寫庫,saveGoods是腳手架生成的函數,會做寫時復制的處理。//清空緩存repo.Cache.Delete(entity)return e}func (repo *Goods) FindsByPage(page, pageSize int, tag string) (entitys []*entity.Goods, e error) {build := repo.NewORMDescBuilder("id").NewPageBuilder(page, pageSize) //創建分頁器e = findGoodsList(repo, po.Goods{Tag: tag}, &entitys, build)if e != nil {return}//注入基礎Entity 包含運行時和領域事件的producerrepo.InjectBaseEntitys(entitys)return}func (repo *Goods) New(name, tag string, price, stock int) (entityGoods *entity.Goods, e error) {goods := po.Goods{Name: name, Price: price, Stock: stock, Tag: tag, Created: time.Now(), Updated: time.Now()}_, e = createGoods(repo, &goods) //寫庫,createGoods是腳手架生成的函數。if e != nil {return}entityGoods = &entity.Goods{Goods: goods}repo.InjectBaseEntity(entityGoods)return}領域服務使用倉庫package domainimport ("github.com/8treenet/freedom/example/fshop/domain/dto""github.com/8treenet/freedom/example/fshop/adapter/repository""github.com/8treenet/freedom/example/fshop/domain/aggregate""github.com/8treenet/freedom/example/fshop/domain/entity""github.com/8treenet/freedom/infra/transaction""github.com/8treenet/freedom")func init() {freedom.Prepare(func(initiator freedom.Initiator) {initiator.BindService(func() *Goods {return &Goods{}})initiator.InjectController(func(ctx freedom.Context) (service *Goods) {initiator.GetService(ctx, &service)return})})}// Goods 商品領域服務.type Goods struct {Worker freedom.Worker //依賴注入請求運行時對象。GoodsRepo repository.Goods //依賴注入商品倉庫}// New 創建商品func (g *Goods) New(name string, price int) (e error) {g.Worker.Logger().Info("創建商品")_, e = g.GoodsRepo.New(name, entity.GoodsNoneTag, price, 100)return}// Items 分頁商品列表func (g *Goods) Items(page, pagesize int, tag string) (items []dto.GoodsItemRes, e error) {entitys, e := g.GoodsRepo.FindsByPage(page, pagesize, tag)if e != nil {return}for i := 0; i < len(entitys); i++ {items = append(items, dto.GoodsItemRes{Id: entitys[i].Id,Name: entitys[i].Name,Price: entitys[i].Price,Stock: entitys[i].Stock,Tag: entitys[i].Tag,})}return}// AddStock 增加商品庫存func (g *Goods) AddStock(goodsId, num int) (e error) {entity, e := g.GoodsRepo.Get(goodsId)if e != nil {return}entity.AddStock(num) //增加庫存entity.DomainEvent("Goods.Stock", entity) //發布增加商品庫存的領域事件return g.GoodsRepo.Save(entity)}

項目代碼 https://github.com/8treenet/freedom/tree/master/example/fshop

總結

以上是生活随笔為你收集整理的商品领域ddd_为 Gopher 打造 DDD 系列:领域模型-资源库的全部內容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。

主站蜘蛛池模板: 超碰影院在线 | 色视频在线播放 | 91视频xxx| 无码久久精品国产亚洲av影片 | 夜夜操天天射 | 美美女高清毛片视频免费观看 | 狠狠干美女 | 911看片 | 91丨porny丨成人蝌蚪 | 亚洲午夜久久 | aaaaa黄色片| 精品久久久久久久久久久 | 色天天综合| 亚洲激情欧美激情 | 免费看的一级片 | 国产一国产精品一级毛片 | 久久wwww| 日本精品一区在线 | 成人免费高清在线观看 | 日本裸体xx少妇18在线 | 久久精品2019中文字幕 | 久久伊人99 | 国产乱码精品一区二三赶尸艳谈 | 色xxxxxx | 欧美 日韩 国产 中文 | 99ri国产在线 | 伊人avav | 无码人妻精品一区二区蜜桃网站 | 一区两区小视频 | 成人午夜免费在线观看 | 久久成人精品视频 | 美女撒尿无遮挡网站 | 天天干天天搞天天射 | av大西瓜| 色综合av综合无码综合网站 | 青青青青草 | 国产午夜精品久久久久久久久久 | 欧美性69 | 青青草视频在线观看 | 茄子视频懂你更多在线观看 | 国产色图视频 | 一区二区三区网 | 免费裸体美女网站 | 亚洲av熟女国产一区二区性色 | 黄色高清视频在线观看 | 尤物网站在线 | 成人午夜精品视频 | 精品在线视频一区 | 成人毛片视频免费看 | 成人字幕 | 91重口味| 久久网站免费观看 | 天天爱天天插 | 色视频在线看 | 国产一区二区亚洲 | 午夜天堂视频 | 美女无遮挡网站 | 丁香九月婷婷 | 不卡av电影在线观看 | 国产一卡在线 | 很黄的网站在线观看 | 久久靖品 | 欧美日韩一区在线观看 | 一区二区在线观看免费视频 | 手机在线中文字幕 | 日本网站在线看 | www日本com | 成人永久免费 | 免费国偷自产拍精品视频 | 天天干夜夜想 | 波多野结衣中文字幕一区二区 | 亚洲特级片 | 中国极品少妇xxxx做受 | 欧美一级做a爰片免费视频 成人激情在线观看 | 特级精品毛片免费观看 | 囯产精品久久久久久 | 中文字幕 视频一区 | www.黄色网 | 香蕉视频啪啪 | 淫五月| 黄色网址在线看 | 欧美三级小说 | 亚洲理论片在线观看 | 最新中文字幕在线观看 | 欧产日产国产精品 | 欧洲亚洲成人 | 精品黑人一区二区三区观看时间 | 狠狠人妻久久久久久 | 在线不卡中文字幕 | 青青草国产精品 | 成人毛片一区二区三区 | 国产精品毛片一区视频播 | 日韩欧美电影一区二区三区 | 国产精品久久久久久久久久久久久久久久 | 天天做天天爱 | 亚洲精选国产 | 黄色片成年人 | 超碰在线人人干 | 蜜桃久久精品成人无码av |