- Java锁的逻辑(结合对象头和ObjectMonitor)
- 还在用饼状图?来瞧瞧这些炫酷的百分比可视化新图形(附代码实现)⛵
- 自动注册实体类到EntityFrameworkCore上下文,并适配ABP及ABPVNext
- 基于Sklearn机器学习代码实战
这是一个讲解DDD落地的文章系列,作者是《实现领域驱动设计》的译者 滕云 。本文章系列以一个真实的并已成功上线的软件项目—— 码如云 ( https://www.mryqr.com )为例,系统性地讲解DDD在落地实施过程中的各种典型实践,以及在面临实际业务场景时的诸多取舍.
本系列包含以下文章:
既然DDD是“领域”驱动,那么我们便不能抛开业务而只讲技术,为此让我们先从业务上了解一下贯穿本文章系列的案例项目 —— 码如云 (不是马云,也不是码云)。如你已经在本系列的其他文章中了解过该案例,可跳过.
码如云 是一个基于二维码的一物一码管理平台,可以为每一件“物品”生成一个二维码,并以该二维码为入口展开对“物品”的相关操作,典型的应用场景包括固定资产管理、设备巡检以及物品标签等.
在使用码如云时,首先需要创建一个 应用 (App),一个 应用 包含了多个 页面 (Page),也可称为表单,一个 页面 又可以包含多个 控件 (Control),比如单选框控件。 应用 创建好后,可在 应用 下创建多个 实例 (QR)用于表示被管理的对象(比如机器设备)。每个 实例 均对应一个二维码,手机扫码便可对 实例 进行相应操作,比如查看 实例 相关信息或者填写页面表单等,对表单的一次填写称为 提交 (Submission);更多概念请参考 码如云术语 .
在技术上,码如云是一个无代码平台,包含了表单引擎、审批流程和数据报表等多个功能模块。码如云全程采用DDD完成开发,其后端技术栈主要有Java、Spring Boot和MongoDB等.
码如云的源代码是开源的,可以通过以下方式访问:
码如云源代码: https://github.com/mryqr-com/mry-backend 。
在上一篇 DDD概念大白话 中,我们提出了一个观点: DDD的战略设计只在解决一个问题,即软件的模块化划分的问题 。在本文中,我们将对此做出详细的解释.
不过,首先让我们来看看DDD的战略设计原本包含哪些内容。战略设计中包含领域、子域、通用语言和限界上下文等概念。 领域 (Domain)表示一个行业中所发生的一切业务; 子域 (Subdomain)则表示领域中细分之后的子业务,是比领域更小的概念,子域又可细分为 核心子域 (Core Domain)、 支撑子域 (Supporting Domain)和 通用子域 (Generic Domain); 通用语言 (Ubiquitous Language)表示在领域中所有人员都使用一套相同的语言进行沟通交流; 限界上下文 (Bounded Context)则表示由通用语言所形成的上下文边界。读到这里,你是不是感觉好像什么都说了,又感觉什么都没说?
事实上不难看出,无论是从领域到子域,还是从通用语言到限界上下文,其中都体现了一种“分”的思想,这种思想也正是整个计算机科学中的一种基本思想—— 分治法(devide and conquer) 。作为顶层设计的DDD战略设计来讲,这种“分”的思想的落地不正是我们在软件架构图中所看到的那些方块么?不正是软件的模块化划分么?
有了以上认识,再让我们来重新审视一下战略设计中的各种概念。软件中有些模块是业务的核心,对应着DDD中的“核心域”的概念,比如电商系统中的订单模块;有些模块对核心模块起支撑作用,对应DDD中“支撑域”,比如电商系统中的积分模块;而有些模块是通用性质的,对应着DDD中的“通用域”,比如登录管理模块。限界上下文可以看做是子域落地后的概念,因此通常与子域存在一一对应的关系,也即一个限界上下文表示一个模块。限界上下文可以这么理解:在DDD中,允许在不同的模块中存在相同名称的对象,但是它们在各自的上下文中所表示的含义是不同的,这也是“限界”一词的由来。举个例子,在电商系统中,存在交易模块和物流模块,它们都包含“订单(Order)”对象,但是交易模块中的订单和物流模块中的订单所承载的业务含义是不一样的,在交易模块中我们更专注订单的价格、数量和折扣等,而在物流模块中我们则更关注订单的重量、体积和物流状态等.
说DDD的战略设计只是模块化划分并不是要贬低战略设计的意思,事实上恰恰相反,战略设计很重要。DDD的开山鼻祖Eric Evans曾经说,如果让他重新撰写《领域驱动设计》那本书,他会将原书中的很大部分全部撕掉,然后用于撰写与限界上下文相关的内容,从此也可见战略设计的重要性。但是,我们希望做的是让读者认清其中的本质,毕竟DDD本身是一种实践性很强的学问,我们对DDD的认识不应该停留在对概念的咬文爵字上,而是真正能够产出高质量的软件.
事实上,软件的模块化划分是一个非常古老的概念,它伴随着软件的诞生而诞生,其萌芽至少可以追溯到世界上第一台通用电子计算机 ENIAC 的发明者之一 约翰·皮斯普·埃克特 (J. Presper Eckert)在一篇研究 穿孔纸带 的论文中所提到的“Decomposition(分解)”.
后来,软件的模块化经 道格拉斯·麦克罗伊 (Douglas McIlroy)和 布莱德·考克斯 (Brad Cox,Objective-C发明人)等人得到了进一步发展。如果我们再将眼光放开阔一些,你会发现模块化的思想存在于各个行业中,比如船舶、桥梁、建筑以及航空等领域.
因此,模块化对于接受了现代工业文明洗礼的我们来说,并不是一个陌生的词汇。然而,难点并不在于如何定义模块,而在于如何划分模块。在DDD中,这是一个见仁见智众说纷纭的话题,为此,让我们从一个小故事展开.
一个2岁的幼儿,从来没有看到人的头像简易画(下图中左边的图片),但是当你问他那是什么的时候,他可能会说“人人”。这是为什么?
幼儿能够辨认出他从来没有看到过的东西,是因为他拥有两种能力: 经验 和 抽象 。他虽然没有看到过人头像简易画,但是他之前一定看到过真实的人,此所谓经验,也即我们过去所经历的事情;而他能够将人像简易画和真实的人对等起来,则是因为人类与身俱来的抽象能力。此二者,恰恰是我们划分软件模块所需要的东西,并且人人皆有。因此,你并不需要一套专门的学问来指导你完成DDD的战略设计,你需要的依然是那些在日常工作生活中我们始终在使用着的技能.
但是,经验有多有少,抽象有深有浅,导致不同的人所划分出来的模块形态也不一样。为了做好DDD战略设计,你需要有充足的经验以及对业务的深入了解。那么,经验到底到底多少算多呢?5年工作经验够不够?10年又够不够?这种按照年限来区分经验多寡的方式是不合适的,一个10年工作经验的架构师,他可能在这10年内一直在重复性地做着一件事情,而一个3年工作经验的程序员,却可能已经经历过很多项目、技术以及行业。因此,经验是根据你在自己所处的行业中所耕耘的深度和广度来计算的,而非时间.
你可能会认为经验这个东西太不可名状了,无法提炼出一套有据可循理论框架出来,的确没有。然而,这正是软件被称之为艺术的原因,它让每个人都有属于其自己的发挥空间,况且还有大哲学家和大科学家为你背书,你还那么不自信到要去追求一个咨询师没把你教会而你自己也没学会的所谓的理论框架吗?坦诚点,自信点,自豪地告诉别人:“我通过自己对业务的深入了解,外加自己的从业经验和抽象能力,搞定了DDD的战略设计!” 。
让我们来看个例子吧,搜索功能是多数应用网站都有的功能,在一些应用中,搜索可以简单到只是做个正则匹配的小功能点,此时的搜索固然称不上一个模块,而在另一些应用中,比如大型电商网站,搜索包含了多条件多方式查询等众多内容,其后台的软件架构和技术栈甚至都是专门设计的,此时的搜索功能你哪怕找一个毕业生来设计估计结果都是一个独立模块。这里,从功能点到功能模块的变化过程中,没有什么量化工具和理论框架可言,说得直白点,这就是架构师的一个主观认知而已。但是,这个认知却是重要的,因为它体现了架构师对于一个问题的抽象能力,以及对于软件边界的识别能力。什么是架构呢, 一种解释 是软件架构是项目中的资深程序员们对某个问题所达成的统一认识而已.
理论框架虽然没有,但是指导性的原则还是有的,以下原则是程序员们耳熟能详的编程原则,将其用在模块化划分上依然成立:
说到DDD,我们可能不得不说一下微服务,因为一般的理解是DDD因为微服务的兴起而重新被业界重视。事实上,DDD和微服务的关系被牵强式地放大了,DDD之于微服务,无外乎“DDD的限界上下文可以用于指导微服务的划分”,然而,在我们把限界上下文理解为模块后,这种说法也就不值得再成为一个单独的命题了。DDD的意义在于“DDD之于软件”,而不是“DDD之于微服务”.
在 码如云 ,我们采用了单体架构而非微服务,但这并不影响我们划分限界上下文(模块),我们通过Java分包的方式来划分模块.
码如云的模块关系并不复杂,仅包含3个顶层模块,一个是核心上下文(模块),其中包含各种核心的业务实体,比如应用和实例等,每个业务实体均被建模为一个 聚合根 ;第二个是后台管理上下文(模块),用于码如云的后台运营,包含客户关系、投诉管理和订单管理等;第三个是集成上下文(模块),用于处理与第三方的API集成。在前文中我们提到,登录功能可以被看做是通用子域而建模为一个独立的模块,但是在码如云中我们并未这么划分,而是将登录功能消化在了核心上下文中,因为其粒度尚未大到需要独立为一个模块的程度.
很多简单的东西被人为的复杂化了,当资深人士们还在高谈阔论微服务和SOA的区别时,Robert C. Martin(Bob大叔)站出来说,这俩就是一个东西。当下的DDD同样也在遭受着“被复杂化”的境遇,软件(至少企业级应用软件)向来的实践性是非常强的,结果从业者们自己把自己搞不会了,实则不应该呀!在本文中,我们说DDD的战略设计只是在解决软件的模块化划分的问题,并不是要贬低战略设计,而是希望读者看到战略设计的本质,进而从DDD中得到实实在在的好处,也推动这个行业可以健康地,不要那么浮夸式地发展.
最后此篇关于产品代码都给你看了,可别再说不会DDD(三):战略设计的文章就讲到这里了,如果你想了解更多关于产品代码都给你看了,可别再说不会DDD(三):战略设计的内容请搜索CFSDN的文章或继续浏览相关文章,希望大家以后支持我的博客! 。
我有几个系统使用 docker-compose 并且没有问题。 但是,我在这里有一个“向下”根本不做任何事情的地方。 'up'虽然完美。这是在 MacOS 上。 该项目的昵称是“ Storm ”,脚本
解释起来确实很奇怪,所以就这样...... 我正在从 phpmyadmin 获取包含未转义单引号的数据。我正在尝试转换'至'通过使用Content-Type: text/html;在 php
伙计们?在这里需要一些帮助。我使用委托(delegate)协议(protocol)将一些字符串从“第二个 View Controller ”传回给它的前一个。 我的数组附加了我在委托(delegate
我有以下 eval() 东西: c = Customer() eval("c.name = row.value('customer', '{c}')".format(c=column_name), {
我写了这个测试类: @ContextConfiguration(locations = { "classpath:/test/BeanConfig.xml" }) public class Candi
我这样写代码: @ContextConfiguration(locations = { "classpath:/test/BeanConfig.xml" }) @RunWith(SpringJUnit
假设我更改了文件,然后进行 pull 。 Git 会报错,因为本地仓库还没有保存,将被覆盖。如果我然后删除该添加并使文件与以前相同(与远程 repo 相同),那么会发生 pull 吗? 最佳答案 是的
我正在阅读《Java for Dummies》一书,但遇到了问题。我不明白为什么 @Override 不起作用。我确信这与我的代码有关,因为我之前已经获得了一个多态数组来使用覆盖,但它对我来说太简单了
我从我的项目中提取了这段代码,因为我试图找到我犯的一个错误,该错误使我的 BeginStoryboard 无法自行停止。我尽可能地简化了代码,但仍然没有发现问题。你认为它可能是什么?
这个问题在这里已经有了答案: Difference between char[] and char * in C [duplicate] (3 个答案) 关闭 7 年前。 我想我知道自己问题的答案,
我一直在使用 java 的 Scanner 类时遇到问题。我可以让它很好地读取我的输入,但问题是当我想要输出一些东西时。给定多行输入,我想在完全读取所有输入后只打印一行。这是我用来读取输入的代码:
对于这个问题,我已经用最简单的术语表达了这一点。 如果元素被点击,'active'类被添加到元素,'active'类从其他元素中移除。 但是,如果该元素是“事件的”并且它被第二次单击,则“事件”类不应
这会在桌面上创建一个新文件夹,但不会将文件夹 .pfrom 的内容 move 到文件夹 .pTo。 int main() { SHFILEOPSTRUCT sf = {0}; TCHA
我有一个关于多线程调试 DLL (/MDd) 和多线程调试 (/MTd) 设置的问题。它们之间的区别很明显:一个是使用动态库,一个是使用静态库。当我使用/MDd 编译我的程序时,一切都进行得很好。但是
我的问题是,如果我在页面加载时创建一个克隆变量,jQuery 只会 append 它一次。奇怪! Click to copy This is an element! $(document)
所以...我是一个开发 django 应用程序的新手,但是当我尝试通过 virtualbox heroku 运行 heroku run python manage.py syncdb 时,它一直在下面
我在 Spring Boot 初始化时遇到了问题。我在一个简单的 Spring Boot 项目中有这个结构。 com.project.name |----App.java (Annoted with
我在 www.7hermanosmx.com/menu.php 页面上有以下代码 - 一切正常,除了黄色框(类 menuholder)应该每行三个相互 float 。他们坚决拒绝这样做!我知道我做错了
我正在尝试在我正在构建的小型网站上添加一个下拉菜单。出于某种原因,我可以获得我想要向下滑动到 fadeOut() 的 div 并执行其他类似的操作,但我无法将它获取到 slideDown()。我不知道
我有一个不能正确 float 的 div。当您切换可见性时,它会覆盖一些当前文本,但我可以稍后移动它。只是好奇为什么它不能正确 float ! Simple Tabs with CSS &am
我是一名优秀的程序员,十分优秀!