前言

领域驱动模型设计在业界也喊了几年口号了,但是对于很多“务实”的程序员来说,纸上谈“术”远比敲代码难得太多太多。本人能力有限,在拜读相关作品时既要隐忍书中晦涩难懂的专业名词,又要去迎合西方大牛在撰写的过程中融入的西式故事。我想总会有一部分水平和我类似的码农们,需要一份对系统阐述DDD小白文化的文本。因此,本人便自不量力地结合一些简单的项目经验,将领域驱动模型设计思想从理解到落地的实施和总结分享给诸位。当然,如果是某些行业先锋不幸看到本人稚嫩的文字时,就当作是马戏团中的小丑,一笑了之翻页便可。

思维的入门

在学习架构思想的初期,特别时面对架构模型时(例如六边形架构、领域服务拆分),我总会不自然地在对号入座,思考在模型中的这一块放到代码实现上是Controller层还是Dao层,是采用消息中间件还是NoSQL缓存。这种自动联想的“被动技能”在学习微服务设计架构思想的过程中是致命的,过于专注业务的技术实现而脱离架构思想本身的大局观,就容易陷入用“具体”无法概括“抽象”的处境。

对此,让我们先忘了平日在开发中使用的各种被分类到细致入微的技术,带着一种阅读“无用”文学而非可以模仿实操的工具书的心态一起来对领域驱动设计做一个基础性的理解。

1 常用的服务拆分方法

1.1 根据业务能力进行服务拆分

创建微服务架构的策略之一就是采用业务能力进行服务拆分,这也是目前市面上大部分产品设计初期采用的方式。主要原因是这种方法对于架构师来说是比较容易实施的,就好像在做一个分类游戏,关于用户的注册登录以及信息管理可以划分为一个业务,关于文章的发布下架以及内容更迭可以划分为一个业务。

这样的分类手段核心是以业务活动进行划分,也保证了大部分面向对象的程序员在代码实现时可以更快地、更明确地创建实体类,从而开辟出一个个针对不同业务功能的微服务。

倘若我们以程序员盘踞的技术论坛/博客为例,那么它们必须要实现最基础的三大业务:1、用户的注册登录以及用户对个人信息的操作;2、用户可以发布/修改/删除自己的文章;3、网站的后台管理员可以对前面两者进行更高权限的操作。至于更多的用户评论、用户私聊、vip充值等一些功能则是拓展,毕竟没有它们也不会影响到整个网站的正常运作,因此暂不加入讨论。

所以根据上述最基础的三大业务,如果要进行微服务架构设计(当然现实中不会有为了如此简陋的网站采用微服务,不然会被从业的程序员背地挖苦痛骂),我们可以画出相应的映射图【图1-1】:

采用业务能力进行拆分固然方便了架构师和程序员,在应对一般的企业项目时,这种方式是非常稳妥的方案,架构中每个划分出的服务内部后期可以根随功能的需求增加而逐渐迭代(例如,我现在需要文章发布时可以附带图片,那么可以在实现文章业务的服务中添加相关的接口与具体实现代码),但是整体的项目架构是保持不变的。

话虽如此,但读者们要知道上面仅仅是为了实现技术博客网站最基本业务而定义的“初代”架构。一种情况是随着整个技术博客网站的日益庞大,为了迎合用户的需要,我们不得不扩展网站的功能性,例如增添用户对文章点赞/收藏/留言,后台对文章内容是否健康的审核等等,这时候初代被划分好的服务内部就会日益“庞大”,需要我们重新操刀对其进行分解切割。

另外一种情况是由于用户量的提升,致使服务之间的远程调用、进程间的通信次数骤增而导致请求响应效率逐步低下,例如拥有庞大数量的读者用户在访问文章时不仅要通过文章服务获取文章内容,还要通过文章的作者每次调用用户服务来获取作者个人信息简介(在没有缓存机制,每个服务根据业务主体分割明确的假设情况),我们又得考虑把一些服务组合在一起:

例如当我们现在常用的注册/登录手段——利用第三方服务进行短信验证,如果只是为了实现用户注册/登录功能,完全可以并入到用户的服务中【图1-2】:

