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(-).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分支可以找到。

Asp.net Core 系列之--3.领域、仓储、服务简单实现的更多相关文章

  1. asp.net core 系列之webapi集成Dapper的简单操作教程

    Dapper也是是一种ORM框架 这里记录下,使用ASP.NET 集成 Dapper 的过程,方便自己查看 至于Dapper的特性以及操作可以参考Dapper官方文档 1.创建数据库相关 在Sql S ...

  2. asp.net core 系列之webapi集成EFCore的简单操作教程

    因为官网asp.net core webapi教程部分,给出的是使用内存中的数据即 UseInMemoryDatabase 的方式, 这里记录一下,使用SQL Server数据库的方式即 UseSql ...

  3. Asp.net Core 系列之--5.认证、授权与自定义权限的实现

    ChuanGoing 2019-11-24 asp.net core系列已经来到了第五篇,通过之前的基础介绍,我们了解了事件订阅/发布的eventbus整个流程,初探dapper ORM实现,并且简单 ...

  4. Asp.net Core 系列之--2.ORM初探:Dapper实现MySql数据库各类操作

    ChuanGoing 2019-09-10 距离上一篇近一个月时间,断断续续才把本篇码完,后面将加快进度,争取年度内把本系列基本介绍完成,同时督促本人持续学习. 本篇学习曲线: 1.初识Dapper ...

  5. Asp.net Core 系列之--4.事务、日志及错误处理

    ChuanGoing 2019-11-17 这篇原本时想把事务处理.日志处理.错误处理.授权于鉴权一并介绍完的,授权和鉴权我想结合自定义权限来介绍,全部放到这里篇幅可能太长,因此权限部分将会在下篇来介 ...

  6. 【目录】asp.net core系列篇

    随笔分类 - asp.net core系列篇 asp.net core系列 68 Filter管道过滤器 摘要: 一.概述 本篇详细了解一下asp.net core filters,filter叫&q ...

  7. 1.1专题介绍「深入浅出ASP.NET Core系列」

    大家好,我是IT人张飞洪,专注于.NET平台十年有余. 工作之余喜欢阅读和写作,学习的内容包括数据结构/算法.网络技术.Linux系统原理.数据库技术原理,设计模式.前沿架构.微服务.容器技术等等…… ...

  8. asp.net core系列 30 EF管理数据库架构--必备知识 迁移

    一.管理数据库架构概述 EF Core 提供两种主要方法来保持 EF Core 模型和数据库架构同步.一是以 EF Core 模型为基准,二是以数据库为基准. (1)如果希望以 EF Core 模型为 ...

  9. asp.net core系列 40 Web 应用MVC 介绍与详细示例

    一. MVC介绍 MVC架构模式有助于实现关注点分离.视图和控制器均依赖于模型. 但是,模型既不依赖于视图,也不依赖于控制器. 这是分离的一个关键优势. 这种分离允许模型独立于可视化展示进行构建和测试 ...

随机推荐

  1. mysql 生成数据字典sql语句

    SELECT TABLE_SCHEMA, TABLE_NAME, COLUMN_NAME, COLUMN_TYPE, COLUMN_COMMENT FROM information_schema. C ...

  2. Linux下mysql相关操作

    Linux下mysql相关操作 1.创建MySQL mysql -u root -p create user 'username'@'%' identified by 'password'; %可以选 ...

  3. 给老师安排课表JAVA项目及登录窗口的实现

    实现一个安排课表的Java实验. 有以下几点要求: ①用所给的教师姓名进行课表安排 ②用所给的地点进行课表安排 ③不得有重复的课程名称出现 ④将信息写入到文件里 ⑤用窗口来进行实现 package c ...

  4. NOIP_TG

    本博客主要记录一些在刷题的途中遇到的一些巧妙的题目 砝码称重 一开始想到可以DP递推标记能凑成的数量 但发现同一种砝码可能有多个于是想多开一维状态存当前还剩多少砝码 真是愚蠢至极 直接把所有砝码单独看 ...

  5. Web前端学习,需用了解的7大HTML知识点

    HTML是web前端开发基础,关于HTML,这里有几个很重要的知识点,在日常开发常常用到,并且在大家面试的时候也会问的,记住这7个重要知识点,助你在面试时优先录用. 1.网页结构 网页结构一般都包含文 ...

  6. Lombok中关于@Data的使用

    当你在使用 Lombok 的 @Data 注解时,其实会有一些坑需要关注,今天就让我们来见识一下. Lombok 先来简单介绍一下 Lombok ,其官方介绍如下: Project Lombok ma ...

  7. PHP array_change_key_case

    (PHP 4 >= 4.2.0, PHP 5, PHP 7) 1.函数的作用 : 改变数组所有键值的大小写: 2.参数: 1)array : 应用的数组: 2)case  : 指定转换为大写或者 ...

  8. [Noip1997] 棋盘问题(2)

    题目描述 在N×NN \times NN×N的棋盘上(1≤N≤10)(1≤N≤10)(1≤N≤10),填入1,2,…,N21,2,…,N^21,2,…,N2共N2N^2N2个数,使得任意两个相邻的数之 ...

  9. Maven安装和在IDEA配置Maven

    一.Windows安装Maven 1.下载Maven 这里需要注意:不要去官网下载最新的版本,因为会出现与IDEA不兼容的现象. 这里提供下载地址:https://archive.apache.org ...

  10. asp.net core 设置默认文档index.html

    参考:https://jingyan.baidu.com/article/6079ad0e3e212168fe86db75.html 在Startup.cs的Configure添加 app.UseFi ...