Golang領域模型-實體

{"type":"doc","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"strong"}],"text":"前言:"},{"type":"text","text":" 實體具有業務屬性、業務邏輯和業務行爲,是是實實在在的業務對象。在事件風暴中,我們可以根據命令、操作與事件將業務上緊密結合在一起的多個實體與值對象進行聚合形成聚合根。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"heading","attrs":{"align":null,"level":2},"content":[{"type":"text","text":"實體是什麼"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"雖然數據庫的設計佔據了主導地位(這個是沒錯的),但開發者也不應該只關注數據,而且要關注模型。數據+行爲= 模型,實體就是含有領域概念的模型。它是一個唯一的東西,在相當長的時間裏數據狀態在持續地變化,並且一定有唯一鍵,這區別於值對象。注意的是如果非要用表結構裏的一條含有主鍵的數據去理解實體也是可以的,但不少情況下可能是有多個表或者k/v數據來組成的一個實體。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"heading","attrs":{"align":null,"level":3},"content":[{"type":"text","text":"實體、值對象與數據模型示例"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"image","attrs":{"src":"https://static001.geekbang.org/infoq/72/720640bd1feeb7799a2c54b92f34b18d.png","alt":null,"title":null,"style":null,"href":null,"fromPaste":true,"pastePass":true}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":"center","origin":null},"content":[{"type":"text","marks":[{"type":"size","attrs":{"size":10}}],"text":"實體、值對象與數據模型示例"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"實體是可變的,是變性,每個用戶實體都有自己的唯一性,我們用id來進行區分。值對象是不變的,是共性,實體都有相同的值對象,例如國家等信息。我們以此區分好實體與值對象。"}]},{"type":"heading","attrs":{"align":null,"level":2},"content":[{"type":"text","text":"爲什麼使用實體"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"如果開發者設計系統時,並沒有建模而是直接開始設計表結構和它的CRUD,其實這也算是建模的一種,但這種方式僅僅能應對簡單的模型。這樣的操作當更復雜的業務和更復雜的模型出現後是駕馭不了的。假設我們做10平米的臥室設計,那麼簡單的量一下桌椅牀就ok。但如果我們設計一個200平米的化學實驗室的時候,這個簡單的擺放可能搞不好會導致爆炸吧~"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"image","attrs":{"src":"https://static001.geekbang.org/infoq/1c/1cccbd9c5d9726daba384f8ea5ba2bd9.jpeg","alt":null,"title":null,"style":null,"href":null,"fromPaste":true,"pastePass":true}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":"center","origin":null},"content":[{"type":"text","marks":[{"type":"size","attrs":{"size":10}}],"text":"實體"}]},{"type":"heading","attrs":{"align":null,"level":3},"content":[{"type":"text","text":"唯一標識"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"在實體的設計早期,我們將關注點都放在了實體的身份唯一性、屬性、行爲上。同時還應該關注對實體的查詢和創建,我們首先要考慮實體的本質特徵,特別是實體的唯一標識符,它是一個關係節點。比如用戶這個實體username是不是唯一標識,如果不是唯一,是不是可以通過username去查找。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"heading","attrs":{"align":null,"level":3},"content":[{"type":"text","text":"實踐"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"link","attrs":{"href":"https://github.com/8treenet/freedom/tree/master/example/fshop/domain/entity","title":null},"content":[{"type":"text","text":"https://github.com/8treenet/freedom/tree/master/example/fshop/domain/entity"}]}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":" 1. 所有的entity都必須繼承freedom.Entity 接口, 這裏爲實體注入了領域事件和運行時的Worker對象。 2. 所有的entity都必須重寫Identity() string 方法。 3. 實體可以選擇的繼承PO或者DTO,PO是通過腳手架生成的表模型屬性和Get/Set方法。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"codeblock","attrs":{"lang":null},"content":[{"type":"text","text":"type Entity interface {\n //發佈領域事件\n DomainEvent(string,interface{},...map[string]string)\n //唯一ID\n Identity() string\n //獲取請求運行時對象\n GetWorker() Worker\n SetProducer(string)\n Marshal() []byte\n}"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"image","attrs":{"src":"https://static001.geekbang.org/infoq/6a/6abf3b81c11f187e7b1ca01591852921.png","alt":null,"title":null,"style":null,"href":null,"fromPaste":true,"pastePass":true}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":"center","origin":null},"content":[{"type":"text","marks":[{"type":"size","attrs":{"size":10}}],"text":"商品的屬性和行爲"}]},{"type":"heading","attrs":{"align":null,"level":3},"content":[{"type":"text","text":"商品實體"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"codeblock","attrs":{"lang":null},"content":[{"type":"text","text":"package entity\n\nimport (\n \"errors\"\n \"strconv\"\n\n \"github.com/8treenet/freedom\"\n \"github.com/8treenet/freedom/example/fshop/domain/po\"\n)\n\nconst (\n //熱銷\n GoodsHotTag = \"HOT\"\n //新品\n GoodsNewTag = \"NEW\"\n GoodsNoneTag = \"NONE\"\n)\n\n// 商品實體\ntype Goods struct {\n freedom.Entity //繼承實體基類接口\n po.Goods //繼承持久化的商品模型,包含了商品的列和屬性方法\n}\n\n// Identity 唯一\nfunc (g *Goods) Identity() string {\n return strconv.Itoa(g.Id)\n}\n\n// CutStock 扣庫存\nfunc (g *Goods) CutStock(num int) error {\n if num > g.Stock {\n return errors.New(\"庫存不足\")\n }\n g.AddStock(-num) //po對象的方法,增加庫存\n return nil\n}\n\n// MarkedTag 爲商品打tag\nfunc (g *Goods) MarkedTag(tag string) error {\n if tag != GoodsHotTag && tag != GoodsNewTag && tag != GoodsNoneTag {\n return errors.New(\"Tag doesn't exist\")\n }\n g.SetTag(tag) //po對象的方法,設置tag\n return nil\n}"}]},{"type":"heading","attrs":{"align":null,"level":3},"content":[{"type":"text","text":"用戶實體"}]},{"type":"codeblock","attrs":{"lang":null},"content":[{"type":"text","text":"package entity\n\nimport (\n \"errors\"\n \"strconv\"\n\n \"github.com/8treenet/freedom\"\n \"github.com/8treenet/freedom/example/fshop/domain/po\"\n)\n\n// 用戶實體\ntype User struct {\n freedom.Entity //繼承實體基類接口\n po.User //繼承持久化的用戶模型,包含了用戶的列和屬性方法\n}\n\n// Identity 唯一\nfunc (u *User) Identity() string {\n return strconv.Itoa(u.Id)\n}\n\n// ChangePassword 修改密碼\nfunc (u *User) ChangePassword(newPassword, oldPassword string) error {\n //判斷舊密碼是否正確\n if u.Password != oldPassword {\n return errors.New(\"Password error\")\n }\n u.SetPassword(newPassword) //po對象的方法,可以設置密碼\n return nil\n}"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"heading","attrs":{"align":null,"level":3},"content":[{"type":"text","text":"訂單實體"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"codeblock","attrs":{"lang":null},"content":[{"type":"text","text":"package entity\n\nimport (\n \"github.com/8treenet/freedom\"\n \"github.com/8treenet/freedom/example/fshop/domain/po\"\n)\n\nconst (\n OrderStatusPAID = \"PAID\" //付款\n OrderStatusNonPayment = \"NON_PAYMENT\" //未付款\n OrderStatusShipment = \"SHIPMENT\" //發貨\n)\n\n// 訂單實體\ntype Order struct {\n freedom.Entity //繼承實體基類接口\n po.Order //繼承持久化的訂單模型,包含了訂單的列和屬性方法\n Details []*po.OrderDetail //定義訂單商品詳情成員變量,一個訂單包含多個商品\n}\n\n// Identity 唯一\nfunc (o *Order) Identity() string {\n return o.OrderNo\n}\n\n// AddOrderDetal 增加訂單詳情\nfunc (o *Order) AddOrderDetal(detal *po.OrderDetail) {\n o.Details = append(o.Details, detal) //增加訂單詳情,repository會做持久化處理\n}\n\n// Pay 付款\nfunc (o *Order) Pay() {\n o.SetStatus(OrderStatusPAID) //po對象的方法,設置狀態\n}\n\n// Shipment 發貨\nfunc (o *Order) Shipment() {\n o.SetStatus(OrderStatusShipment) //po對象的方法,設置狀態\n}\n\n// IsPay 是否支付\nfunc (o *Order) IsPay() bool {\n //判斷是否付款\n if o.Status != OrderStatusPAID {\n return false\n }\n return true\n}"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"heading","attrs":{"align":null,"level":2},"content":[{"type":"text","text":"目錄"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"bulletedlist","content":[{"type":"listitem","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"golang領域模型-開篇"}]}]},{"type":"listitem","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"golang領域模型-六邊形架構"}]}]},{"type":"listitem","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"golang領域模型-實體"}]}]},{"type":"listitem","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"golang領域模型-資源庫"}]}]},{"type":"listitem","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"golang領域模型-依賴倒置"}]}]},{"type":"listitem","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"golang領域模型-聚合根"}]}]},{"type":"listitem","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"golang領域模型-CQRS"}]}]},{"type":"listitem","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"golang領域模型-領域事件"}]}]}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"heading","attrs":{"align":null,"level":3},"content":[{"type":"text","text":"項目代碼 "},{"type":"link","attrs":{"href":"https://github.com/8treenet/freedom/tree/master/example/fshop","title":null},"content":[{"type":"text","text":"https://github.com/8treenet/freedom/tree/master/example/fshop"}]}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"PS:關注公衆號《從菜鳥到大佬》,發送消息“加羣”或“領域模型”,加入DDD交流羣,一起切磋DDD與代碼的藝術!"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}}]}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章