一旦不单用户登录/注册情况用到短信验证,例如要充值时候也要用到短信验证支付,那么就得把它独立出来【图1-3】:



上面的问题正是以业务进行服务拆分时难以避免的,因为业务必然是不断发展的(参考市面上众多臃肿的软件),被繁杂的业务牵着鼻子走的架构模式必然会陷入到不停拆分或重组的境地,那么服务内部以及服务之间的关系也将逐步模糊紊乱。

1.2 根据子域进行服务拆分

即使这是一段很枯燥的历史,但为了感谢曾在这个领域躬耕的技术先驱们我还是要带上这段文字:Eric Evans在他的经典著作中(Addison-Wesley Professional)提出的领域驱动设计是构建复杂软件的方法论,这些软件通常都是以面向对象和领域模型为核心。

既然是方法论,它提供的是“怎么办”的理论体系。类比蛋炒韭菜,我所能提供的是做这道菜需要什么工具和佐料,放油放蛋放盐放韭菜的先后顺序和比例,至于真正做起来火候的大小,炒菜的姿势,蛋菜的克量等都是视情况而定,并没有固定的要求。这又回到我在“思维入门”中提到的,不要用机械的“具体”去反推“抽象”理论,因为炒菜的姿势最多能影响的是菜的口味,并非是完成这道菜的必要条件。

下面让我们来理解DDD中重要的两个概念:子域和界限上下文。我先以好理解的方式在各位脑海中勾勒出这两者的基本认识,然后各位再去看专业解释会好接受的多。

如果把一个项目业务比作一个国家整体,而子域相当于国家内部的各个省份,这些省份细致地划分了国家每个地域面积大小和居住人群,而所有省份的聚合又构成了整个国家。作为限界上下文,其英文为bounded context,可以直译成“有界的环境”,那么套入前文各位可能举一反三理解成“省界”。但事实并非如此,以“省份”类比子域的话,省界并非是限制人口流动的主要原因,限制人流的本质在于一个省份有一个省份的风土人情和文化语言,例如广东省用粤语交流,福建省用闽南语交流,这些语言在省份内部是大家达成共识都能理解的,但跑到广东说福建话只会让本地人摸不着头脑。

因此,每个省份(子域)之间产生的交流障碍归根于它们各自内部通用语言环境不同(限界上下文)。这个举例或许不符合社会学对于人口流动的分析,但拿来解释子域和限界上下文还是很贴切的。

那么让我们引入教科书对二者的解释来巩固诸位对它们的记忆:子域是领域的一部分,领域是DDD中用来描述应用程序问题域的一个术语。识别子域的方法和识别业务能力一样:分析业务并识别业务的不同专业领域,分析产生的子域定义结果也会和识别业务能力得到的结果非常相近。DDD把领域设计模型的边界成为界限上下文,当使用微服务架构时,每一个界限上下文对应一个或则一组服务,我们可以通过DDD方式定义子域,并把子域对应为每一个服务【图1-4】。

1.3 拆分单体应用的难点

1.网络延时

网络延时是分布式系统中一直存在的问题。不论是以业务进行拆分还是子域进行拆分,对服务不断细化分解会导致各个服务之间的大量往返调用。即使可以通过批量处理API在一次往返中获取多个对象,从而减少延时。但是在其他情况下,解决方案是把多个服务整合到一起,用变成语言中的函数调用替换昂贵的进程通信。

2.同步进程间通信导致可用性降低

举一个最简单的例子,当我们在某东下单一件商品时创建了订单,创建订单的过程中需要获取商品的详细信息和购买者的详细信息,而这其中有一个服务出现了不可用的状态就会导致整个业务创建失败,这种同步进程通信带来的可用性降低让我们不得不折中采用异步消息进行处理。

3.服务之间维持数据的一致性

