本系列所有文章

如何一步一步用DDD设计一个电商网站(一)—— 先理解核心概念

如何一步一步用DDD设计一个电商网站(二)—— 项目架构

如何一步一步用DDD设计一个电商网站(三)—— 初涉核心域

如何一步一步用DDD设计一个电商网站(四)—— 把商品卖给用户

如何一步一步用DDD设计一个电商网站(五)—— 停下脚步,重新出发

如何一步一步用DDD设计一个电商网站(六)—— 给购物车加点料,集成售价上下文

如何一步一步用DDD设计一个电商网站(七)—— 实现售价上下文

如何一步一步用DDD设计一个电商网站(八)—— 会员价的集成

如何一步一步用DDD设计一个电商网站(九)—— 小心陷入值对象持久化的坑

如何一步一步用DDD设计一个电商网站(十)—— 一个完整的购物车

如何一步一步用DDD设计一个电商网站(十一)—— 最后的准备

如何一步一步用DDD设计一个电商网站(十二)—— 提交并生成订单

如何一步一步用DDD设计一个电商网站(十三)—— 领域事件扩展

 

阅读目录

一、前言

  之前的文章中已经涉及到了购买商品加入购物车,购物车内购物项的金额计算等功能。本篇准备把剩下的购物车的基本概念一次处理完。

二、回顾

  在动手之前我对之前的购买上下文内对象做了一次回顾。先梳理一下已经在上下文内出现的领域对象,如图1所示:

                          【图1】

  在梳理的过程中,我把原来Cart.AddCartItem(string productId, int quantity, decimal price)重构为了Cart.AddCartItem(Product product, int quantity),这样的好处的是2个:

  1.更清晰的表述出了在购物车中添加商品的意思。

  2.约束了外部只能通过Product对象来进行商品的添加,这样在Product构造函数中的约束在这里无需再次验证(如salename不能空等)。

三、梳理

  目前的购物车中在操作上的方法只有一个。参照目前主流电商平台的设计,我们需要增加:

  1.修改数量

  2.删除

  3.选择参与的促销(如果存在多个非单品级促销)

  4.收藏商品

  前面3个比较简单,都是购物车自身的概念,只有其中第四点超出了购物车自身的范畴,并且笔者认为收藏本就不是购物车特有的概念,而是在任何看得到商品的地方都可以做添加收藏的操作。那么自然引出了一个新的概念——收藏夹。看下最新的UML图,如图2所示:

                          【图2】

  我想会有一部分同学在设计收藏夹(Favorites)的时候会以另外的方式来做,比如像下图3这样:

                          【图3】

  这里我认为这样考虑的原因可能是由于DBFirst的思想导致的,因为图2中的“收藏夹”仅仅是维护了一个“用户”与“收藏项”之间的关系,那么只要在“收藏项”上增加一个UserId就直接可以省去了这一层关系,并且数据结构更加简单。这时候我们就需要注意了,千万不能有DBFirst思想去影响领域的建模,这样的方式会把“添加购物项”这类的业务含义泄露到了Repository层或者Application层去实现,导致无法用通用语言进行完整的业务描述了。

  并且在这个场景下,我个人观点认为,收藏商品其实只是为商品的展示途径中增加了一种途径而已,所以它应该被设计为独立存在的,由它自身来管理这些“被收藏的商品”,它的存在与否都不影响其它领域对象。

