zoukankan      html  css  js  c++  java
  • 购物车

    一个完整的购物车

     

     阅读目录

    一、前言

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

    二、回顾

      在动手之前我对之前的购买上下文内对象做了一次回顾。先梳理一下已经在上下文内出现的领域对象,如图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_Fan

  • 相关阅读:
    Spring MVC,绑定数组、集合
    kettle 使用 CARTE 执行
    正交投影矩阵_相机中的透视投影几何——讨论相机中的正交投影
    VS 遇到异常 可能是某个扩展导致的 解决方法 和提升程序管理员权限的方法
    IDEA Community环境搭建笔记
    PHP ROT18加解密
    C# TTS 文字转语音
    软件开发文档【模板】
    java 打印日志 规范建议
    java 死锁 排查 (jstack jconsole jvisualvm jmc)
  • 原文地址:https://www.cnblogs.com/Leo_wl/p/6254344.html
Copyright © 2011-2022 走看看