当我们对服务进行拆分后,服务之间如何保持数据一致性成为重点和难点。还是以某东为例,在活动期间大量用户可能在同一刻参与了秒杀活动,而对于仓存服务与商品服务之间如何保证它们在数量的一致性(比如前台有100个用户下单,那么对于商品来说只需要在原有的数量上减去100然后返回给前端页面作为商品信息的一部分及时展示给用户还有多少存货便可,但是实际上真正需要扣减的应该发生在仓库服务中,因为仓库服务内存储的才是实际库存),怎么让一波狂欢后实际库存与前端保持一致是业务中必须攻克的难题。

4.上帝类阻碍了服务的拆分

分解的一部分障碍就是所谓的上帝类,即全局类或则是“公用”类。上帝类通常为应用程序不同方面实现业务逻辑。

以美团外卖业务举例,一个不经思考设计的订单类中会将以下所有信息属性直接构建成一个类:商家信息属性(商家名称、商家地址等)、用户信息属性(账户、昵称、地址等)、外卖商品属性(外卖商品名称、价格、配料等)、配送方属性(配送员身份、配送员电话号码、配送起始时间、配送截止时间等)。不过这些属性涉及了不同服务中的应用程序,导致了商品系统中必然存在的订单所包含的信息变得特别庞大,并且与其他服务之间因为共同的属性保持着“暧昧”的联系。

一种解决方法是在数据库中创立一个公用的订单数据库,处理订单的所有服务使用此数据库,但这就出现了“紧耦合”的情况。

对此应用DDD将每个服务视为“孤岛”般的子域(尽量不与其他服务发生在属性的上纠葛,形成一座具有特色便于识别的“孤岛”)。所以让我们看看自己手机上美团APP中客户订单,然后再看看拿到外卖时钉在外卖上的商家收到的订单,幸运的话再看看外卖小哥送外卖时接收的订单,它们所包含的信息内容和信息数量绝对是不同。

这代表着在商家子域、用户子域、配送子域中我们根据子域的不同定义了不同侧重点的“订单”,而不是一股脑地都塞进全局订单类里让不同服务进行共享。也正是不同侧重点的“订单”只有在自己子域中才是“有效的”,“能被读懂的”(总不可能让商家去看外卖小哥的订单信息,用户去看商家的订单信息),子域便有了与之对应的界限上下文。

这一章的基础内容便到此结束,下一章可能会讲一下服务的进程通信或则是Saga管理事务,尽情期待。

