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分支能够找到。