DDD理论学习类别

DDD理论学习体系——案例及目录


1.引言

在针对大型的繁杂领域拓展建立模型时,聚合、实体和值对象期间的正视性关系大概会变得11分复杂。在某些对象中为了保险其借助对象的有效性实例被创立,须要长远摸底对象实例化逻辑,我们也许须要加载别的有关对象,且或许为了保全其余对象的园地不改变性扩充了额外的事情逻辑,那样即打破了世界的纯粹权利标准(SRP),又增加了世界的复杂性。

那怎么去创造复杂的天地对象啊?因为复杂的天地对象的生命周期大概须要协调本事展开创办。
那个时候,大家就足以引进创制类形式——工厂方式来救助,将对象的应用与成立分开,将指标的制造逻辑显然地包裹到工厂对象中去。

2. DDD中的工厂

大家有不能缺少先理清工厂和工厂情势。
DDD广西中华南理工科业余大学学学程公司厂的要紧对象是潜伏对象的复杂性创制逻辑;次要指标便是要明了的发挥对象实例化的企图。
而工厂形式是计方式中的成立类情势之一。借助工厂格局大家能够很好贯彻DDD中世界对象的创始。

而针对工厂方式的落到实处主要有二种形式:

  • 粗略工厂:简单实用,但违反开放封闭;
  • 厂子方法:开放封闭,单一产品;
  • 空洞工厂:开放封闭,多少个产品;
  • 反射工厂:能够最大限度的解耦。

实际得以实现可以参见始建相似对象,就交给『工厂格局』吧

三.封装内部结构

当要求为聚合添美成分时,我们无法暴光聚合的协会。大家以丰盛货色到购物车为例,来上课怎样一步一步的选用工厂方式。

一般的话,增添到购物车需求多少个步骤:

  1. 加载用户购物车
  2. 得到商品税收的比率
  3. 始建新的购物车子项

连带的应用层代码如下:

namespace Application {
    public class AddProductToBasket {
        // ......
        public void Add (Product product, Guid basketId) {
            var basket = _basketRepository.FindBy (basketId);
            var rate = TaxRateService.ObtainTaxRateFor (product.Id, country.Id);
            var item = new BasketItem (rate, product.Id, product.price);
            basket.Add (item);
            // ...
        }
    }
}

在以上代码中,应用服务须要掌握什么创建BasketItem(购物车子项)的详细逻辑。而那不该时应用服务的天职,应用服务的任务在于协调。我们品尝做以下改变来防止暴光聚合的内部结构。

namespace Application {
    public class AddProductToBasket {
        // ......
        public void Add (Product product, Guid basketId) {
            var basket = _basketRepository.FindBy (basketId);
            basket.Add (product);
            // ...
        }
    }
}
namespace DomainModel {
    public class Basket {
        // ......
        public void Add (Product product) {
            if (Contains (product))
                GetItemFor (product).IncreaseItemQuantitBy (1);
            else {
                var rate = TaxRateService.ObtainTaxRateFor (product.Id,
                    country.Id);
                var item = new BasketItem (rate, product.Id, product.price);
                _items.Add (item);
            }
        }
    }
}

以上代码显示了Basket(购物车)对象提供二个Add办法,用来成功拉长商品到购物车的工作逻辑,对应用服务隐藏了购物车怎么存款和储蓄商品的底细。此外购物车聚集能够保险其里面聚集的完整性,因为它能够确认保障世界的不改变性。通过那种格局,完结了任务的切换,今后的应用服务要简明的多。

但是,却引入了1个新的标题。为了依据商品创建有效的购物车子项,购物车须要提供2个有效的税收的比率。为了创建这厮所得税收的比率,它要信赖四个TaxRateService(税率服务)。获取成立购物车子项正视的税收的比率,这并不属于购物车的天职。而遵循地点的兑现,购物车承担了第一职分,因为它必须一贯通晓哪些创建有效的购物车子项以及在哪个地方去赚取实惠的税率。

为了制止购物车承担额外的任务和藏身购物车子项的内部结构。上面大家引进二个厂子对象来封装购物车子项的开创,包涵获取科学的税收的比率。

namespace DomainModel {
    public class Basket {
        // ......
        public void Add (Product product) {
            if (Contains (product))
                GetItemFor (product).IncreaseItemQuantitBy (1);
            else
                _items.Add (BasketItemFactory.CreateItemFor (product,
                    deliveryAddress));
        }
    }
    public class BasketItemFactory {
        public static void CreateBasketFrom (Product product, Country country) {
            var rate = TaxRateService.ObtainTaxRateFor (product.Id, country.Id);
            return new BasketItem (rate, product.Id, product.price);
        }
    }
}

引进工厂形式后,购物车的天职单一了,且隔绝了来自购物车子项的转移,比如当税收的比率变化时,或购物车子项必要别的音信创制时,都不会潜移默化到购物车的有关逻辑。

四.逃匿创制逻辑

设想那样的须求:订单创立成功后,进行发货处理时,要求依据订单的物品和收件人音信选拔妥当的快递格局。比如暗中认可发顺丰,顺丰无法送达的抉择中华夏族民共和国邮政。

