如何一步一步用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 设计 实现 结语 一.前言 之前的十一篇把用户购买商品并提交订单整个流程上的中间环节都过了一遍.现在来到了这最后一个环节,提交订单.单从业务上看,这个 ...
随机推荐
- 关于自己写C++的一点风格
现在,我学了很长时间的C++,但是自己就是无法精通.许多知识是入门书上没有的.现在写C++最重要的就是风格问题. 我现在的C++风格: 把自己所有的东西都放在一个名称空间下. 没有全局的函数,有的函数 ...
- java中的锁
java中有哪些锁 这个问题在我看了一遍<java并发编程>后尽然无法回答,说明自己对于锁的概念了解的不够.于是再次翻看了一下书里的内容,突然有点打开脑门的感觉.看来确实是要学习的最好方式 ...
- Gradle配置APK自动签名完整流程
转载请注明出处:http://www.cnblogs.com/LT5505/p/6256683.html 一.生成签名 1.命令行生成签名,输入命令keytool -genkey -v -keysto ...
- redis 学习笔记(2)
redis-cluster 简介 redis-cluster是一个分布式.容错的redis实现,redis-cluster通过将各个单独的redis实例通过特定的协议连接到一起实现了分布式.集群化的目 ...
- iOS 小知识点(持续更新)
1.如何通过代码设置Button title的字体大小 设置Button.titleLabel.font = [UIFont systemFontOfSize:<#(CGFloat)#> ...
- Android中的多线程断点下载
首先来看一下多线程下载的原理.多线程下载就是将同一个网络上的原始文件根据线程个数分成均等份,然后每个单独的线程下载对应的一部分,然后再将下载好的文件按照原始文件的顺序"拼接"起来就 ...
- vim安装中文帮助手册
安装方法: 在下面的网站下载中文帮助的文件包:$wget http://nchc.dl.sourceforge.net/sourceforge/vimcdoc/vimcdoc-1.5.0.tar. ...
- 萌新笔记——linux下查看内存的使用情况
windows上有各种软件可以进行"一键加速"之类的操作,释放掉一些内存(虽然我暂时不知道是怎么办到的,有待后续学习).而任务管理器也可以很方便地查看各进程使用的内存情况,如下图: ...
- CYQ.Data V5 从入门到放弃ORM系列:教程 - MAction类使用
背景: 随着V5框架使用者的快速增加,终于促使我开始对整个框架编写完整的Demo. 上周大概花了一星期的时间,每天写到夜里3点半,终完成了框架所有功能的Demo. 同时,按V5框架名称空间的顺序,对每 ...
- 布里斯班Twilight Bay Run半程马拉松
自从8月3日跑了半马以后,又一鼓作气报了11月份的西昌马拉松.与第一次马拉松的只求完赛目标不同,第二次当然想取得一个更好的成绩.所以8月份练的比较猛,基本上是练2.3天休息一天,周么还要拉个长于21公 ...