如何一步一步用DDD设计一个电商网站(八)—— 会员价的集成
本系列所有文章
如何一步一步用DDD设计一个电商网站(一)—— 先理解核心概念
如何一步一步用DDD设计一个电商网站(四)—— 把商品卖给用户
如何一步一步用DDD设计一个电商网站(五)—— 停下脚步,重新出发
如何一步一步用DDD设计一个电商网站(六)—— 给购物车加点料,集成售价上下文
如何一步一步用DDD设计一个电商网站(七)—— 实现售价上下文
如何一步一步用DDD设计一个电商网站(八)—— 会员价的集成
如何一步一步用DDD设计一个电商网站(九)—— 小心陷入值对象持久化的坑
如何一步一步用DDD设计一个电商网站(十)—— 一个完整的购物车
如何一步一步用DDD设计一个电商网站(十一)—— 最后的准备
如何一步一步用DDD设计一个电商网站(十二)—— 提交并生成订单
如何一步一步用DDD设计一个电商网站(十三)—— 领域事件扩展
阅读目录
一、前言
前面几篇已经实现了一个基本的购买+售价计算的过程,这次再让售价丰满一些,增加一个会员价的概念。会员价在现在的主流电商中,是一个不大常见的模式,其带来的问题是:
1.加大了运营的复杂度,会员价如何与促销结合,比如应在折前运用还是折后运用等。
2.如果是折前那么需要考虑满减类型促销的金额满足点门槛反而相对来说是提高了。
3.如果是折后那么享受了多重优惠,成本控制的时候需要考虑进去。
在我们这个练手的Demo中暂时决定让会员价在折后运用,并且仅在不满足满减促销的情况下才有效。
二、建模
那么开始先来建模,这次的会员价相对比较简单,一般就是一个打折率的问题。只要建立几个关系即可满足需求,如下:
会员与等级的关系(值对象):我认为等级的升级降级应该在“用户上下文”中处理,那么在这里的售价上下文中仅是对数据做的一个冗余,与“用户上下文”是一个最终一致性的关系。当然也可以不做这个冗余,从远程服务去获取,这可以根据实际情况来权衡。我认为用户等级的变化是一个非高频数据,所以在这里做冗余可以减少RPC次数。
等级与折扣的关系(值对象):这个数据应该是一旦确定就不大会变化了,并且会用于对外公示,毋庸置疑建立为值对象。如下图1所示:

【图1】
三、运用
先把上面定义的2个值对象数据来源确认一下,暂定把会员与等级的关系(UserRoleRelation)从用户上下文获取,因为我们还没开始引入最终一致性的概念;等级与折扣的关系(RoleDiscountRelation)存在本地上下文。那么这里第一次出现了在售价上下文中需要访问外部资源,我们也需要给其建立一个防腐层来处理这个RPC交互。既然如此和购买上下文一起,把防腐层放入到每个上下文的虚拟文件夹中,如下图2所示:

