领域驱动设计实战--战略建模

发布时间:2026/7/3 17:58:18
领域驱动设计实战--战略建模 自从去年接触DDD以来阅读了大量的相关书籍看了园子里面很多DDD方面的文章也在实际项目中也有意无意的使用DDD的一些思想和方法。但总觉得这些知识太分散没有给自己思想带来质的升华。观摩了园子里面很多DDD的例子[.NET领域驱动设计实战系列]专题二结合领域驱动设计的面向服务架构来搭建网上书店领域驱动设计实践领域驱动设计系列文章汇总等等还有很多这些例子的质量都很高图文并茂一堆让人流口水的代码示例。但每次看完例子后我的迷茫就会又加深一分。因为我觉得很多博文都只是打着DDD的幌子还是按自己的原来理解方式写代码而已。并没有给大家深入剖析DDD。别忘了DDD是领域驱动设计而不是领域驱动开发。在完成这些例子过程中太重视出成果出一个实际的代码项目而忽视了设计即使有些设计也只是停留在战术建模的阶段并没有站在战略建模的高度来对需求进行分析通常给出一个UML类关系图一个分层架构图就开始突突突地来一堆代码最后还不忘贴两个漂亮的UI截图引起无数粉丝追捧额他们的粉丝中也有我看来可以把我拉黑了。用沃恩.弗农大神的话这样做只能算DDD-Lite离真正的DDD还有段距离。于是我有了写这篇文章的想法。所谓的战略建模有两点界限上下文Bounded Context、上下文映射图Context Mapping。来看一下它们的定义限界上下文它是一个限定边界的环境在该环境中每一个模型的概念包括它的属性和操作都具有特殊的含义。它是战略建模的核心。上下文映射图通用使用框图或代码的方式来展现限界上下文之间的集成关系。它们为什么重要我以dax.net大大的一个例子来说明来看一下他的一篇文章EntityFramework之领域驱动设计实践三 文中内容大概是这样安排的首先介绍他的例子一个简易的销售系统给出UML实体框图然后突突的给出一堆代码这是一个典型的战术建模的例子。而且该文中有一句极其误导DDD新人的话我不得不吐槽一下上面的模型表述了领域模型中各个实体及其之间的关系。我们先不去讨论整个系统的业务会是什么样的我们先看看EF是如何支持实体和值对象概念的。它直接让我们这些技术狂热爱好者们瞬间偏离了DDD以业务为核心不依赖具体技术实现的初衷。DDD本来就是引导我们来解决业务上的问题而不是来让我们炫耀新技能的。如果只是介绍EF那这篇文章是篇合格的文章。但如果把它作为DDD的文章那它就是个反面教程。很不幸本文开始的几个链接也都被我划到反面教程之列而且类似的文章园子里面还有很多。这些文章用来教大家写代码可以但不能作为DDD的教程。我的DDD设计之道就像前面说的我们进行DDD的第一步不应该是急着去创建实体模型而应该站在更高的层面去理解需求划分领域。这里我就以dax.net这个简易的销售系统为例。首先我们来看看那篇文章中的模型图注意这里不是用这个模型图而仅仅是参考一下用来分析业务就当这个模型图是会说话的客户吧必须坐下来和客户就是上面这个模型图好好谈谈了以下为谈话内容把对话内容总结一下我们可以看出需求业务大概是这样的客户可以在本系统进行支付并且可以使用多信用卡支付为客户生成订单在现有的订单基础上可以进行退订管理客户信息主要是信用卡信息管理管理产品信息管理产品分类信息来把这几条业务进行划分得到原始领域结构图上图只是对领域进行了划分及确定限界上下文各限界上下文的关系下面会有介绍。这里共4个限界上下文客户上下文订单上下文支付上下文及产品上下文。这里有以下几点要特别说明通过对领域的划分优化了原有系统设计中Item实体即表示订单项目又表示产品的混淆的定义。在订单上下文中没有产品这个概念只有订单项订单项的属性数据主要是产品名称单价会在生成订单项实体时从产品上下文中的产品获取支付上下文中没有客户这个概念只有帐户这个概念(图中画成帐号在此纠正一下)同订单中的订单项帐户的属性也会在生成帐户实体时从其他上下文中获取这里是从客户上下文中获取帐户是客户的一个子集随着业务的增长可以把退订业务从订单管理子域中划分出来作为一个单独子域这里暂时先不考虑可以看出在确定子域及限界上下文后一些容易混淆的概念会逐渐得到清晰的描述这样可以方便开发团队、业务人员及客户之间的交流而且还为我们开发时划分项目功能提供最直接的依据。我们进一步对该图进行优化。来看一下限界上下文之间的关系即上下文映射图图中的实线连接表示两端的限界上下文之间存在联系。线上标注的U/D表示上游/下游。通常情况下上游的限界上下文会为下游提供访问接口或服务下游使用一个防腐层获取从上游接口传过来的数据然后转化成本限界中使用的实体。举个例子产品上下文是订单上下文的上游。当用户进行产品选购时会向订单里面添加订单项丫的这里没设计购物车class Order { private IListItem _items new ListItem(); //支付上下文中的防腐层 private IACLService _iaclService; public Order(IACLService aclService) { _iaclService aclService; } /// summary /// 添加订单项 /// /summary /// param nameproductid产品ID/param /// param namecount产品个数/param public void AddItem(string productid, int count) { Item item _iaclService.GetItemFor(productid); item.Count count; _items.Add(item); } }在伪实现中添加订单项的函数AddItem参数用的是产品的id而不是产品的具体信息这样就可以实现订单项与产品的解耦。假设我们的产品管理子域是通过WCF用服务的方式实现防腐层就可以通过访问服务来获取该产品然后把它转化成一个Item。从而实现订单项的添加。DDD方面的代码实现会在近期放出敬请期待后记本文是站在个人理解角度去阐述DDD在实现过程中也没有涉及CQRS/AES等概念只是想说明一下如果使用DDD请站在业务的角度而不是技术的角度。关于学习DDD强烈建议学习netfocus的ENode框架本文如有冒犯之处还请海涵欢迎拍砖不过你要是进行人身攻