Asp.net Core 系列之--3.领域、仓储、服务简单实现

摘要:
本文将简要介绍如何使用Dapper(一种轻量级ORM)来实现持久性。PublicpartialclassOrder:DomainEntity<Guid>{///<summary>///订单序列号///</summary>publicstringSn{get;privateset;}//<summary˃///<总价///<汇总>publicdecimalTotalPrice{get;privateset;}/////状态///</汇总>publicOrderStatusStatus{get,privateset,}////付款时间//˂汇总>publicingPayment time{gettime////publishingExpireTime{get;privateset;}/////备注//publicstringDescription{get,privateset,}////用户////publicGuidUserId{get{//////订单号////publicGuidOrderId{get;privateset;}//////项目号/summary˃publiclongJoinTime{get,privateset,}}}}有关订单的详细信息,您可以看到Order类是DomainEntity,OrderItem是Entity,Order和OrderItem形成业务聚合。因此为了激励模型,我们需要向其添加一些行为:publicpartialclassOrder{publicvoid add{Sn=Guid.NewGuid().ToString(“N”);TotalPrice=orderItems.Sum;Status=OrderStatus.TobePad;ExpireTime=DateTimeOffset.Now.AddMinutes(-30).ToUnixTimeMilliseconds();UserId=UserId;Adress=address.ToString();Description=Description;orderItems.ForEach;SetItems;}publicvoid Pay(){Status=OrderStatus.Payed;PaymentTime=DateTimeOffset.Now.ToUnixTimeMilliseconds();}publicvoid SetItems{OrderItems=OrderItems;}}订单行为通过这种方式,我们将一些行为赋给domain=null){Order.SetItems;}returnorder;}域仓库实现域中定义的接口pub

ChuanGoing 2019-11-11 

   距离上篇近两个月时间,一方面时因为其他事情耽搁,另一方面也是之前准备不足,关于领域驱动有几个地方没有想通透,也就没有继续码字。目前网络包括园子里大多领域驱动设计的文章,关于仓储者一层都没有详细的说明,只是简单的一笔带过:领域驱动不关心具体的持久化如何落地。但是,作为"猿人类"就不可避免的绕不开持久化。本篇将会简略的介绍利用Dapper这个轻量级的ORM来实现如何持久化。

本篇学习曲线:

1.领域模型

2.领域仓储

3.简单服务实现

领域模型设计

  我这里用常见的电商业务模型来介绍领域模型的设计,因篇幅有限,这里主要介绍订单与订单明细模型,基于如下业务:

1.创建订单时生成订单号、计算商品的总价同时生成订单明细。

2.根据订单号获取订单信息

  我的这个例子基本上涵盖了领域模型的主要几种业务组合:领域-实体-值对象,这几个基本概念这里不做赘述,园子里一搜一大堆。

Asp.net Core 系列之--3.领域、仓储、服务简单实现第1张Asp.net Core 系列之--3.领域、仓储、服务简单实现第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; }
    }
订单
Asp.net Core 系列之--3.领域、仓储、服务简单实现第3张Asp.net Core 系列之--3.领域、仓储、服务简单实现第4张
 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,也就是通常所说的贫血模型。因此,为了赋予模型活力,我们需要为其添加某些行为:

Asp.net Core 系列之--3.领域、仓储、服务简单实现第5张Asp.net Core 系列之--3.领域、仓储、服务简单实现第6张
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数据库各类操作介绍的仓储示例,这里为领域模型单独设计了"领域仓储层"

Asp.net Core 系列之--3.领域、仓储、服务简单实现第7张Asp.net Core 系列之--3.领域、仓储、服务简单实现第8张
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中定义的接口

Asp.net Core 系列之--3.领域、仓储、服务简单实现第9张Asp.net Core 系列之--3.领域、仓储、服务简单实现第10张
 public interface IOrderRepository : IRepository<Order, Guid>, IScopeInstance
    {
        Order GetBySn(string sn);
    }
订单仓储接口定义

值得注意的时,领域仓储层这里起到了承上启下的作用,隔离了领域对于持久化层的直接依赖。