【图2】
下面的代码定义了这2个数据获取的接口:
public interface IUserService
{
UserRoleRelation GetUserRoleRelation(string userId);
}
public interface IRoleDiscountRelationRepository// : IRepository<RoleDiscountRelation>
{
RoleDiscountRelation Get(string roleId);
}
可以看到IRoleDiscountRelationRepository中有一行注释的代码,是因为这里需要把一个值对象独立的持久化到资源库中,在我们之前的设计中仅支持聚合根的持久化,所以此处先临时以手动定义的方式通过本篇的代码编写,会在下篇专门讲述如何处理这种情况。
然后由于计算会员价需要根据用户来计算,故要在CartRequest中增加UserId的参数,让购买上下文传递该数据才能保证这里的业务需要。
public class CartRequest
{
public string CartId { get; set; } public string UserId { get; set; } public CartItemRequest[] CartItems { get; set; }
}
会员价的计算是等级与折扣(值对象)的功能,可以在这个值对象中创建一个方法,目前来说里面的实现就是对传入的价格进行折扣金额的计算然后就返回。如下代码:
public decimal CalculateDiscountedPrice(decimal price)
{
return price * Convert.ToDecimal(this.DiscountRate);
}
然后我们开始把它和之前的促销业务结合起来。还记得我们之前的CalculateSalePriceService.Calculate(CartRequest cart)方法返回的数据结构吗(传送门:http://www.cnblogs.com/Zachary-Fan/p/DDD_7.html):
return new CalculatedCartDTO
{
CalculatedCartItems = boughtProducts.Where(ent => fullGroupDtos.SelectMany(e => e.CalculatedCartItems).All(e => e.ProductId != ent.ProductId))
.Select(ent => ent.ToDTO()).ToArray(),
CalculatedFullGroups = fullGroupDtos.ToArray(),
CartId = cart.CartId
};
我们只要把给CalculatedCartItems赋值的数据再进行计算会员价就好了,因为这些就是未参与满减促销的购物项。但是这里为了让BoughtProduct支持我们业务操作并且假设界面上需要展示会员价和促销价分别优惠了多少金额,故在BoughtProduct值对象中增加了一个ReducePriceByMemberPrice,用于存储由会员价所减免的金额。随后BoughtProduct中增加相应的设置会员价减免金额的方法,如下:
public BoughtProduct ChangeReducePriceByMemberPrice(decimal reducePriceByMemberPrice)
{
if (reducePriceByMemberPrice < )
throw new ArgumentException("reducePriceByMemberPrice不能小于0"); var selectedMultiProdcutsPromotionId = this.InMultiProductPromotionRule == null
? null
: ((PromotionRule)this.InMultiProductPromotionRule).PromotoinId;
return new BoughtProduct(this.ProductId, this.Quantity, this.UnitPrice, this.ReducePrice, reducePriceByMemberPrice, this._promotionRules, selectedMultiProdcutsPromotionId);
}
最后CalculateService调整为下图3这样:

【图3】
四、结语
可能写到中途有些枯燥,但是我想我的主题是运用DDD从0开始实现一个电商网站的过程,DDD中业务是核心,所以业务的细枝末节和DDD概念的运用必然都不能丢。
本文的源码地址:https://github.com/ZacharyFan/DDDDemo/tree/Demo8。
作者:Zachary
出处:https://zacharyfan.com/archives/159.html
▶关于作者:张帆(Zachary,个人微信号:Zachary-ZF)。坚持用心打磨每一篇高质量原创。欢迎扫描右侧的二维码~。
定期发表原创内容:架构设计丨分布式系统丨产品丨运营丨一些思考。
如果你是初级程序员,想提升但不知道如何下手。又或者做程序员多年,陷入了一些瓶颈想拓宽一下视野。欢迎关注我的公众号「跨界架构师」,回复「技术」,送你一份我长期收集和整理的思维导图。
如果你是运营,面对不断变化的市场束手无策。又或者想了解主流的运营策略,以丰富自己的“仓库”。欢迎关注我的公众号「跨界架构师」,回复「运营」,送你一份我长期收集和整理的思维导图。
如何一步一步用DDD设计一个电商网站(八)—— 会员价的集成的更多相关文章
- 如何一步一步用DDD设计一个电商网站(九)—— 小心陷入值对象持久化的坑
阅读目录 前言 场景1的思考 场景2的思考 避坑方式 实践 结语 一.前言 在上一篇中(如何一步一步用DDD设计一个电商网站(八)—— 会员价的集成),有一行注释的代码: public interfa ...
- 如何一步一步用DDD设计一个电商网站(十)—— 一个完整的购物车
阅读目录 前言 回顾 梳理 实现 结语 一.前言 之前的文章中已经涉及到了购买商品加入购物车,购物车内购物项的金额计算等功能.本篇准备把剩下的购物车的基本概念一次处理完. 二.回顾 在动手之前我对之 ...
- 如何一步一步用DDD设计一个电商网站(七)—— 实现售价上下文
阅读目录 前言 明确业务细节 建模 实现 结语 一.前言 上一篇我们已经确立的购买上下文和销售上下文的交互方式,传送门在此:http://www.cnblogs.com/Zachary-Fan/p/D ...
- 如何一步一步用DDD设计一个电商网站(六)—— 给购物车加点料,集成售价上下文
阅读目录 前言 如何在一个项目中实现多个上下文的业务 售价上下文与购买上下文的集成 结语 一.前言 前几篇已经实现了一个最简单的购买过程,这次开始往这个过程中增加一些东西.比如促销.会员价等,在我们的 ...
- 如何一步一步用DDD设计一个电商网站(五)—— 停下脚步,重新出发
阅读目录 前言 单元测试 纠正错误,重新出发 结语 一.前言 实际编码已经写了2篇了,在这过程中非常感谢有听到观点不同的声音,借着这个契机,今天这篇就把大家提出的建议一个个的过一遍,重新整理,重新出发 ...
- 如何一步一步用DDD设计一个电商网站(四)—— 把商品卖给用户
阅读目录 前言 怎么卖 领域服务的使用 回到现实 结语 一.前言 上篇中我们讲述了“把商品卖给用户”中的商品和用户的初步设计.现在把剩余的“卖”这个动作给做了.这里提醒一下,正常情况下,我们的每一步业 ...
- 如何一步一步用DDD设计一个电商网站(三)—— 初涉核心域
一.前言 结合我们本次系列的第一篇博文中提到的上下文映射图(传送门:如何一步一步用DDD设计一个电商网站(一)—— 先理解核心概念),得知我们这个电商网站的核心域就是销售子域.因为电子商务是以信息网络 ...
- 如何一步一步用DDD设计一个电商网站(十一)—— 最后的准备
阅读目录 前言 准备 实现 结语 一.前言 最近实在太忙,上周停更了一周.按流程一步一步走到现在,到达了整个下单流程的最后一公里——结算页的处理.从整个流程来看,这里需要用户填写的信息是最多的,那么 ...
- 如何一步一步用DDD设计一个电商网站(十二)—— 提交并生成订单
阅读目录 前言 解决数据一致性的方案 回到DDD 设计 实现 结语 一.前言 之前的十一篇把用户购买商品并提交订单整个流程上的中间环节都过了一遍.现在来到了这最后一个环节,提交订单.单从业务上看,这个 ...
随机推荐
- Restful资源文章
理解RESTful架构 RESTful API设计指南 RESTful架构详解 NodeJs的RESTful API
- spark处理大规模语料库统计词汇
最近迷上了spark,写一个专门处理语料库生成词库的项目拿来练练手, github地址:https://github.com/LiuRoy/spark_splitter.代码实现参考wordmaker ...
- 【Machine Learning】机器学习及其基础概念简介
机器学习及其基础概念简介 作者:白宁超 2016年12月23日21:24:51 摘要:随着机器学习和深度学习的热潮,各种图书层出不穷.然而多数是基础理论知识介绍,缺乏实现的深入理解.本系列文章是作者结 ...
- 数据的双向绑定 Angular JS
接触AngularJS许了,时常问自己一些问题,如果是我实现它,会在哪些方面选择跟它相同的道路,哪些方面不同.为此,记录了一些思考,给自己回顾,也供他人参考. 初步大致有以下几个方面: 数据双向绑定 ...
- 火星坐标、百度坐标、WGS-84坐标相互转换及墨卡托投影坐标转经纬度JavaScript版
火星坐标 火星坐标是国家测绘局为了国家安全在原始坐标的基础上进行偏移得到的坐标,基本国内的电子地图.导航设备都是采用的这一坐标系或在这一坐标的基础上进行二次加密得到的.火星坐标的真实名称应该是GCJ- ...
- 程序员必须要知道的Hadoop的一些事实
程序员必须要知道的Hadoop的一些事实.现如今,Apache Hadoop已经无人不知无人不晓.当年雅虎搜索工程师Doug Cutting开发出这个用以创建分布式计算机环境的开源软...... 1: ...
- C# 序列化与反序列化几种格式的转换
这里介绍了几种方式之间的序列化与反序列化之间的转换 首先介绍的如何序列化,将object对象序列化常见的两种方式即string和xml对象; 第一种将object转换为string对象,这种比较简单没 ...
- windows 7(32/64位)GHO安装指南(U盘制作篇)~
本篇接着上篇的介绍,为同学讲解U盘启动盘的制作.虽然本篇大白菜的官网上也有类似的教程,但是为了完整性,我将在这里为大家细细的讲解.在这里说一下,系统包和软件包的下载大家可以自行下载,很简单的,小主就不 ...
- 烂泥:wiki系统confluence5.6.6安装、中文、破解及迁移
本文由ilanniweb提供友情赞助,首发于烂泥行天下 想要获得更多的文章,可以关注我的微信ilanniweb confluence是一个专业的企业知识管理与协同软件,可以用于构建企业wiki.通过它 ...
- openfire的组件(Component)开发
在之前的文章<Openfire阶段实践总结>中提到过一种openfire的扩展模式Compoent.本文将主要探讨对这种模式的应用与开发方法. 内部与外部组件介绍 在openfire中的许 ...