如何一步一步用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 < 0)
                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_Fan 出处:http://www.cnblogs.com/Zachary-Fan/p/DDD_8.html

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

发表于

我来说两句

0 条评论
登录 后参与评论

相关文章

来自专栏博岩Java大讲堂

Java虚拟机--Java发展史Java虚拟机

3787
来自专栏大数据文摘

程序员之痛点:取个好名字

1683
来自专栏PPV课数据科学社区

最让程序员感到崩溃的10种编程语言

很显然,软件开发领域中的程序员对编程语言最有发言权。一种语言可能是一些程序员的最爱,但它同时也是另一些程序员的噩梦。如果你在编程领域呆了一段时 间,你就迟早会发...

5355
来自专栏博岩Java大讲堂

Java虚拟机--虚拟机发展史

3645
来自专栏程序员宝库

程序员的基础生存技能:高效用Google

来源:GavinZhang( @GavinBuildSomething ) guoze.me/2016/06/26/how-to-google/ 如果说近二十年...

31011
来自专栏斑斓

代码诊所

几年前,我有机会负责一个项目的咨询。团队很小,目标是对旧有系统的后端用Java改写,而团队的开发人员全为C程序员。我的工作职责是负责项目设计、开发,以及担任项目...

4116
来自专栏闰土大叔

太原面经分享:如何在vue面试环节,展示你晋级阿里P6+的技术功底?

一年一度紧张刺激的高考开始了,与此同时,我也没闲着,奔走在各大公司的前端面试环节,不断积累着经验,一路升级打怪。

1961
来自专栏WeTest质量开放平台团队的专栏

浅谈软件工程师的代码素养

“程序是写给人读的,只是偶尔让计算机执行一下。” ——Donald Ervin Knuth(高德纳)

61913
来自专栏CDA数据分析师

一个初级python web后端开发工程师的面试总结

原文链接:https://blog.csdn.net/ayocross/article/details/56509840

1653
来自专栏编程

32行代码实现微信聊天机器人

在智能手机无所不能的今天,聊天机器人大家并不陌生。语音、文字的,随口都能说出几个。记得最早火起来的,当属人人网上的小黄鸡了吧,那个@小黄鸡风靡一时的时代,如今...

5705

扫码关注云+社区

领取腾讯云代金券