简单服务实现

    接下来,我们实现如何在业务服务层调用领域仓储实现数据的持久化。新建Application项目:

Asp.net Core 系列之--3.领域、仓储、服务简单实现第11张

 定义订单接口服务

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测试下创建和获取订单信息

Asp.net Core 系列之--3.领域、仓储、服务简单实现第12张

订单参数如上

 Asp.net Core 系列之--3.领域、仓储、服务简单实现第13张

调试信息-订单创建

 Asp.net Core 系列之--3.领域、仓储、服务简单实现第14张

根据订单编号,返回订单信息

回顾

  回顾一下本篇内容,分别介绍了:领域模型、领域仓储、简单服务的实现,然后利用postman模拟http请求演示了数据的创建与获取。

  本篇只是简单的介绍了领域服务及相关概念,后面有机会再做详细讨论,下篇将介绍日志、鉴权、错误及事物处理。

代码

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

免责声明:文章转载自《Asp.net Core 系列之--3.领域、仓储、服务简单实现》仅用于学习参考。如对内容有疑问,请及时联系本站处理。

上篇win7怎样开启loopback接口(环回网卡)命令行方式实现QQ自动登录下篇

宿迁高防,2C2G15M,22元/月;香港BGP,2C5G5M,25元/月 雨云优惠码:MjYwNzM=

相关文章

架构系列:逻辑分层总结

概述: 将业务逻辑层独立出来是逻辑架构分层的基础,而将应用逻辑从业务逻辑层中分离出来是服务层(应用层)的基础。高内聚低耦合是分层依赖的基础,因此合理的划分层次,减少层级依赖是逻辑分层架构的核心。 一.基础知识准备: 1.层的原则: (1)每一层以接口方式供上层调用。 (2)上层只能调用下层。 (3)依赖分为松散交互和严格交互两种。 2.业务逻辑分...

[Architecture Design] DDD经验分享 (下)

接续... [Architecture Design] DDD经验分享 (中) 系统设计阶段 (SD) 「系统设计阶段」主要的工作是对设计完成的系统架构,做每个功能模块的对象设计。一般会采UML的「类别图」、「循序图」等等工具,来完成系统设计的工作。最终将设计完毕的解决方案,整理成一份「系统设计规格书」。如果说需求分析阶段是建立骨架,那么系统设计阶段就是填...

DDD领域模型企业级系统(二)

用户层: 1.请求应用层获取用户显示的信息 2.发送命令给应用层要求执行某个命令 应用层: 对用户界面提供各种应用功能(包括信息获取与命令执行),应用层不包含业务逻辑,业务层是由应用层调用领域层(领域对象或领域服务)来完成的,应用层是很薄的一层 领域层: 包含领域对象和领域服务,完成系统所需的业务处理,是系统的核心。业务逻辑与仓储接口都在领域层 基础机构层...

分析模式(可复用的对象模型)- 读书笔记

读后感: Martin Fowler 20年前的书,OO和领域的思想对于今天的我们来说很基础,但在那时应该算是萌芽。Smalltalk语言简单,语法中省略空格可能因为那时的硬件设备昂贵,而不得不做出的选择,但是可读性真的很差,而书中基本是用Smalltalk进行示例。翻开这本书是为了查找财务模型,它没有让我失望,特别是第6章“库存与财务”给了我思维建模的...

DDD从入门到精通:基础篇

这篇文章主要还是表述清楚DDD相关的基础概念,因为DDD入门有一定的专业名词,还是得有个基本的了解。 先讲解下领域模型作用: 对软件需求进行设计,维持其内在逻辑的一致性 1)划分边界、也是一种高内聚、低耦合 2)所有逻辑都是内聚在对象内部的【包含行为和数据】   为什么需要DDD? 行业现状:   贫血模型、充血模型   领域模型就是DDD? 其实领域...

再论 Java 应用中的“领域建模”

再论 Java 应用中的“领域建模”转载请保留作者信息: 作者:88250 Blog:http:/blog.csdn.net/DL88250 MSN & Gmail & QQ:DL88250@gmail.com 再论 Java 应用中的“领域建模” 相关术语与概念 POJO(Plain Old Java Object) 领域模型(...