草庐IT

可落地的DDD(7)-战术设计上的一些误区

方丈的寺院 2023-03-28 原文

背景

几年前我总结过DDD战术设计的一些落地经验可落地的DDD(5)-战术设计,和一次关于聚合根的激烈讨论最近两年有些新的落地体验,回过头来发现,当初对这些概念的理解还是没有深入,这篇文章重新阐述下。

之前理解不到位的点有

  1. 战术设计的各个模块是的协作关系
  2. 哪些是问题空间问题,哪些是方案空间问题边界没有划分清楚。
  3. 实体和聚合根的区别理解不不深刻,实体和聚合根建模的方法不对。

以上问题将会在下文解释清楚。

战术设计拆解

DDD的战术设计即设计某个子域的领域模型以及代码落地。领域事件、领域对象、聚合根、实体、值对象、领域服务、工厂、资源库等这些概念都属于这个范畴。

笔者将这些概念重新分层组装了下,如下图所示。


首先将整体分成两部分,问题空间和方案空间。

  1. 问题空间即领域建模。是对业务问题的描述,以及我们如何对这些问题进行抽象。这些是需要在业务、产品、开发都必须达成一致的,与具体的技术方案无关。
  2. 方案空间即如何用技术手段来解决问题,与具体技术的实现有关。

问题空间即领域建模,是通过实体、值对象、领域服务、领域事件来表达。

  1. 实体和值对象是模型对象,实体是重中之重,包括核心模型数据、行为、状态。之所以要区分实体和值对象,是为了降低复杂度,因为值对象是个常数对象,不需要花太多精力。
    注意某个对象在某个领域内是个值对象,在另外的领域可能是个实体,所以脱离领域上下文,说某个对象是值对象,肯定是不对的,比如大家常说的地址是个值对象,这一定是对的吗?

  2. 领域事件即实体产生的事件

  3. 领域服务包括一些逻辑的计算,和业务策略。比如商业决策逻辑、业务流程等。

方案空间即如何解决问题,实现领域模型与代码的映射。实现设计与实现的一致性。主要通过工厂,聚合,资源库来表达。

  1. 聚合是对实体、值对象的封装。领域外部对领域对象所有访问都基于聚合来。如基础设施层操作聚合进行数据保存。其他领域引用聚合对象数据。
    聚合的设计一般是围绕着技术来的,比如聚合对象事务性。

  2. 工厂,复杂对象的创建工厂类

  3. 资源库,对聚合的操作。

从笔者的实践角度来说,落地DDD过程中,问题空间比方案空间更重要,收益更大。因为通常我们吐槽的某些代码写的烂,贫血模型。背后并不是因为没有用DDD,而是问题空间没有定义好,对于业务没有深刻理解,导致模型抽象不足。

如何建模

为什么要建模

通常在某个子域落地DDD,我们会按照业务分析-》用例分析-》领域建模(问题空间) -》技术落地(方案空间)这些步骤来操作。但其实即使我们不在代码里落地DDD,只用前面3步维护一个子域内的领域模型也同样能够带来很多收益,包括但不限于

  1. 统一业务组各个角色的认知,业务、产品、开发大家对同一概念的认知是一致的。
  2. 指导开发工作的拆分。

比如在淘宝有个血的教训,至今这个历史债还无法被修复。早期在淘宝开店。一个卖家只能开一个店。卖家和店铺是两个领域对象,关系是1:1。店铺服务觉得是1:1的关系,对外提供的服务有根据sellerId获取店铺信息,所以其他调用方就无意识的直接引用了卖家id,这样也可以拿到店铺。导致shopId被等同于了sellerID。这个误引用发生在成千上万个地方,最后导致后续需要支持一个卖家开多店铺时无法支持。只能通过其他trick方式实现。

以之前介绍过的CRM领域 来讲解。
省略业务分析,直接拿到用例。

用例分析


按用户角色罗列所有的用例,用来推导模型、以及模型之间的关系。领域模型建立好了,需要根据列出的用例来走查一遍,要确保所有的用例都能走通。完整的用例集才能推导出正确的模型,所以当有变化时,首先调整用例集,再来修改领域模型

建模

