Asp.net Core 系列之--3.領域、倉儲、服務簡單實現


ChuanGoing 2019-11-11 

   距離上篇近兩個月時間,一方面時因為其他事情耽擱,另一方面也是之前准備不足,關於領域驅動有幾個地方沒有想通透,也就沒有繼續碼字。目前網絡包括園子里大多領域驅動設計的文章,關於倉儲者一層都沒有詳細的說明,只是簡單的一筆帶過:領域驅動不關心具體的持久化如何落地。但是,作為"猿人類"就不可避免的繞不開持久化。本篇將會簡略的介紹利用Dapper這個輕量級的ORM來實現如何持久化。

本篇學習曲線:

1.領域模型

2.領域倉儲

3.簡單服務實現

領域模型設計

  我這里用常見的電商業務模型來介紹領域模型的設計,因篇幅有限,這里主要介紹訂單與訂單明細模型,基於如下業務:

1.創建訂單時生成訂單號、計算商品的總價同時生成訂單明細。

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測試下創建和獲取訂單信息

 

訂單參數如上

 

 

調試信息-訂單創建

 

 

根據訂單編號,返回訂單信息

 

回顧

  回顧一下本篇內容,分別介紹了:領域模型、領域倉儲、簡單服務的實現,然后利用postman模擬http請求演示了數據的創建與獲取。

  本篇只是簡單的介紹了領域服務及相關概念,后面有機會再做詳細討論,下篇將介紹日志、鑒權、錯誤及事物處理。

代碼

  本篇涉及的源碼在Github的https://github.com/ChuanGoing/Start.git  的Domain分支可以找到。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM