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. SpringBoot系列:Spring Boot使用模板引擎Thymeleaf

    一.Java模板引擎 模板引擎(这里特指用于Web开发的模板引擎)是为了使用户界面与业务数据(内容)分离而产生的,它可以生成特定格式的文档,用于网站的模板引擎就会生成一个标准的HTML文档. 在jav ...

  2. ASP.NET Web API 2系列(三):查看WebAPI接口的详细说明及测试接口

    引言 前边两篇博客介绍了Web API的基本框架以及路由配置,这篇博客主要解决在前后端分离项目中,为前端人员提供详细接口说明的问题,主要是通过修改WebApi HelpPage相关代码和添加WebAp ...

  3. Azure pipeline 配置根据条件执行脚本

    Azure pipeline 配置根据条件执行脚本 Intro 我的应用通过 azure pipeline 来做持续集成,之前已经介绍了根据不同分支去打包不同的package,具体的就不再这里详细介绍 ...

  4. DOM操作方法、属性

    话不多说直接上demo: <!DOCTYPE html> <html lang="en"> <head> <meta charset=&q ...

  5. Vijos 1067守望者的烦恼

    背景 守望者-warden,长期在暗夜精灵的的首都艾萨琳内担任视察监狱的任务,监狱是成长条行的,守望者warden拥有一个技能名叫“闪烁”,这个技能可以把她传送到后面的监狱内查看,她比较懒,一般不查看 ...

  6. 如何在Java中创建数组列表

    为了在Java中存储动态大小的元素,我们使用了ArrayList.每当添加新元素时,它会自动增加它们的大小.ArrayList实现Java的List接口和Java的Collection的一部分. 由于 ...

  7. 【Python3爬虫】我爬取了七万条弹幕,看看RNG和SKT打得怎么样

    一.写在前面 直播行业已经火热几年了,几个大平台也有了各自独特的“弹幕文化”,不过现在很多平台直播比赛时的弹幕都基本没法看的,主要是因为网络上的喷子还是挺多的,尤其是在观看比赛的时候,很多弹幕不是喷选 ...

  8. Jackson中@JsonProperty等常用注解

    Java生态圈中有很多处理JSON和XML格式化的类库,Jackson是其中比较著名的一个.虽然JDK自带了XML处理类库,但是相对来说比较低级 本文将介绍的Jackson常用注解:精简概述 Jack ...

  9. 浅谈celery的坑

    celery celery的使用以及在Django中的配置,不详细介绍,主要记录在Django中使用的坑点. 坑点 时区问题 celery默认的时区是世界标准时间,比东八区慢了8个小时,如果发布定时任 ...

  10. go-go协程

    package main import ( "fmt" "runtime" "time" ) func test() { defer fmt ...