领域建模就是定义模型对象,以及模型对象之间的关联关系。分两步建模,第一步通过名词找模型对象。第二步通过动词、形容词分析对象关联关系

名词

通常反复出现的主语和宾语中的名词就是模型对象,比如市场人员创建一个活动,活动就是一个模型对象。当然定语中出现的名词也可能是模型对象。

1.名词的定义一定要清晰。比如说crm领域有通用的名词叫商机。但是你对口的产品经理不熟悉crm领域,新造了一个词,那你要及早纠正他。

2.名词的含义在限界上下文内语义唯一,在不同的上下文中概念就不一定一样了。比如市场人员创建的活动,和做营销时创建的活动就不一定。

动词、活动

1个市场人员可以创建多个活动,所以市场人员和活动关联关系是1对多。两者独立存在,普通关联关系。

这里为了简化描述,只列了市场活动、线索、客户、商机这些域。用户、角色、权限、数据分析这些域先忽略了。

产出物

在推导的过程中,我们是按照自底向上的方式推导的,最后我们呈现出来的结果是按照如下方式

  1. 领域名词
    市场活动: 市场人员为了展示公司形象、推广公司产品,获取线索而举办的活动。一个活动中可以创建多个线索。

线索: 销售人员基于线索发掘潜在客户,多个线索转换为一个客户。线索可以由一个市场活动生成,或者其他渠道。

客户:有意向购买公司产品的用户,销售人员可以通过跟进客户,转化销售机会。

销售机会:更高质量的线索,有机会签单。可以通过客户转换得到,也可以通过其他渠道来获取

  1. 领域模型
    如上图

  2. 主要领域状态转换。
    因为复杂的领域对象生命周期以及一些跨领域对象交互情况在领域模型图中表达不出来,所以需要借助额外的图来表达。

