如何一步一步用ddd设计一个电商设计素材网站网站

招聘3年以上.Net/Android开发。邮箱:,待遇从优
  实际编码已经写了2篇了,在这过程中非常感谢有听到观点不同的声音,借着这个契机,今天这篇就把大家提出的建议一个个的过一遍,重新整理,重新出发,为了让接下去的DDD之路走的更好。
二、单元测试
  蟋蟀兄在我的第三篇文章下面指出:
  这点其实是我偷懒了,单元测试其实不单单在DDD中是一个很重要的一环,在我们崇尚敏捷,快速迭代的大背景下,有良好的单元测试模块可以保证快速迭代下的项目质量。有甚至可以使用测试先行的TDD模式。
  单元测试的好处我就不多说了,那么现在开始在项目中增加单元测试。单元测试有多种命名方式,我个人的方式是给每一个对象单独建立一个测试类,然后里面每个单元测试方法的命名规则为"方法名_条件_预期的结果"这样子。那么根据我们之前的Cart和CartItem的建模,编写的单元测试如下:
[TestClass]
public class CartTest
[TestMethod]
[ExpectedException(typeof(ArgumentException))]
public void Constructor_CartIdDefault_ThrowArgumentException()
var cart = new Cart(default(Guid), Guid.NewGuid(), DateTime.Now);
Assert.AreNotEqual(null, cart);
[TestMethod]
[ExpectedException(typeof(ArgumentException))]
public void Constructor_UserIdDefault_ThrowArgumentException()
var cart = new Cart(Guid.NewGuid(), default(Guid), DateTime.Now);
Assert.AreNotEqual(null, cart);
[TestMethod]
[ExpectedException(typeof(ArgumentException))]
public void Constructor_LastChangeTimeDefault_ThrowArgumentException()
var cart = new Cart(Guid.NewGuid(), Guid.NewGuid(), default(DateTime));
Assert.AreNotEqual(null, cart);
[TestMethod]
public void AddCartItem_NotExisted_TotalItemCountIsIncreased()
var cart = new Cart(Guid.NewGuid(), Guid.NewGuid(), DateTime.Now);
cart.AddCartItem(new CartItem(new Guid("1-"), 1, 100));
Assert.AreEqual(1, cart.TotalItemCount());
cart.AddCartItem(new CartItem(new Guid("2-"), 1, 100));
Assert.AreEqual(2, cart.TotalItemCount());
[TestMethod]
public void AddCartItem_Existed_TotalItemCountIsNotIncreasedTotalItemNumIsIncreased()
var cart = new Cart(Guid.NewGuid(), Guid.NewGuid(), DateTime.Now);
cart.AddCartItem(new CartItem(new Guid("1-"), 1, 100));
Assert.AreEqual(1, cart.TotalItemCount());
Assert.AreEqual(1, cart.TotalItemNum());
cart.AddCartItem(new CartItem(new Guid("1-"), 1, 100));
Assert.AreEqual(1, cart.TotalItemCount());
Assert.AreEqual(2, cart.TotalItemNum());
[TestClass]
public class CartItemTest
[TestMethod]
[ExpectedException(typeof(ArgumentException))]
public void ModifyQuantity_LessZero_ThrowArgumentException()
var cartItem = new CartItem(new Guid("1-"), 1, 100);
cartItem.ModifyQuantity(-1);
[TestMethod]
[ExpectedException(typeof(ArgumentException))]
public void ModifyQuantity_EqualsZero_ThrowArgumentException()
var cartItem = new CartItem(new Guid("1-"), 1, 100);
cartItem.ModifyQuantity(0);
[TestMethod]
public void ModifyQuantity_MoreZero_Success()
var cartItem = new CartItem(new Guid("1-"), 1, 100);
cartItem.ModifyQuantity(10);
Assert.AreEqual(10, cartItem.Quantity);
[TestMethod]
[ExpectedException(typeof(ArgumentException))]
public void ModifyPrice_LessZero_ThrowArgumentException()
var cartItem = new CartItem(new Guid("1-"), 1, 100);
cartItem.ModifyPrice(-1);
[TestMethod]
public void ModifyQuantity_EqualsZero_Success()
var cartItem = new CartItem(new Guid("1-"), 1, 100);
cartItem.ModifyQuantity(0);
Assert.AreEqual(0, cartItem.Price);
[TestMethod]
public void ModifyQuantity_MoreZero_Success()
var cartItem = new CartItem(new Guid("1-"), 1, 100);
cartItem.ModifyQuantity(10);
Assert.AreEqual(10, cartItem.Price);
三、纠正错误,重新出发
  在写CartItemTest的时候发现了一个问题。领域对象的设计中有一个要点,就是实体必须需要通过其所属的聚合根才能访问,这样才能体现出聚合的的整体性,并且减少外界对聚合内部过多的了解。而目前对于CartItem的运用却有些背道而驰的意思,由外部对象进行实例化,必然增加了外部调用方对整个购物项构造过程的了解。有一位园友有提到这点。
  &我思考了下,觉得这位园友的建议是对的。他建议的改法恰恰能够满足这个要求,隐藏了构造CartItem实体的细节。
  好了那先把CartItem的构造函数访问类型设置为internal吧,这样也只能在CartItem所在的Domain项目中进行实例化了,然后再修改Cart.AddCartItem方法的参数。变为如下:
