[.NET领域驱动设计实战系列]专题六:DDD实践案例:网上书店订单功能的实现
一、引言
上一专题已经为网上书店实现了购物车的功能了,在这一专题中,将继续对网上书店案例进行完善,本专题将对网上书店订单功能的实现进行介绍,现在废话不多说了,让我们来一起看看订单功能是如何实现的吧。
二、订单功能的实现思路
在网上购过物的朋友,对于订单功能的流程自然不陌生,这里我还是先来梳理下下订单的一个流程:
- 用户点击我的购物车,可以勾选对应的商品进行结算
- 在结算页面可以提交订单来创建一个订单
- 创建订单成功之后就是进行付款了。
一般购物网站下订单的流程分上面3步,由于在本案例中并没有对接第三方的支付平台,所以这里就没有上面第三步的过程了。即在网上书店案例中订单提交成功之后就是已付款状态。
从上面下订单流程我们就可以知道订单功能的实现思路:
- 用户点击购物车中购买商品按钮来进行下订单,此时触发控制器中的结算方法进行调用
- 结算方法通过调用OrderService服务中的结算方法
- 由于订单的创建涉及了3个实体操作,包括购物车实体,购物车项实体和订单实体。所以这里需要引入领域服务。因为创建订单这个操作涉及了多个实体,则这个业务逻辑放在每个实体中都不合适,因为并属于单独一个实体的逻辑。所以这里引入领域服务来实现这种涉及多个实体的操作。
- 则OrderService服务中的结算方法通过调用领域服务中的CreateOrder方法来完成订单创建的功能。
- 领域服务中可以调用对应的实体仓储来完成实体的持久化。这里需要注意的一点:因为领域服务涉及多个实体的持久化,则需要引入工作单元模式将这些实体的操作进行统一提交,要不都成功,要不都不成功。这也就是引入工作单元初衷。
上面的思路就是订单功能的实现思路。有了上面的思路之后,实现订单功能也一目了然了。下面让我们一起在网上书店案例中实现下看看。
三、网上书店订单功能的实现
这里我们按照上面的实现思路由下至上地去实现订单功能。
- 首先我们需要订单仓储来完成订单实体的持久化。具体订单仓储接口和实现如下代码所示:
// 订单仓储接口
public interface IOrderRepository : IRepository<Order>
{
} // 订单仓储的实现类
public class OrderRepository : EntityFrameworkRepository<Order>, IOrderRepository
{
public OrderRepository(IRepositoryContext context) : base(context)
{
}
}
2. 领域服务的实现。具体的领域服务接口和实现代码如下所示:
// 领域服务接口
public interface IDomainService
{
Order CreateOrder(User user, ShoppingCart shoppingCart);
} // 领域服务类型
// 牵涉到多个实体的操作可以把这些操作封装到领域服务里
public class DomainService : IDomainService
{
private readonly IRepositoryContext _repositoryContext;
private readonly IShoppingCartItemRepository _shoppingCartItemRepository;
private readonly IOrderRepository _orderRepository; /// <summary>
/// 创建订单,涉及到的操作有2个:1. 把购物车中的项中购物车移除; 2.创建一个订单。
/// 这两个操作必须同时完成或失败。
/// </summary>
/// <param name="user"></param>
/// <param name="shoppingCart"></param>
/// <returns></returns>
public Order CreateOrder(User user, ShoppingCart shoppingCart)
{
var order = new Order();
var shoppingCartItems =
_shoppingCartItemRepository.GetAll(
new ExpressionSpecification<ShoppingCartItem>(s => s.ShoopingCart.Id == shoppingCart.Id));
if (shoppingCartItems == null || !shoppingCartItems.Any())
throw new InvalidOperationException("购物篮中没有任何物品"); order.OrderItems = new List<OrderItem>();
foreach (var shoppingCartItem in shoppingCartItems)
{
var orderItem = shoppingCartItem.ConvertToOrderItem();
orderItem.Order = order;
order.OrderItems.Add(orderItem);
_shoppingCartItemRepository.Remove(shoppingCartItem);
}
order.User = user;
order.Status = OrderStatus.Paid;
_orderRepository.Add(order);
_repositoryContext.Commit();
return order;
}
}
3. 订单服务的实现。具体订单服务实现代码如下所示:
public class OrderServiceImp : ApplicationService, IOrderService
{
#region Private Fileds
private readonly IShoppingCartRepository _shoppingCartRepository;
private readonly IShoppingCartItemRepository _shoppingCartItemRepository;
private readonly IUserRepository _userRepository;
private readonly IOrderRepository _orderRepository;
private readonly IProductRepository _productRepository;
private readonly IDomainService _domainService;
private readonly IEventBus _eventBus;
#endregion #region Ctor
public OrderServiceImp(IRepositoryContext context,
IUserRepository userRepository,
IShoppingCartRepository shoppingCartRepository,
IProductRepository productRepository,
IShoppingCartItemRepository shoppingCartItemRepository,
IDomainService domainService,
IOrderRepository orderRepository,
IEventBus eventBus) : base(context)
{
_userRepository = userRepository;
_shoppingCartRepository = shoppingCartRepository;
_productRepository = productRepository;
_shoppingCartItemRepository = shoppingCartItemRepository;
_domainService = domainService;
_orderRepository = orderRepository;
_eventBus = eventBus;
} #endregion public OrderDto Checkout(Guid customerId)
{
var user = _userRepository.GetByKey(customerId);
var shoppingCart = _shoppingCartRepository.GetByExpression(s => s.User.Id == user.Id);
var order = _domainService.CreateOrder(user, shoppingCart); return Mapper.Map<Order, OrderDto>(order);
}
public OrderDto GetOrder(Guid orderId)
{
var order = _orderRepository.GetBySpecification(new ExpressionSpecification<Order>(o=>o.Id.Equals(orderId)), elp=>elp.OrderItems);
return Mapper.Map<Order, OrderDto>(order);
} // 获得指定用户的所有订单
public IList<OrderDto> GetOrdersForUser(Guid userId)
{
var user = _userRepository.GetByKey(userId);
var orders = _orderRepository.GetAll(new ExpressionSpecification<Order>(o => o.User.Id == userId), sp => sp.CreatedDate, SortOrder.Descending, elp=>elp.OrderItems);
var orderDtos = new List<OrderDto>();
orders
.ToList()
.ForEach(o=>orderDtos.Add(Mapper.Map<Order, OrderDto>(o)));
return orderDtos;
}
4. HomeController控制器中Checkout操作的实现。具体实现代码如下所示:
public class HomeController : ControllerBase
{
/// <summary>
/// 结算操作
/// </summary>
/// <returns></returns>
[Authorize]
public ActionResult Checkout()
{
using (var proxy = new OrderServiceClient())
{
var model = proxy.Checkout(this.UserId);
return View(model);
}
} [Authorize]
public ActionResult Orders()
{
using (var proxy = new OrderServiceClient())
{
var model = proxy.GetOrdersForUser(this.UserId);
return View(model);
}
} [Authorize]
public ActionResult Order(string id)
{
using (var proxy = new OrderServiceClient())
{
var model = proxy.GetOrder(new Guid(id));
return View(model);
}
}
}
这样我们就在网上书店中实现了订单功能了。具体的视图界面也就是上一专题中实现的购物车页面。下面具体看看订单的具体实现效果:
结算页面:
点击确认购买按钮,在弹出框中点击确认来完成订单的创建:
通过我的订单来查看所有订单页面:
四、总结
到此,网上书店案例的订单功能的实现就完成了,在接下来的专题将继续对该案例进行完善,在下一专题中将为该案例引入后台管理操作。商家或管理员可以进入后台管理来对用户订单进行确认发货,以及添加商品,分类等操作。具体实现请见下一专题。
本专题中所有实现源码下载:https://github.com/lizhi5753186/OnlineStore_Second/
[.NET领域驱动设计实战系列]专题六:DDD实践案例:网上书店订单功能的实现的更多相关文章
- [.NET领域驱动设计实战系列]专题十一:.NET 领域驱动设计实战系列总结
一.引用 其实在去年本人已经看过很多关于领域驱动设计的书籍了,包括Microsoft .NET企业级应用框架设计.领域驱动设计C# 2008实现.领域驱动设计:软件核心复杂性应对之道.实现领域驱动设计 ...
- [.NET领域驱动设计实战系列]专题二:结合领域驱动设计的面向服务架构来搭建网上书店
一.前言 在前面专题一中,我已经介绍了我写这系列文章的初衷了.由于dax.net中的DDD框架和Byteart Retail案例并没有对其形成过程做一步步分析,而是把整个DDD的实现案例展现给我们,这 ...
- [.NET领域驱动设计实战系列]专题一:前期准备之EF CodeFirst
一.前言 从去年已经接触领域驱动设计(Domain-Driven Design)了,当时就想自己搭建一个DDD框架,所以当时看了很多DDD方面的书,例如领域驱动模式与实战,领域驱动设计:软件核心复杂性 ...
- [.NET领域驱动设计实战系列]专题十:DDD扩展内容:全面剖析CQRS模式实现
一.引言 前面介绍的所有专题都是基于经典的领域驱动实现的,然而,领域驱动除了经典的实现外,还可以基于CQRS模式来进行实现.本专题将全面剖析如何基于CQRS模式(Command Query Respo ...
- [.NET领域驱动设计实战系列]专题四:前期准备之工作单元模式(Unit Of Work)
一.前言 在前一专题中介绍了规约模式的实现,然后在仓储实现中,经常会涉及工作单元模式的实现.然而,在我的网上书店案例中也将引入工作单元模式,所以本专题将详细介绍下该模式,为后面案例的实现做一个铺垫. ...
- [.NET领域驱动设计实战系列]专题八:DDD案例:网上书店分布式消息队列和分布式缓存的实现
一.引言 在上一专题中,商家发货和用户确认收货功能引入了消息队列来实现的,引入消息队列的好处可以保证消息的顺序处理,并且具有良好的可扩展性.但是上一专题消息队列是基于内存中队列对象来实现,这样实现有一 ...
- [.NET领域驱动设计实战系列]专题七:DDD实践案例:引入事件驱动与中间件机制来实现后台管理功能
一.引言 在当前的电子商务平台中,用户下完订单之后,然后店家会在后台看到客户下的订单,然后店家可以对客户的订单进行发货操作.此时客户会在自己的订单状态看到店家已经发货.从上面的业务逻辑可以看出,当用户 ...
- [.NET领域驱动设计实战系列]专题五:网上书店规约模式、工作单元模式的引入以及购物车的实现
一.前言 在前面2篇博文中,我分别介绍了规约模式和工作单元模式,有了前面2篇博文的铺垫之后,下面就具体看看如何把这两种模式引入到之前的网上书店案例里. 二.规约模式的引入 在第三专题我们已经详细介绍了 ...
- [.NET领域驱动设计实战系列]专题三:前期准备之规约模式(Specification Pattern)
一.前言 在专题二中已经应用DDD和SOA的思想简单构建了一个网上书店的网站,接下来的专题中将会对该网站补充更多的DDD的内容.本专题作为一个准备专题,因为在后面一个专题中将会网上书店中的仓储实现引入 ...
随机推荐
- bc:linux下命令行计算器
在linux下,存在一个命令行的计算器:bc.该程序一般随发行版发布. bc计算器能够执行一些基本的计算,包括+,-,×,\,%. 这些计算不经针对十进制,还可以使用二进制,八进制,十六进制,并且可以 ...
- docke部署mysql
#1 docker pull mysql #2 docker run -v /data/var/mysql/:/var/lib/mysql -e MYSQL_ROOT_PASSWORD=k ...
- android中将EditText改成不可编辑的状态
今天在做项目的时候,要想实现一个将EditText变成不可编辑的状态,通过查找博客,发现一个好方法,对于单独的EditText控件我们可以单独设置 1.首先想到在xml中设置Android:edita ...
- 「脑洞」图片转HTML(支持动画)
也许是受到很久以前看到的这玩意儿的原因:The Shapes of CSS 现在开脑洞写了个自动转换,顺便支持了动画……嗯,纯 CSS (:з」∠) 主要步骤就是用 Python 读图片,然后把像素全 ...
- WinForm开发之取送货管理2
写的有点慢,但都是一步步操作的,希望这些能成为以后宝贵的财富,话不多说,续上次取送货基本信息管理之产品分类管理,下面进行增删改的编写. 增加产品分类管理信息记录,双击[新增]按钮(其新增可让用户在Te ...
- 百度地图定位经纬度返回4.9E-324有关问题
1.查看你的应用是否有权限查看你的地理位置信息,有可能是你没有加上权限,或者当你第一次打开app时询问你是否给予软件权限查看你的地理位置信息,你选择了否,所以经纬度就一直返回4.9E-324 2.查看 ...
- angularjs 分页精华代码
//pageinfo $scope.pageSize=10;$scope.currentType={{ current_type }};$scope.currentPage={{ json_encod ...
- Windows 下 ffmpeg 转 mp4
最近在研究所有视频格式转 mp4 因为html5 只支持mov MP4 等格式..查阅了 很多资料发现 转成flv 很简单.. 可是要转 mp4 就难了... 经过我不屑的努力..终于转换成功了 ...
- MVC模式下向qq邮箱发送邮件
将已经保存在数据库中的密码通过邮件发送到qq邮箱中.用的ssm框架,其中的config文件要先配置好. 用到的jar包有gson-2.2.1.jar,gson.jar,mail.jar,activat ...
- JDBC小工具--TxQueryRunner及其单元测试
1.TxQueryRunner的简介(需要相关jar包的请留言) TxQueryRunner类是common-dbutils下QueryRunner的子类,是用来简化JDBC操作的,所以要导入comm ...