有关可落地的DDD(7)-战术设计上的一些误区的更多相关文章

  1. ruby-on-rails - Rails - 子类化模型的设计模式是什么? - 2

    我有一个模型:classItem项目有一个属性“商店”基于存储的值,我希望Item对象对特定方法具有不同的行为。Rails中是否有针对此的通用设计模式?如果方法中没有大的if-else语句,这是如何干净利落地完成的? 最佳答案 通常通过Single-TableInheritance. 关于ruby-on-rails-Rails-子类化模型的设计模式是什么?,我们在StackOverflow上找到一个类似的问题: https://stackoverflow.co

  2. ruby-on-rails - 使用 rails 4 设计而不更新用户 - 2

    我将应用程序升级到Rails4,一切正常。我可以登录并转到我的编辑页面。也更新了观点。使用标准View时,用户会更新。但是当我添加例如字段:name时,它​​不会在表单中更新。使用devise3.1.1和gem'protected_attributes'我需要在设备或数据库上运行某种更新命令吗?我也搜索过这个地方,找到了许多不同的解决方案,但没有一个会更新我的用户字段。我没有添加任何自定义字段。 最佳答案 如果您想允许额外的参数,您可以在ApplicationController中使用beforefilter,因为Rails4将参数

  3. ruby-on-rails - date_field_tag,如何设置默认日期? [ rails 上的 ruby ] - 2

    我想设置一个默认日期,例如实际日期,我该如何设置?还有如何在组合框中设置默认值顺便问一下,date_field_tag和date_field之间有什么区别? 最佳答案 试试这个:将默认日期作为第二个参数传递。youcorrectlysetthedefaultvalueofcomboboxasshowninyourquestion. 关于ruby-on-rails-date_field_tag,如何设置默认日期?[rails上的ruby],我们在StackOverflow上找到一个类似的问

  4. ruby-on-rails - openshift 上的 rails 控制台 - 2

    我将我的Rails应用程序部署到OpenShift,它运行良好,但我无法在生产服务器上运行“Rails控制台”。它给了我这个错误。我该如何解决这个问题?我尝试更新ruby​​gems,但它也给出了权限被拒绝的错误,我也无法做到。railsc错误:Warning:You'reusingRubygems1.8.24withSpring.UpgradetoatleastRubygems2.1.0andrun`gempristine--all`forbetterstartupperformance./opt/rh/ruby193/root/usr/share/rubygems/rubygems

  5. ruby-on-rails - 相关表上的范围为 "WHERE ... LIKE" - 2

    我正在尝试从Postgresql表(table1)中获取数据,该表由另一个相关表(property)的字段(table2)过滤。在纯SQL中,我会这样编写查询:SELECT*FROMtable1JOINtable2USING(table2_id)WHEREtable2.propertyLIKE'query%'这工作正常:scope:my_scope,->(query){includes(:table2).where("table2.property":query)}但我真正需要的是使用LIKE运算符进行过滤,而不是严格相等。然而,这是行不通的:scope:my_scope,->(que

  6. ruby-on-rails - 如何生成传递一些自定义参数的 `link_to` URL? - 2

    我正在使用RubyonRails3.0.9,我想生成一个传递一些自定义参数的link_toURL。也就是说,有一个articles_path(www.my_web_site_name.com/articles)我想生成如下内容:link_to'Samplelinktitle',...#HereIshouldimplementthecode#=>'http://www.my_web_site_name.com/articles?param1=value1¶m2=value2&...我如何编写link_to语句“alàRubyonRailsWay”以实现该目的?如果我想通过传递一些

  7. LC滤波器设计学习笔记(一)滤波电路入门 - 2

    目录前言滤波电路科普主要分类实际情况单位的概念常用评价参数函数型滤波器简单分析滤波电路构成低通滤波器RC低通滤波器RL低通滤波器高通滤波器RC高通滤波器RL高通滤波器部分摘自《LC滤波器设计与制作》,侵权删。前言最近需要学习放大电路和滤波电路,但是由于只在之前做音乐频谱分析仪的时候简单了解过一点点运放,所以也是相当从零开始学习了。滤波电路科普主要分类滤波器:主要是从不同频率的成分中提取出特定频率的信号。有源滤波器:由RC元件与运算放大器组成的滤波器。可滤除某一次或多次谐波,最普通易于采用的无源滤波器结构是将电感与电容串联,可对主要次谐波(3、5、7)构成低阻抗旁路。无源滤波器:无源滤波器,又称

  8. 计算机毕业设计ssm+vue基本微信小程序的小学生兴趣延时班预约小程序 - 2

    项目介绍随着我国经济迅速发展,人们对手机的需求越来越大,各种手机软件也都在被广泛应用,但是对于手机进行数据信息管理,对于手机的各种软件也是备受用户的喜爱小学生兴趣延时班预约小程序的设计与开发被用户普遍使用,为方便用户能够可以随时进行小学生兴趣延时班预约小程序的设计与开发的数据信息管理,特开发了小程序的设计与开发的管理系统。小学生兴趣延时班预约小程序的设计与开发的开发利用现有的成熟技术参考,以源代码为模板,分析功能调整与小学生兴趣延时班预约小程序的设计与开发的实际需求相结合,讨论了小学生兴趣延时班预约小程序的设计与开发的使用。开发环境开发说明:前端使用微信微信小程序开发工具:后端使用ssm:VU

  9. ruby-on-rails - Ruby - 如何从 ruby​​ 上的 .pfx 文件中提取公钥、rsa 私钥和 CA key - 2

    我有一个.pfx格式的证书,我需要使用ruby​​提取公共(public)、私有(private)和CA证书。使用shell我可以这样做:#ExtractPublicKey(askforpassword)opensslpkcs12-infile.pfx-outfile_public.pem-clcerts-nokeys#ExtractCertificateAuthorityKey(askforpassword)opensslpkcs12-infile.pfx-outfile_ca.pem-cacerts-nokeys#ExtractPrivateKey(askforpassword)o

  10. ruby - 找一些句子 - 2

    我想找到在某些文本中找到一些(让它是两个)句子的好方法。什么会更好-使用正则表达式或拆分方法?你的想法?应JeremyStein的要求-有一些例子示例:输入:ThefirstthingtodoistocreatetheCommentmodel.We’llcreatethisinthenormalway,butwithonesmalldifference.IfwewerejustcreatingcommentsforanArticlewe’dhaveanintegerfieldcalledarticle_idinthemodeltostoretheforeignkey,butinthis

随机推荐