领域驱动模型DDD(一)——服务拆分策略的更多相关文章

  1. 领域驱动模型DDD(二)——领域事件的订阅/发布实践

    前言 凭良心来说,<微服务架构设计模式>此书什么都好,就是选用的业务过于庞大而导致代码连贯性太差,我作为读者来说对于其中采用的自研框架看起来味同嚼蜡,需要花费的学习成本实在是过于庞大,不仅 ...

  2. 领域驱动模型DDD(三)——使用Saga管理事务

    前言 虽然一直说想写一篇关于Saga模式,在多次尝试后不得不承认这玩意儿的仿制代码真不是我一个菜鸟就能完成的,所以还是妥协般地引用现成的Eventuate Tram Saga框架(虽然我对它一直很反感 ...

  3. 领域驱动设计(DDD)

    领域驱动设计(DDD)实现之路 2004年,当Eric Evans的那本<领域驱动设计——软件核心复杂性应对之道>(后文简称<领域驱动设计>)出版时,我还在念高中,接触到领域驱 ...

  4. 领域驱动设计(DDD:Domain-Driven Design)

    领域驱动设计(DDD:Domain-Driven Design) Eric Evans的"Domain-Driven Design领域驱动设计"简称DDD,Evans DDD是一套 ...

  5. python 全栈开发,Day116(可迭代对象,type创建动态类,偏函数,面向对象的封装,获取外键数据,组合搜索,领域驱动设计(DDD))

    昨日内容回顾 1. 三个类 ChangeList,封装列表页面需要的所有数据. StarkConfig,生成URL和视图对应关系 + 默认配置 AdminSite,用于保存 数据库类 和 处理该类的对 ...

  6. 关于领域驱动设计 DDD(Domain-Driven Design)

    以下旨在 理解DDD. 1.     什么是领域? 妈妈好是做母婴新零售的产品,应该属于电商平台,那么电商平台就是一个领域. 同一个领域的系统都有相同的核心业务. eg: 电商领域都有:商品浏览.购物 ...

  7. 【tornado】系列项目(一)之基于领域驱动模型架构设计的京东用户管理后台

    本博文将一步步揭秘京东等大型网站的领域驱动模型,致力于让读者完全掌握这种网络架构中的“高富帅”. 一.预备知识: 1.接口: python中并没有类似java等其它语言中的接口类型,但是python中 ...

  8. 【tornado】系列项目(二)基于领域驱动模型的区域后台管理+前端easyui实现

    本项目是一个系列项目,最终的目的是开发出一个类似京东商城的网站.本文主要介绍后台管理中的区域管理,以及前端基于easyui插件的使用.本次增删改查因数据量少,因此采用模态对话框方式进行,关于数据量大采 ...

  9. 基于领域驱动设计(DDD)超轻量级快速开发架构(二)动态linq查询的实现方式

    -之动态查询,查询逻辑封装复用 基于领域驱动设计(DDD)超轻量级快速开发架构详细介绍请看 https://www.cnblogs.com/neozhu/p/13174234.html 需求 配合Ea ...

随机推荐

  1. python基础语法_3面向对象

    http://www.runoob.com/python3/python3-class.html https://www.imooc.com/learn/317 慕课网:987809563@qq.co ...

  2. Oracle用户创建、删除和授权等方法总结

    一.查看用户及权限 1.查询所有用户: 1.1.查看所有用户基本信息 select * from all_users; 1.2.查看所有用户相信信息 select * from dba_users; ...

  3. MyBatis功能点二:从责任链设计模式的角度理解插件实现技术

    MyBatis允许对其四大组件(Executor,StatementHandler,ParameterHandler, ResultSetHandler)进行增强处理.在创建四大组件对象的时候 1.每 ...

  4. .net core 和 WPF 开发升讯威在线客服系统:调用有道翻译接口实现实时自动翻译的方法

    业余时间用 .net core 写了一个在线客服系统.并在博客园写了一个系列的文章,写介绍这个开发过程. 我把这款业余时间写的小系统丢在网上,陆续有人找我要私有化版本,我都给了,毕竟软件业的初衷就是免 ...

  5. JVM学习——G1垃圾回收器(学习过程)

    JVM学习--G1垃圾回收器 把这个跨时代的垃圾回收器的笔记独立出来. 新生代:适用复制算法 老年代:适用标记清除.标记整理算法 二娃本来看G1的时候觉得比较枯燥,但是后来总结完之后告诉我说,一定要慢 ...

  6. Nginx基本简述

    一.Nginx简介 Nginx是一个开源且高性能.可靠的HTTP中间件.代理服务. 开源:直接获取源代码 高性能:支持海量高并发 1.nginx应用场景: 静态处理   (对静态页面的处理,不管是ht ...

  7. 蓝桥杯2022年java试题

    一:基础练习: (本文只附代码,解析后续修改后添上) 1.a+b问题: 代码如下: 1 import java.util.*; 2 public class Main { 3 public stati ...

  8. k8s基础环境配置:基于CentOS7.9

    k8s基础环境配置:基于CentOS7.9 wmware15安装centos7.9:https://www.cnblogs.com/uncleyong/p/15261742.html 1.配置静态ip ...

  9. 别人都在用数据分析软件,你还在用excel做数据分析?

    之前听朋友吐槽过,他们是上千人的企业,但做数据分析居然还是靠手动上传数据,而且还是用的excel做的.但其实excel并不是企业做数据分析的好工具. 数据分析是指用适当的统计分析方法对收集来的大量数据 ...

  10. Tableau绘图一热图、日历图、人口金字塔、标靶图、凹凸图、帕累托图

    Tableau绘图一热图.日历图.人口金字塔.标靶图.凹凸图.帕累托图 本文首发于博客冰山一树Sankey,去博客浏览效果更好.直接右上角搜索该标题即可 一.热图 例子:示例超市 可以通过更改颜色来改 ...