<kbd id="afajh"><form id="afajh"></form></kbd>
<strong id="afajh"><dl id="afajh"></dl></strong>
    <del id="afajh"><form id="afajh"></form></del>
        1. <th id="afajh"><progress id="afajh"></progress></th>
          <b id="afajh"><abbr id="afajh"></abbr></b>
          <th id="afajh"><progress id="afajh"></progress></th>

          為 Gopher 打造 DDD 系列:領域模型-實體

          共 3751字,需瀏覽 8分鐘

           ·

          2020-07-27 19:48

          前言: 實體具有業(yè)務屬性、業(yè)務邏輯和業(yè)務行為,是是實實在在的業(yè)務對象。在事件風暴中,我們可以根據(jù)命令、操作與事件將業(yè)務上緊密結合在一起的多個實體與值對象進行聚合形成聚合根。

          實體是什么

          雖然數(shù)據(jù)庫的設計占據(jù)了主導地位(這個是沒錯的),但開發(fā)者也不應該只關注數(shù)據(jù),而且要關注模型。

          數(shù)據(jù)+行為= 模型,實體就是含有領域概念的模型。它是一個唯一的東西,在相當長的時間里數(shù)據(jù)狀態(tài)在持續(xù)地變化,并且一定有唯一鍵,這區(qū)別于值對象。

          注意的是如果非要用表結構里的一條含有主鍵的數(shù)據(jù)去理解實體也是可以的,但不少情況下可能是有多個表或者k/v數(shù)據(jù)來組成的一個實體。

          實體、值對象與數(shù)據(jù)模型示例

          實體、值對象與數(shù)據(jù)模型示例

          實體是可變的,是變性,每個用戶實體都有自己的唯一性,我們用id來進行區(qū)分。值對象是不變的,是共性,實體都有相同的值對象,例如國家等信息。我們以此區(qū)分好實體與值對象。

          為什么使用實體

          如果開發(fā)者設計系統(tǒng)時,并沒有建模而是直接開始設計表結構和它的CRUD,其實這也算是建模的一種,但這種方式僅僅能應對簡單的模型。這樣的操作當更復雜的業(yè)務和更復雜的模型出現(xiàn)后是駕馭不了的。假設我們做10平米的臥室設計,那么簡單的量一下桌椅床就ok。但如果我們設計一個200平米的化學實驗室的時候,這個簡單的擺放可能搞不好會導致爆炸吧~


          實體的數(shù)據(jù)與行為

          唯一標識

          在實體的設計早期,我們將關注點都放在了實體的身份唯一性、屬性、行為上。同時還應該關注對實體的查詢和創(chuàng)建,我們首先要考慮實體的本質特征,特別是實體的唯一標識符,它是一個關系節(jié)點。比如用戶這個實體username是不是唯一標識,如果不是唯一,是不是可以通過username去查找。

          實踐

          https://github.com/8treenet/freedom/tree/master/example/fshop/domain/entity

          1. 所有的entity都必須繼承freedom.Entity 接口, 這里為實體注入了領域事件和運行時的Worker對象。
          2. 所有的entity都必須重寫 Identity() string 方法。
          3. 實體可以選擇的繼承PO或者DTOPO是通過腳手架生成的表模型屬性和Get/Set方法。

          發(fā)布領域事件

          type Entity interface {    //發(fā)布領域事件  DomainEvent(string,interface{},...map[string]string)    //唯一ID  Identity() string    //獲取請求運行時對象  GetWorker() Worker  SetProducer(string)  Marshal() []byte}

          商品的屬性和行為

          商品實體

          package entity
          import ( "errors" "strconv"
          "github.com/8treenet/freedom" "github.com/8treenet/freedom/example/fshop/domain/po")
          const ( //熱銷 GoodsHotTag = "HOT" //新品 GoodsNewTag = "NEW" GoodsNoneTag = "NONE")
          // 商品實體type Goods struct { freedom.Entity //繼承實體基類接口 po.Goods //繼承持久化的商品模型,包含了商品的列和屬性方法}
          // Identity 唯一func (g *Goods) Identity() string { return strconv.Itoa(g.Id)}
          // CutStock 扣庫存func (g *Goods) CutStock(num int) error { if num > g.Stock { return errors.New("庫存不足") } g.AddStock(-num) //po對象的方法,增加庫存 return nil}
          // MarkedTag 為商品打tagfunc (g *Goods) MarkedTag(tag string) error { if tag != GoodsHotTag && tag != GoodsNewTag && tag != GoodsNoneTag { return errors.New("Tag doesn't exist") } g.SetTag(tag) //po對象的方法,設置tag return nil}

          用戶實體
          package entity
          import ( "errors" "strconv"
          "github.com/8treenet/freedom" "github.com/8treenet/freedom/example/fshop/domain/po")
          // 用戶實體type User struct { freedom.Entity //繼承實體基類接口 po.User //繼承持久化的用戶模型,包含了用戶的列和屬性方法}
          // Identity 唯一func (u *User) Identity() string { return strconv.Itoa(u.Id)}
          // ChangePassword 修改密碼func (u *User) ChangePassword(newPassword, oldPassword string) error { //判斷舊密碼是否正確 if u.Password != oldPassword { return errors.New("Password error") } u.SetPassword(newPassword) //po對象的方法,可以設置密碼 returnnil}

          訂單實體

          package entity
          import ( "github.com/8treenet/freedom" "github.com/8treenet/freedom/example/fshop/domain/po")
          const ( OrderStatusPAID = "PAID" //付款 OrderStatusNonPayment = "NON_PAYMENT" //未付款 OrderStatusShipment = "SHIPMENT" //發(fā)貨)
          // 訂單實體type Order struct { freedom.Entity //繼承實體基類接口 po.Order //繼承持久化的訂單模型,包含了訂單的列和屬性方法 Details []*po.OrderDetail //定義訂單商品詳情成員變量,一個訂單包含多個商品}
          // Identity 唯一func (o *Order) Identity() string { return o.OrderNo}
          // AddOrderDetal 增加訂單詳情func (o *Order) AddOrderDetal(detal *po.OrderDetail) { o.Details = append(o.Details, detal) //增加訂單詳情,repository會做持久化處理}
          // Pay 付款func (o *Order) Pay() { o.SetStatus(OrderStatusPAID) //po對象的方法,設置狀態(tài)}
          // Shipment 發(fā)貨func (o *Order) Shipment() { o.SetStatus(OrderStatusShipment) //po對象的方法,設置狀態(tài)}
          // IsPay 是否支付func (o *Order) IsPay() bool { //判斷是否付款 if o.Status != OrderStatusPAID { return false } return true}

          目錄

          • golang領域模型-開篇
          • golang領域模型-六邊形架構
          • golang領域模型-實體
          • golang領域模型-資源庫
          • golang領域模型-依賴倒置
          • golang領域模型-聚合根
          • golang領域模型-CQRS
          • golang領域模型-領域事件

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





          推薦閱讀



          學習交流 Go 語言,掃碼回復「進群」即可


          站長 polarisxu

          自己的原創(chuàng)文章

          不限于 Go 技術

          職場和創(chuàng)業(yè)經(jīng)驗


          Go語言中文網(wǎng)

          每天為你

          分享 Go 知識

          Go愛好者值得關注

          瀏覽 144
          點贊
          評論
          收藏
          分享

          手機掃一掃分享

          分享
          舉報
          評論
          圖片
          表情
          推薦
          點贊
          評論
          收藏
          分享

          手機掃一掃分享

          分享
          舉報
          <kbd id="afajh"><form id="afajh"></form></kbd>
          <strong id="afajh"><dl id="afajh"></dl></strong>
            <del id="afajh"><form id="afajh"></form></del>
                1. <th id="afajh"><progress id="afajh"></progress></th>
                  <b id="afajh"><abbr id="afajh"></abbr></b>
                  <th id="afajh"><progress id="afajh"></progress></th>
                  国产精品九九九九九九 | 免费毛片a在线看 | 欧美双飞在线 | 成人三级片网站视频 | 天天通天天透天天插 |