四、实现

  要实现这4个操作,那么需要在ICartService中增加下面4个接口:

        Result ChangeQuantity(string userId, string id, int quantity);

        Result DeleteCartItem(string userId, string id);

        Result AddToFavorites(string userId, string productId);

        Result ChangeMultiProductsPromotion(string userId, string productId, string selectedMultiProductsPromotionId);

  其中的部分实现如下:

        public Result AddToFavorites(string userId, string productId)
{
var cart = _confirmUserCartExistedDomainService.GetUserCart(userId); if (cart.IsEmpty())
{
return Result.Fail("当前购物车中并没有商品");
} var cartItem = cart.GetCartItem(productId);
if (cartItem == null)
{
return Result.Fail("该购物项已不存在");
} var favorites = DomainRegistry.FavoritesRepository().GetByUserId(userId) ?? new Favorites(userId, null);
favorites.AddFavoritesItem(cartItem);
DomainRegistry.FavoritesRepository().Save(favorites);
return Result.Success();
}

  其中关于Favorites的构造函数我是这么做的:

        public Favorites(string userId, IEnumerable<FavoritesItem> favoritesItems)
{
if (string.IsNullOrWhiteSpace(userId))
throw new ArgumentNullException("userId");
this.UserId = userId;
this._favoritesItems = new List<FavoritesItem>(); if (favoritesItems != null && favoritesItems.Any())
{
foreach (var favoritesItem in favoritesItems)
{
AddFavoritesItem(favoritesItem);
}
}
}

  这样可以重用AddFavoritesItem中的一些守卫操作,保证在业务产生变动之后历史数据从DB取出来的时候经过一次最新的业务验证,确保数据在流转过程中的合法性。这个方式可以择机运用在任何聚合的构造函数中。

五、结语

  本篇主要的观点还是在建模上的思维惯性,抛开DB,抛开DB,抛开DB,重要的事情说3遍。

  

本文的源码地址:https://github.com/ZacharyFan/DDDDemo/tree/Demo10

作者:Zachary
出处:https://zacharyfan.com/archives/180.html

▶关于作者:张帆(Zachary,个人微信号:Zachary-ZF)。坚持用心打磨每一篇高质量原创。欢迎扫描右侧的二维码~。

定期发表原创内容:架构设计丨分布式系统丨产品丨运营丨一些思考。

如果你是初级程序员,想提升但不知道如何下手。又或者做程序员多年,陷入了一些瓶颈想拓宽一下视野。欢迎关注我的公众号「跨界架构师」,回复「技术」,送你一份我长期收集和整理的思维导图。

如果你是运营,面对不断变化的市场束手无策。又或者想了解主流的运营策略,以丰富自己的“仓库”。欢迎关注我的公众号「跨界架构师」,回复「运营」,送你一份我长期收集和整理的思维导图。

如何一步一步用DDD设计一个电商网站(十)—— 一个完整的购物车的更多相关文章

  1. 如何一步一步用DDD设计一个电商网站(六)—— 给购物车加点料,集成售价上下文

    阅读目录 前言 如何在一个项目中实现多个上下文的业务 售价上下文与购买上下文的集成 结语 一.前言 前几篇已经实现了一个最简单的购买过程,这次开始往这个过程中增加一些东西.比如促销.会员价等,在我们的 ...

  2. 如何一步一步用DDD设计一个电商网站(九)—— 小心陷入值对象持久化的坑

    阅读目录 前言 场景1的思考 场景2的思考 避坑方式 实践 结语 一.前言 在上一篇中(如何一步一步用DDD设计一个电商网站(八)—— 会员价的集成),有一行注释的代码: public interfa ...

  3. 如何一步一步用DDD设计一个电商网站(八)—— 会员价的集成

    阅读目录 前言 建模 实现 结语 一.前言 前面几篇已经实现了一个基本的购买+售价计算的过程,这次再让售价丰满一些,增加一个会员价的概念.会员价在现在的主流电商中,是一个不大常见的模式,其带来的问题是 ...

  4. 如何一步一步用DDD设计一个电商网站(七)—— 实现售价上下文

    阅读目录 前言 明确业务细节 建模 实现 结语 一.前言 上一篇我们已经确立的购买上下文和销售上下文的交互方式,传送门在此:http://www.cnblogs.com/Zachary-Fan/p/D ...

  5. 如何一步一步用DDD设计一个电商网站(五)—— 停下脚步,重新出发

    阅读目录 前言 单元测试 纠正错误,重新出发 结语 一.前言 实际编码已经写了2篇了,在这过程中非常感谢有听到观点不同的声音,借着这个契机,今天这篇就把大家提出的建议一个个的过一遍,重新整理,重新出发 ...

  6. 如何一步一步用DDD设计一个电商网站(四)—— 把商品卖给用户

    阅读目录 前言 怎么卖 领域服务的使用 回到现实 结语 一.前言 上篇中我们讲述了“把商品卖给用户”中的商品和用户的初步设计.现在把剩余的“卖”这个动作给做了.这里提醒一下,正常情况下,我们的每一步业 ...

  7. 如何一步一步用DDD设计一个电商网站(十一)—— 最后的准备

     阅读目录 前言 准备 实现 结语 一.前言 最近实在太忙,上周停更了一周.按流程一步一步走到现在,到达了整个下单流程的最后一公里——结算页的处理.从整个流程来看,这里需要用户填写的信息是最多的,那么 ...

  8. 如何一步一步用DDD设计一个电商网站(十二)—— 提交并生成订单

    阅读目录 前言 解决数据一致性的方案 回到DDD 设计 实现 结语 一.前言 之前的十一篇把用户购买商品并提交订单整个流程上的中间环节都过了一遍.现在来到了这最后一个环节,提交订单.单从业务上看,这个 ...

  9. 如何一步一步用DDD设计一个电商网站(十三)—— 领域事件扩展

    阅读目录 前言 回顾 本地的一致性 领域事件发布出现异常 订阅者处理出现异常 结语 一.前言 上篇中我们初步运用了领域事件,其中还有一些问题我们没有解决,所以实现是不健壮的,下面先来回顾一下. 二.回 ...