据书上说这几个需要,大家能够抽象出四个Kuaidi(快递)对象用来封装快递音信,和2个Delivery(发货)对象用来封装发货新闻(货色、收件人新闻、快递等)。创造Delivery的天职大家能够放置Order中去,但针对Order来讲它并不知道要创制(选拔)哪一种Kuaidi(快递)。所以,大家可以制造3个KuaidiFactory工厂负责Kuaidi指标的创制。

namespace DomainModel {
    public class Order {
        // ...
        public Delivery CreateFor (IEnumerable<Item> items, destination) {
            var kuaidi = KuaidiFactory.GetKuaidiFor (items,
                destination.Country);
            var delivery = new Delivery (items, destination, kuaidi);
            SetAsDispatched (items, delivery);
            return delivery;
        }
    }
    public class KuaidiFactory {
        public static Kuaidi GetKuaidiFor (IEnumerable<Item> deliveryItems,
            DeliveryAddress destination) {
            if (Shunfeng.CanDeliver (deliveryItems, destination)) {
                return new Shunfeng (deliveryItems, destination);
            } else {
                return new EMS (deliveryItems, destination);
            }
        }
    }
}

如上代码所示,工厂类中大家封装了快递的精选逻辑。

当要开创的对象类型有八个采取,且客户端并不保护创建项目的抉择时,我们能够在圈子层使用工厂中去定义逻辑去调整要成立对象的档次。

五.汇集中的工厂方法

关联工厂,并不是都急需要求创设独立的工厂类来承担对象的创导。3个厂子方法也能够存在于二个晤面中。

比如说这样一项必要,顾客能够将购物车中的商品移到希望清单中去。

第一,那些动作是产生在购物车上的,所以大家得以不假思索的在购物车中定义该行为。第二,将商品丰盛到希望清单中去,就要求创立3个心愿清单子项。

namespace DomainModel {
    public class Basket {
        // .....
        public WishListItem MoveToWishList (Product product) {
            //首先检查购物车中是否包含此商品
            if (BasketContainsAnItemFor (product)) {
                //从购物车中获取该商品对应的子项
                var basketItem = GetItemFor (product);
                //调用工厂方法根据购物车子项创建愿望清单子项
                var wishListItem = WishListItemFactory.CreateFrom (basketItem);
                //从购物车中移除购物车子项
                RemoveItemFor (basketItem);
                return wishListItem;
            }
        }
    }
}

从地点能够见见Basket暴光二个主意用于将BasketItem转换为WishListItem。返回的WishListItemWishList聚合根的实体。其余1些我们就此在Basket新能源车,中调用工厂去创建WishListItem对象,是因为Basket涵盖了创办愿望清单子项所需的全体信息。在开创了WishListItem之后,对于Basket指标的话它的天职就马到功成了。

6.采纳工厂重建对象

在品种中,若是未有借助O揽胜M进行数据模型与世界模型之间的映照,也许通过Web服务从四个老旧系统中获得领域对象,都亟需大家对世界对象进行重建以满意领域的不改变性。使用工厂来重建天地对象相对来讲要比一向开立要复杂。

思虑这么的场地:顾客能够在已购订单中式点心击重新买入开关,全数订单项全体再一次加多到购物车中去。

其一场景就属于购物车对象的重建,跟直接成立购物车对象就不一样了。因为将订单中的全部子项恢复生机到购物车中去,大家就供给额外确定保证世界的不改变性。比如订单子项对应的货品现在是还是不是下架,要是下架大家是一向抛出十三分,照旧如故创设多个锁定的购物车子项,标记其为已下架状态?

namespace DomainModel {
    public class Order {
        // ......
        public Basket AddToCartFromOrder (Guid id) {
            OrderDTO rawData = ExternalService.ObtainOrder (id.ToString ());
            var basket = BasketFactory.ReconstituteBasketFrom (rawData);
            return basket;
        }
    }
    namespace DomainModel {
        public class BasketFactory {
            // ...
            public static Basket ReconstituteBasketFrom (OrderDTO rawData) {
                Basket basket;
                // ...
                foreach (var orderItem in rawData.Items) {
                    //是否下架
                    if (!ProductServie.IsOffTheShelf (orderItem.ProductId)) {
                        var newBasketItem = newBasketItem (orderItem.ProductId, orderItem.Qty);
                        basket.Add (newBasketItem);
                    } else {
                        throw new Exception ("订单中该商品已下架,无法重新购买!");
                    }
                }
                // .....
                return basket;
            }
        }
    }

7.总结

指标创造不是三个天地的关怀点,但它实在存在于应用程序的小圈子层中。通过应用工厂能够使得的保管领域模型的透彻清爽,以保障世界模型的对具体的高精度表明。使用工厂具有以下好处:

  1. 工厂将世界对象的应用和创制分离。
  2. 由此选取工厂类,能够隐藏成立复杂领域对象的工作逻辑。
  3. 厂子类能够依照调用者的内需,创立相应的园地对象。
  4. 厂子方法能够打包聚合的在那之中情状。

不过,并不是别的索要实例化对象的地点都要选用工厂。唯有当用工厂比使用构造函数更有表现力时,或存在三个构造函数轻易变成混淆时,可能对要创制对象所依靠的靶子不珍贵时,才选拔工厂开始展览对象的创造。

参考资料:
《Patterns, Principles, and Practices of Domain-Driven
Design》

网站地图xml地图