public void AddCartItem(Guid productId, int quantity, decimal price)
var cartItem = new CartItem(productId, quantity, price);
var existedCartItem = this._cartItems.FirstOrDefault(ent =& ent.ProductId == cartItem.ProductId);
if (existedCartItem == null)
this._cartItems.Add(cartItem);
existedCartItem.ModifyPrice(cartItem.Price); //有可能价格更新了,每次都更新一下。
existedCartItem.ModifyQuantity(existedCartItem.Quantity + cartItem.Quantity);
单元测试也做出相应的更改:
[TestClass]
public class CartTest
[TestMethod]
[ExpectedException(typeof(ArgumentException))]
public void Constructor_CartIdDefault_ThrowArgumentException()
var cart = new Cart(default(Guid), Guid.NewGuid(), DateTime.Now);
Assert.AreNotEqual(null, cart);
[TestMethod]
[ExpectedException(typeof(ArgumentException))]
public void Constructor_UserIdDefault_ThrowArgumentException()
var cart = new Cart(Guid.NewGuid(), default(Guid), DateTime.Now);
Assert.AreNotEqual(null, cart);
[TestMethod]
[ExpectedException(typeof(ArgumentException))]
public void Constructor_LastChangeTimeDefault_ThrowArgumentException()
var cart = new Cart(Guid.NewGuid(), Guid.NewGuid(), default(DateTime));
Assert.AreNotEqual(null, cart);
[TestMethod]
public void AddCartItem_NotExisted_TotalItemCountIsIncreased()
var cart = new Cart(Guid.NewGuid(), Guid.NewGuid(), DateTime.Now);
cart.AddCartItem(new Guid("1-"), 1, 100);
Assert.AreEqual(1, cart.TotalItemCount());
cart.AddCartItem(new Guid("2-"), 1, 100);
Assert.AreEqual(2, cart.TotalItemCount());
[TestMethod]
public void AddCartItem_Existed_TotalItemCountIsNotIncreasedTotalItemNumIsIncreased()
var cart = new Cart(Guid.NewGuid(), Guid.NewGuid(), DateTime.Now);
cart.AddCartItem(new Guid("1-"), 1, 100);
Assert.AreEqual(1, cart.TotalItemCount());
Assert.AreEqual(1, cart.TotalItemNum());
cart.AddCartItem(new Guid("1-"), 1, 100);
Assert.AreEqual(1, cart.TotalItemCount());
Assert.AreEqual(2, cart.TotalItemNum());
[TestClass]
public class CartItemTest
[TestMethod]
[ExpectedException(typeof(ArgumentException))]
public void ModifyQuantity_LessZero_ThrowArgumentException()
var cart = new Cart(Guid.NewGuid(), Guid.NewGuid(), DateTime.Now);
cart.AddCartItem(new Guid("1-"), 1, 100);
var cartItem = cart.GetCartItem(new Guid("1-"));
Assert.AreNotEqual(null, cartItem);
Assert.AreEqual(1, cartItem.Quantity);
cartItem.ModifyQuantity(-1);
[TestMethod]
[ExpectedException(typeof(ArgumentException))]
public void ModifyQuantity_EqualsZero_ThrowArgumentException()
var cart = new Cart(Guid.NewGuid(), Guid.NewGuid(), DateTime.Now);
cart.AddCartItem(new Guid("1-"), 1, 100);
var cartItem = cart.GetCartItem(new Guid("1-"));
Assert.AreNotEqual(null, cartItem);
Assert.AreEqual(1, cartItem.Quantity);
cartItem.ModifyQuantity(0);
[TestMethod]
public void ModifyQuantity_MoreZero_Success()
var cart = new Cart(Guid.NewGuid(), Guid.NewGuid(), DateTime.Now);
cart.AddCartItem(new Guid("1-"), 1, 100);
var cartItem = cart.GetCartItem(new Guid("1-"));
Assert.AreNotEqual(null, cartItem);
Assert.AreEqual(1, cartItem.Quantity);
cartItem.ModifyQuantity(10);
Assert.AreEqual(10, cartItem.Quantity);
[TestMethod]
[ExpectedException(typeof(ArgumentException))]
public void ModifyPrice_LessZero_ThrowArgumentException()
var cart = new Cart(Guid.NewGuid(), Guid.NewGuid(), DateTime.Now);
cart.AddCartItem(new Guid("1-"), 1, 100);
var cartItem = cart.GetCartItem(new Guid("1-"));
Assert.AreNotEqual(null, cartItem);
Assert.AreEqual(100, cartItem.Price);
cartItem.ModifyPrice(-1);
[TestMethod]
public void ModifyPrice_EqualsZero_Success()
var cart = new Cart(Guid.NewGuid(), Guid.NewGuid(), DateTime.Now);
cart.AddCartItem(new Guid("1-"), 1, 100);
var cartItem = cart.GetCartItem(new Guid("1-"));
Assert.AreNotEqual(null, cartItem);
Assert.AreEqual(100, cartItem.Price);
cartItem.ModifyPrice(0);
Assert.AreEqual(0, cartItem.Price);
[TestMethod]
public void ModifyPrice_MoreZero_Success()
var cart = new Cart(Guid.NewGuid(), Guid.NewGuid(), DateTime.Now);
cart.AddCartItem(new Guid("1-"), 1, 100);
var cartItem = cart.GetCartItem(new Guid("1-"));
Assert.AreNotEqual(null, cartItem);
Assert.AreEqual(100, cartItem.Price);
cartItem.ModifyPrice(10);
Assert.AreEqual(10, cartItem.Price);
  这样一来,被和2位园友所指出的奇怪的&UserBuyProductDomainService&也自然消失了。应用层代码变成:
