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. zend studio 13.6 导入项目及其他设置

    1. 先创建一个新的项目:file -> new -> project 2. 创建新项目之后,在左侧的项目目录上右键 -> import 到此导入项目, 完成! 设置编码utf-8 ...

  2. B/S 工业互联网 地铁行业

    前言 近几年,互联网与交通运输的融合,改变了交易模式,影响着运输组织和经营方式,改变了运输主体的市场结构.模糊了运营与非营运的界限,也更好的实现了交通资源的集约共享,同时使得更多依靠外力和企业推动交通 ...

  3. Xshell、Xftp 5、6 解决“要继续使用此程序,您必须应用最新的更新或使用新版本”

    今天打开Xshell.Xftp,突然弹出“要继续使用此程序,您必须应用最新的更新或使用新版本”. 后来经过一番搜索发现,XShell配置文件中写入了强制升级时间,这个版本是2017年12月27日发布的 ...

  4. MySql + Workbench使用教程

    Mysql安装及使用 注意:不推荐下载zip版本,需要配置额外的环境变量和其他设置,很复杂.官方的windows安装版本可以自动完成所有操作. 下载地址:https://dev.mysql.com/d ...

  5. X-扫描线算法

    多边形的扫描转换 多边形有两种重要的表示方法:顶点表示和点阵表示 顶点表示是用多边形的顶点序列来表示多边形.这种表示直观.几何意义强.占内存少,易于进行几何变换. 但由于它没有明确指出哪些象素在多边形 ...

  6. Github桌面端安装慢问题

    失败截图: 原因是某些域名被墙,所以这里想安装快的话还是得翻墙.如何翻墙请自行百度. 如果翻墙还是很慢,请参考网上大神文章:https://www.yaozeyuan.online/2015/10/0 ...

  7. ESP8266开发之旅 网络篇⑪ WebServer——ESP8266WebServer库的使用

    授人以鱼不如授人以渔,目的不是为了教会你具体项目开发,而是学会学习的能力.希望大家分享给你周边需要的朋友或者同学,说不定大神成长之路有博哥的奠基石... QQ技术互动交流群:ESP8266&3 ...

  8. textView 实现完成收键盘操作

    -(BOOL)textView:(UITextView *)textView shouldChangeTextInRange:(NSRange)range replacementText:(NSStr ...

  9. NOIP2018提高组初赛游记

    AH省的,好像水军多,走的都比较早(莫非是真·大佬!!) 本人考了71,较去年退步了.(去年还考80多的来着) 题目坑.. 第一.二大题选择 第三题年份,看了试卷标题,第二十二届,算出来后没有这个选项 ...

  10. ElasticSearch - ElasticSearch和kinaba的简单使用

    ElasticSearch和kinaba的简单使用 ElasticSeatch 文档推荐 ElasticSearch 下载 (端口 9200) 安装好es,可以访问 http://localhost: ...