ChuanGoing 2019-11-11 html
距離上篇近兩個月時間,一方面時由於其餘事情耽擱,另外一方面也是以前準備不足,關於領域驅動有幾個地方沒有想通透,也就沒有繼續碼字。目前網絡包括園子裏大多領域驅動設計的文章,關於倉儲者一層都沒有詳細的說明,只是簡單的一筆帶過:領域驅動不關心具體的持久化如何落地。可是,做爲"猿人類"就不可避免的繞不開持久化。本篇將會簡略的介紹利用Dapper這個輕量級的ORM來實現如何持久化。git
本篇學習曲線:github
1.領域模型數據庫
2.領域倉儲api
3.簡單服務實現網絡
領域模型設計app
我這裏用常見的電商業務模型來介紹領域模型的設計,因篇幅有限,這裏主要介紹訂單與訂單明細模型,基於以下業務:ide
1.建立訂單時生成訂單號、計算商品的總價同時生成訂單明細。post
2.根據訂單號獲取訂單信息學習
個人這個例子基本上涵蓋了領域模型的主要幾種業務組合:領域-實體-值對象,這幾個基本概念這裏不作贅述,園子裏一搜一大堆。
public partial class Order : DomainEntity<Guid> { /// <summary> /// 訂單流水號 /// </summary> public string Sn { get; private set; } /// <summary> /// 總價 /// </summary> public decimal TotalPrice { get; private set; } /// <summary> /// 狀態 /// </summary> public OrderStatus Status { get; private set; } /// <summary> /// 支付時間 /// </summary> public long PaymentTime { get; private set; } /// <summary> /// 過時時間 /// </summary> public long ExpireTime { get; private set; } /// <summary> /// 備註 /// </summary> public string Description { get; private set; } /// <summary> /// 用戶 /// </summary> public Guid UserId { get; private set; } public string Adress { get; private set; } /// <summary> /// 訂單明細 /// </summary> [Ignore] public List<OrderItem> OrderItems { get; private set; } }
public partial class OrderItem : Entity<Guid> { /// <summary> /// 訂單編號 /// </summary> public Guid OrderId { get; private set; } /// <summary> /// 商品編號 /// </summary> public Guid ProductId { get; private set; } /// <summary> /// 商品單價 /// </summary> public decimal Price { get; private set; } /// <summary> /// 數量 /// </summary> public int Count { get; private set; } /// <summary> /// 加入時間 /// </summary> public long JoinTime { get; private set; } }
能夠看到Order類爲DomainEntity(領域實體-聚合根),OrderItem爲Entity(實體),Order和OrderItem組成一個業務聚合。爲何這麼劃分呢?有兩方面的緣由:
1.本例不涉及複雜業務,沒有直接針對訂單明細的業務操做
2.訂單明細依賴於訂單,生命週期隨着訂單主體產生和消逝
訂單和訂單明細都被設計爲"partial",由於到目前爲止,咱們的實體類仍是POCO,也就是一般所說的貧血模型。所以,爲了賦予模型活力,咱們須要爲其添加某些行爲:
public partial class Order { public void Add(Guid userId, Adress adress, string description, List<OrderItem> orderItems) { Sn = Guid.NewGuid().ToString("N"); TotalPrice = orderItems.Sum(i => i.Price * i.Count); Status = OrderStatus.TobePaid; ExpireTime = DateTimeOffset.Now.AddMinutes(-30).ToUnixTimeMilliseconds(); UserId = userId; Adress = adress.ToString(); Description = description; orderItems.ForEach(i => { i.SetOrder(this); }); SetItems(orderItems); } public void Pay() { Status = OrderStatus.Paid; PaymentTime = DateTimeOffset.Now.ToUnixTimeMilliseconds(); } public void SetItems(List<OrderItem> orderItems) { OrderItems = orderItems; } }
這樣,咱們給領域賦予了某些行爲。
領域倉儲
結合我上篇Asp.net Core 系列之--2.ORM初探:Dapper實現MySql數據庫各種操做介紹的倉儲示例,這裏爲領域模型單獨設計了"領域倉儲層"
public class OrderRepository : DapperRepository<Order, Guid>, IOrderRepository { public OrderRepository(IComponentContext container, IDapperDbContext dbContext) : base(container, dbContext) { } public Order GetBySn(string sn) { var order = QuerySingleOrDefault<Order>("SELECT * FROM `Order` WHERE `Sn`=@Sn;", new { Sn = sn }); if (order != null) { order.SetItems(Query<OrderItem>("SELECT * FROM `OrderItem` WHERE `OrderId`=@OrderId;", new { OrderId = order.Id }).ToList()); } return order; } }
領域倉儲實現了Domain中定義的接口
public interface IOrderRepository : IRepository<Order, Guid>, IScopeInstance { Order GetBySn(string sn); }
值得注意的時,領域倉儲層這裏起到了承上啓下的做用,隔離了領域對於持久化層的直接依賴。
簡單服務實現
接下來,咱們實現如何在業務服務層調用領域倉儲實現數據的持久化。新建Application項目:
定義訂單接口服務
public interface IOrderService : IScopeInstance { void Add(OrderViewModel order); OrderViewResult Get(string sn); }
訂單服務實現
public void Add(OrderViewModel view) { if (view == null) new Exception("參數無效"); var order = new Order(); Mapper.Map(view, order); order.Add(view.UserId, view.Adress, view.Description, order.OrderItems); _repo.Insert(order); order.OrderItems.ForEach(i => _itemRepo.Insert(i)); } public OrderViewResult Get(string sn) { var order = _repo.GetBySn(sn); OrderViewResult result = new OrderViewResult(); return order == null ? null : Mapper.Map(order, result); }
在Webapi項目控制器文件夾下新建OrderController
public class OrderController : Controller { private readonly IOrderService _service; public OrderController(IOrderService service) { _service = service; } [HttpPost] public void Add([FromBody]OrderViewModel order) { _service.Add(order); } [HttpGet] public OrderViewResult Get([FromQuery]string sn) { return _service.Get(sn); } }
具體代碼請看篇末源代碼連接
這樣,咱們實現了領域模型的持久化。
回顧
回顧一下本篇內容,分別介紹了:領域模型、領域倉儲、簡單服務的實現,而後利用postman模擬http請求演示了數據的建立與獲取。
本篇只是簡單的介紹了領域服務及相關概念,後面有機會再作詳細討論,下篇將介紹日誌模塊和權限模塊。
代碼
本篇涉及的源碼在Github的https://github.com/ChuanGoing/Start.git 的Domain分支能夠找到。