public Result Buy(Guid userId, Guid productId, int quantity)
var product = DomainRegistry.ProductService().GetProduct(productId);
if (product == null)
return Result.Fail("对不起,未能获取产品信息请重试~");
var cart = _getUserCartDomainService.GetUserCart(userId);
cart.AddCartItem(productId, quantity, product.SalePrice);
DomainRegistry.CartRepository().Save(cart);
return Result.Success();
  DDD的道路是坎坷的,我希望通过在园子里发布的文章能够结交到志同道合的DDD之友,欢迎大家不吝啬自己的见解,多多留言,也让想学习或者正在学习DDD的园友少走一些弯路。
本文的源码地址:。
作者:出处:
阅读(...) 评论()如何一步一步用DDD设计一个电商网站(七)―― 实现售价上下文 - Zachary_Fan
来源:互联网
编辑:刘梓楠
有网友碰到过这样的问题:如何一步一步用DDD设计一个电商网站(七)―― 实现售价上下文 - Zachary_Fan,问题详细内容为:挺不错的博文:如何一步一步用DDD设计一个电商网站(七)―― 实现售价上下文 - Zachary_Fan,我搜你通过互联网收集了相关的一些解决方案,希望对有过相同或者相似问题的网友提供帮助,具体如下:
明确业务细节
  上一篇我们已经确立的购买上下文和销售上下文的交互方式,传送门在此:/Zachary-Fan/p/DDD_6.html,本篇我们来实现售价上下文的具体细节。