随机推荐

  1. js学习笔记:webpack基础入门(一)

    之前听说过webpack,今天想正式的接触一下,先跟着webpack的官方用户指南走: 在这里有: 如何安装webpack 如何使用webpack 如何使用loader 如何使用webpack的开发者 ...

  2. jQuery之ajax实现篇

    jQuery的ajax方法非常好用,这么好的东西,你想拥有一个属于自己的ajax么?接下来,我们来自己做一个简单的ajax吧. 实现功能 由于jq中的ajax方法是用了内置的deferred模块,是P ...

  3. SignalR系列续集[系列8:SignalR的性能监测与服务器的负载测试]

    目录 SignalR系列目录 前言 也是好久没写博客了,近期确实很忙,嗯..几个项目..头要炸..今天忙里偷闲.继续我们的小系列.. 先谢谢大家的支持.. 我们来聊聊SignalR的性能监测与服务器的 ...

  4. XStream将java对象转换为xml时,对象字段中的下划线“_”,转换后变成了两个的解决办法

            在前几天的一个项目中,由于数据库字段的命名原因 其中有两项:一项叫做"市场价格"一项叫做"商店价格" 为了便于区分,遂分别将其命名为market ...

  5. 客户端的验证插件validator

    简单,智能,令人愉悦的表单验证~~~ 官方文档:http://www.niceue.com/validator/ <!DOCTYPE html> <html> <head ...

  6. netty5 HTTP协议栈浅析与实践

      一.说在前面的话 前段时间,工作上需要做一个针对视频质量的统计分析系统,各端(PC端.移动端和 WEB端)将视频质量数据放在一个 HTTP 请求中上报到服务器,服务器对数据进行解析.分拣后从不同的 ...

  7. Android权限管理之RxPermission解决Android 6.0 适配问题

    前言: 上篇重点学习了Android 6.0的运行时权限,今天还是围绕着Android 6.0权限适配来总结学习,这里主要介绍一下我们公司解决Android 6.0权限适配的方案:RxJava+RxP ...

  8. SQL Server-表表达式基础回顾(二十四)

    前言 从这一节开始我们开始进入表表达式章节的学习,Microsoft SQL Server支持4种类型的表表达式:派生表.公用表表达式(CTE).视图.内嵌表值函数(TVF).简短的内容,深入的理解, ...

  9. Hawk 5. 数据库系统

    Hawk在设计之初,就是以弱schema风格定义的.没有严格的列名和列属性.用C#这样的静态强类型语言编写Hawk,其实并不方便.但弱schema让Hawk变得更灵活更强大. 因此,Hawk虽然之前支 ...

  10. zookeeper源码分析之三客户端发送请求流程

    znode 可以被监控,包括这个目录节点中存储的数据的修改,子节点目录的变化等,一旦变化可以通知设置监控的客户端,这个功能是zookeeper对于应用最重要的特性,通过这个特性可以实现的功能包括配置的 ...