二、明确业务细节
  电商市场越来越成熟,竞争也越来越激烈,影响客户流量的关键因素之一就是价格,运营的主要打法之一也是价格,所以是商品价格是一个在电商中很重要的一环。正因为如此也让促销演变的越来越复杂,那么如何在编码上花点心思来尽可能的降低业务的复杂化带来的影响和提高可扩展性来拥抱变化就变得很重要了。先从最简单的开始,我浏览了某东的促销,先把影响价格相关的几个促销找出来,暂时得出以下几个结论(这里又要提一下,我们实际工作中应在开始编码之前要做的就是和领域专家讨论促销的细节):
  1.满减:可以多个商品共同参与,汇总金额达到某个阈值之后减免XX金额。
  2.多买优惠(方式1):可以多个商品共同参与,汇总购买数量达到一定数量得到X折的优惠。
  3.多买优惠(方式2):可以多个商品共同参与,汇总购买数量达到一定数量减免最便宜的X件商品。
  4.限时折扣:直接商品的购买金额被修改到指定值。
  5.满减促销的金额满足点以优惠后价格为准,比如该商品既有限时折扣又有满减,则使用限时折扣的价格来计算金额满足点。
  6.优惠券是在之上的规则计算之后得出的金额基础下计算金额满足点。
  7.每一个商品的满减+多买优惠仅能参与一种。并且相同促销商品在购物车中商品展示的方式是在一组中。
  根据上面的业务描述先找到其中的几个领域对象,然后在做一些适当的抽象,得出下面的UML图(点击图片可查看大图):
&                            【图1】
  建模完之后下面的事情就容易了,先梳理一下我们的业务处理顺序:
  1.根据购买上下文传入的购物车信息获取产品的相关促销。
  2.先处理单品促销。
  3.最后处理多商品共同参与的促销。
  梳理的过程中发现,为了能够实现满减和多买优惠促销仅能参与一个,所以需要再购买上下文和售价上下文之间传递购物项时增加一个参数选择的促销唯一标识(SelectedMultiProductsPromotionId)。
  随后根据上面业务处理顺序,发现整个处理的链路比较长,那么这里我决定定义一个值对象来承载整个处理的过程。如下:
public class BoughtProduct
private readonly List&PromotionRule& _promotionRules = new List&PromotionRule&();
public string ProductId { }
public int Quantity { }
public decimal UnitPrice { }
public decimal ReducePrice { }
/// &summary&
/// 商品在单品优惠后的单价,如果没有优惠则为正常购买的单价
/// &/summary&
public decimal DiscountedUnitPrice
get { return UnitPrice - ReduceP }
public decimal TotalDiscountedPrice
get { return DiscountedUnitPrice * Q }
public ReadOnlyCollection&ISingleProductPromotion& InSingleProductPromotionRules
get { return _promotionRules.OfType&ISingleProductPromotion&().ToList().AsReadOnly(); }
public IMultiProductsPromotion InMultiProductPromotionRule { }
public BoughtProduct(string productId, int quantity, decimal unitPrice, decimal reducePrice, IEnumerable&PromotionRule& promotionRules, string selectedMultiProdcutsPromotionId)
if (string.IsNullOrWhiteSpace(productId))
throw new ArgumentException("productId不能为null或者空字符串", "productId");
if (quantity &= 0)
throw new ArgumentException("quantity不能小于等于0", "quantity");
if (unitPrice & 0)
throw new ArgumentException("unitPrice不能小于0", "unitPrice");
if (reducePrice & 0)
throw new ArgumentException("reducePrice不能小于0", "reducePrice");
this.ProductId = productId;
this.Quantity =
this.UnitPrice = unitP
this.ReducePrice = reduceP
if (promotionRules != null)
this._promotionRules.AddRange(promotionRules);
var multiProductsPromotions = this._promotionRules.OfType&IMultiProductsPromotion&().ToList();
if (multiProductsPromotions.Count & 0)
var selectedMultiProductsPromotionRule = multiProductsPromotions.SingleOrDefault(ent =& ((PromotionRule)ent).PromotoinId == selectedMultiProdcutsPromotionId);
InMultiProductPromotionRule = selectedMultiProductsPromotionRule ?? multiProductsPromotions.First();
public BoughtProduct ChangeReducePrice(decimal reducePrice)
if (reducePrice & 0)
throw new ArgumentException("result.ReducePrice不能小于0");
var selectedMultiProdcutsPromotionId = this.InMultiProductPromotionRule == null
: ((PromotionRule) this.InMultiProductPromotionRule).PromotoinId;
return new BoughtProduct(this.ProductId, this.Quantity, this.UnitPrice, reducePrice, this._promotionRules, selectedMultiProdcutsPromotionId);
  需要注意一下,值对象的不可变性,所以这里的ChangeReducePrice方法返回的是一个新的BoughtProduct对象。另外这次我们的例子比较简单,单品促销只有1种。理论上单品促销是支持叠加参与的,所以这里的单品促销设计了一个集合来存放。
  下面的代码是处理单品促销的代码:
foreach (var promotionRule in singleProductPromotionRules)
var tempReducePrice = ((PromotionRuleLimitTimeDiscount)promotionRule).CalculateReducePrice(productId, unitPrice, DateTime.Now);
//在创建的时候约束促销的重复性。此处逻辑上允许重复
if (unitPrice - reducePrice &= tempReducePrice)
reducePrice = unitP
reducePrice += tempReduceP
  这里也可以考虑把它重构成一个领域服务来合并同一个商品多个单品促销计算结果。
  整个应用服务的代码如下:
public class CalculateSalePriceService : ICalculateSalePriceService
private static readonly MergeSingleProductPromotionForOneProductDomainService _mergeSingleProductPromotionForOneProductDomainService = new MergeSingleProductPromotionForOneProductDomainService();
public CalculatedCartDTO Calculate(CartRequest cart)
List&BoughtProduct& boughtProducts = new List&BoughtProduct&();
foreach (var cartItemRequest in cart.CartItems)
var promotionRules = DomainRegistry.PromotionRepository().GetListByContainsProductId(cartItemRequest.ProductId);
var boughtProduct = new BoughtProduct(cartItemRequest.ProductId, cartItemRequest.Quantity, cartItemRequest.UnitPrice, 0, promotionRules, cartItemRequest.SelectedMultiProductsPromotionId);
boughtProducts.Add(boughtProduct);
#region 处理单品促销
foreach (var boughtProduct in boughtProducts.ToList())
var calculateResult = _mergeSingleProductPromotionForOneProductDomainService.Merge(boughtProduct.ProductId, boughtProduct.DiscountedUnitPrice, boughtProduct.InSingleProductPromotionRules);
var newBoughtProduct = boughtProduct.ChangeReducePrice(calculateResult);
boughtProducts.Remove(boughtProduct);
boughtProducts.Add(newBoughtProduct);
#endregion
#region 处理多商品促销&构造DTO模型
List&CalculatedFullGroupDTO& fullGroupDtos = new List&CalculatedFullGroupDTO&();
foreach (var groupedPromotoinId in boughtProducts.Where(ent =& ent.InMultiProductPromotionRule != null).GroupBy(ent =& ((PromotionRule)ent.InMultiProductPromotionRule).PromotoinId))
var multiProdcutsReducePricePromotion = (IMultiProdcutsReducePricePromotion)groupedPromotoinId.First().InMultiProductPromotionR
//暂时只有减金额的多商品促销
var products = groupedPromotoinId.ToList();
if (multiProdcutsReducePricePromotion == null)
var reducePrice = multiProdcutsReducePricePromotion.CalculateReducePrice(products);
fullGroupDtos.Add(new CalculatedFullGroupDTO
CalculatedCartItems = products.Select(ent =& ent.ToDTO()).ToArray(),
ReducePrice = reducePrice,
MultiProductsPromotionId = groupedPromotoinId.Key
#endregion
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
&  这里的设计没有考虑促销规则的冲突问题,如果做的话把它放在创建促销规则的时候进行约束即可。
本文的源码地址:/ZacharyFan/DDDDemo/tree/Demo7。
作者:Zachary_Fan出处:/Zachary-Fan/p/DDD_7.html
posted @ 请注意,本站信息均收集自互联网,相关信息仅供参考,医疗等重要信息请以正规途径为最终意见,本站不承担任何责任!
您可能还关注浅谈我对DDD领域驱动设计的理解_课工场吧_百度贴吧
&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&签到排名:今日本吧第个签到,本吧因你更精彩,明天继续来努力!
本吧签到人数:0成为超级会员,使用一键签到本月漏签0次!成为超级会员,赠送8张补签卡连续签到:天&&累计签到:天超级会员单次开通12个月以上,赠送连续签到卡3张
浅谈我对DDD领域驱动设计的理解收藏
从遇到问题开始当人们要做一个软件系统时,一般总是因为遇到了什么问题,然后希望通过一个软件系统来解决。比如,我是一家企业,然后我觉得我现在线下销售自己的产品还不够,我希望能够在线上也能销售自己的产品。所以,自然而然就想到要做一个普通电商系统,用于实现在线销售自己企业产品的目的。再比如,我是一家互联网公司,公司有很多系统对外提供服务,面向很多客户端设备。但是最近由于各种原因,导致服务经常出故障。所以,我们希望通过各 种措施提高服务的质量和稳定性。其中的一个措施就是希望能做一个灰度发布的平台,这个平台可以提供灰度发布的服务。然后,当某个业务系统做了一些修改并需 要发布时,可以使用我们的灰度发布平台来非常方便的实现灰度发布的功能。比如在灰度发布平台上方便的定制允许哪些特定的客户端才会访问新服务,哪些客户端 继续使用老服务。灰度发布平台可以提供各种灰度的策略。有了这样的灰度发布机制,那即便系统的新逻辑有什么问题,受影响的面也不会很大,在可控范围内。所以,如果公司里的所有对外提供服务的系统都接入了灰度平台,那这些系统的发布环节就可以更加有保障了。总之,我们做任何一个软件系统,都是有原因的,否则就没必要做这个系统,而这个原因就是我们遇到的问题。所以,通过问题,我们就知道了我们需要一个 什么样的系统,这个系统解决什么样的问题。最后,我们就很自然的得出了一个目标,即知道了自己要什么。比如我要做一个论坛、一个博客系统、一个电商平台、 一个灰度发布系统、一个IDE、一个分布式消息队列、一个通信框架,等等。
需要特别注意的是,领域模型设计只是整个软件设计中的很小一部分。除了领域模型设计之外,要落地一个系统,我们还有非常多的其他设计要做,比如:容量规划架构设计数据库设计缓存设计框架选型发布方案数据迁移、同步方案分库分表方案回滚方案高并发解决方案一致性选型性能压测方案监控报警方案等等。上面这些都需要我们平时的大量学习和积累。作为一个合格的开发人员或架构师,我觉得除了要会DDD领域驱动设计,还要会上面这么多的技术能力,确实是非常不容易的。所以,千万不要以为会DDD了就以为自己很牛逼,实际上你会的只是软件设计中的冰山一角而已。总结本文的重点是基于我个人对DDD的一些理解,希望能整理出一些自己总结出来的一些感悟和经验,并分享给大家。我相信很多人已经看过太多DDD书上的 东西,我总是感觉书上的东西看似都太”正规“,很多时候我们读了之后很难消化,就算理解了书里的内容,当我们想要运用到实践中时,总是感觉无从下手。本文 希望通过通俗易懂的文字,介绍了一部分我对DDD的学习感悟和实践心得,希望能给大家一些启发和帮助。
哈哈哈哈哈
登录百度帐号推荐应用
为兴趣而生,贴吧更懂你。或如何一步一步用DDD设计一个电商网站(六)―― 给购物车加点料,集成售价上下文-学网-中国IT综合门户网站-提供健康,养生,留学,移民,创业,汽车等信息
如何一步一步用DDD设计一个电商网站(六)―― 给购物车加点料,集成售价上下文
来源:互联网 更新时间: 7:08:27 责任编辑:鲁晓倩字体:
  前几篇已经实现了一个最简单的购买过程,这次开始往这个过程中增加一些东西。比如促销、会员价等,在我们的第一篇文章()中规划的上下文映射图可以看到,这些都属于一个独立的上下文(售价上下文)。
二、如何在一个项目中实现多个上下文的业务
  一般情况下,为了更好的分而治之,把不同的上下文作为单独的service,然后通过rpc框架(如WCF)来对其访问是个比较常见的做法。但是在一些小型团队中,虽然划分出了不同上下文,但是我们的开发团队还是同一个。在这种情况下,我个人一般的做法是直接在同一个解决方案中建立不同的项目去做,但是这里需要在解决方案中明确的划分好不同上下文之间的边界,通过代码审核等手段管理好这个边界不被破坏。
                      【图1】
  增加的几个项目如图1所示。
三、售价上下文与购买上下文的集成
  根据我们第一篇所定义的上下文映射图和9种集成模式可以看出,这2个上下文在同一个子域中,并且在我们实际业务场景中,这2者又是相辅相成,所以售价上下文和购买上下文是一种合作关系。确立这个关系之后,那么这个促销的计算逻辑到底是放到哪个上下文种做更合适呢?我们先整理一下几种可能的方式:
  1.购买上下文把购物车中的商品信息丢给售价上下文 --& 售价上下文进行计算 --& 把结果再返回给购买上下文。
  2.购买上下文从销价上下文获取相关会员价和促销信息 --& 再本地的购物车对象基础上进行运算,并直接可运用结果。
  3.再抽出一个专门的计算服务(隶属于售价上下文),去做这个计算的动作。购买上下文把购物车中的商品信息丢给计算服务 --& 计算上下文从销价上下文获取到相关会员价和促销信息 --& 计算 --& 返回结果给购买上下文
  我相信1和2是比较主流的2个方式。但是方式2是把售价上下文仅作为一种数据的提供方,这就把合作关系变成了一个上下游的关系,并且这种方式使得促销规则和购物车强耦合到了一起,不利于促销规则的变化。在这里售价上下文只起了一个简单的数据维护作用,无法完全控制&售价&的定义,没有很好的做到职责分离。方式1和3对购买上下文来说其实是没有区别的,只是方式3让整个数据交互的链路多了一层,会产生额外的开销,好处是服务的粒度更细了,需要结合实际情况权衡一下得失。这里我选择1方式来实现,因为我们在项目初期,还是尽可能的减少非业务目的的拆分导致的额外成本。
  好了,确定了集成方式之后,先把2个上下文之间用于数据交互的DTO模型定一下,如下图2(售价上下文的DTO模型),图3(购买上下文中与前者对应的值对象)。
&                    【图2】
                  【图3】
  另外在图3中可以发现增加了一个ISellingPriceService,抽象了与售价上下文的交互。那么我们在Mall.Infrastructure.Translators项目中增加对这个上下文的防腐层处理,老3样SellingPriceAdapter(发起上下文数据请求的适配器)、SellingPriceService(实现ISellingPriceService)、SellingPriceTranslator(把远程数据对象转换成本地的值对象),代码很简单大家可以在源码中查看。需要注意的是,这里的Mall.Infrastructure.Translators项目仅增加了对Mall.Application.SellingPrice项目的引用,类似于把它当作一个远程资源来对待(按上面所说,如果实际由不同的团队负责可以物理上的分离到2个解决方案中)。
相关文章:
上一篇文章:下一篇文章:
最新添加资讯
24小时热门资讯
Copyright © 2004- All Rights Reserved. 学网 版权所有
京ICP备号-1 京公网安备02号

我要回帖

更多关于 电商网站购物车设计 的文章

 

随机推荐