DDD 实战(一)

目录

一、什么是微服务

二、微服务架构的优缺点

三、微服务架构的误区

 四、领域与领域模型

五、通用语言与界限上下文

六、实体类与值对象

七、聚合与聚合根

八、领域服务与应用服务


一、什么是微服务

       传统的软件项目大部分都是单体结构,也就是项目中的所有代码都放到同一个应用程序中,一般它们也都运行在同一个进程中。

单体结构的项目有结构简单、部署简单等优点,但是有如下的缺点。
1、代码之间耦合严重,代码的可维护性低。
2、项目只能采用单一的语言和技术栈,甚至采用的开发包的版本都必须统一。
3、一个模块的崩溃就会导致整个项目的崩溃。
4、我们只能整体进行服务器扩容,无法对其中一个模块进行单独的服务器扩容。
5、当需要更新某一个功能时,我们需要把整个系统重新部署一遍,这会导致新功能的上线流程变长。

 微服务架构把项目拆分为多个应用程序,每个应用程序单独构建和部署。

二、微服务架构的优缺点

 微服务架构有如下的优点。
1、每个微服务只负责一个特定的业务,业务逻辑清晰、代码简单,对于其他微服务的依赖非常低,因此易于开发和维护。
2、不同的微服务可以用不同的语言和技术栈开发。
3、一个微服务的运行不会影响其他微服务。
4、可以对一个特定的微服务进行单独扩容。
5、 当需要更新某一个功能的时候,我们只需要重新部署这个功能所在的微服务即可,不
需要重新部署整个系统。
微服务架构的缺点如下。
1、在单体结构中,运维人员只需要保证一个应用的正常运行即可,而在微服务架构中
运维人员需要保证多个应用的正常运行,这给运维工作带来了更大的挑战。
2、在单体结构中,各模块之间是进程内调用,数据交互的效率高,而在微服务架构中,
各微服务之间要通过网络进行通信,数据交互的效率低。
3、在单体结构中,各模块之间的调用都是在进程内进行的,实现容错、事务一致性等比较容易,而在微服务架构中,各微服务之间通过网络通信,实现容错、事务一致性等非常困难。

三、微服务架构的误区

       在应用微服务架构的时候,我们可能会有微服务切分过细和微服务之间互相调用过于复杂
有的技术人员并没有深刻理解微服务的本质,迷信微服务,把一个很简单的项目拆分成了这两个主要的误区。几十个甚至上百个微服务,这么多微服务的管理是非常麻烦的。在设计不好的微服务架构中,微服务之间的调用关系非常复杂,一个来自客户端的请求甚至要经过七八层的微服务调用,这样糟糕的设计不仅导致系统间耦合严重,而且使得服务器端的处理效率非常低。

 四、领域与领域模型

       领域(包含子域)可以按照功能划分为核心域、支撑域、通用域。核心域指的是解决项目的核心问题的领域,支撑域指的是解决项目的非核心问题的领域,而通用域指的是解决通用问题的领域。核心域是和组织业务紧密相关的、个性化的领域,支撑域则具有组织特性,但不具有通用性,而通用域则是可以被很多其他领域复用的领域。

       确定一个领域之后,我们就要对领域内的对象进行建模,从而抽象出模型的概念,这些领域中的模型就叫作领域模型 (domain model)。与领域模型对应的概念是“事务脚本”(transaction script),事务脚本是指使用技术人员的语言去描述和实现业务事务,说通俗一点儿就是没有太多设计,没有考虑可扩展性、可维护性.通过使用 if、for 等语句用流水账的形式编写代码。

五、通用语言与界限上下文

        在进行系统开发的时候,非常容易导致歧义的是不同人员对于同一个概念的不同描述。比如用户说“我想要商品可以被删除”,开发人员就开发了一个使用 Delete 语句把商品从数据库中删除的功能;后来用户又说“我想把之前删除的商品恢复回来”,开发人员就会说“数据已经被删除了,恢复不了”,用户就会生气地说“Windows 里的文件删除后都能从回收站里恢复,你们删除的怎么就恢复不了呢”。这其实就是开发人员和用户对于“删除”这个词语的理解不同造成的。再如,电商系统的支付模块的开发人员和后台管理模块的开发人员聊了许久关于“用户管理”的功能,最后才发现支付模块开发人员说的“用户”指的是购买商品的“客户”,而后台管理模块开发人员说的“用户”指的是“网站管理员”。     

        在描述业务对象的时候,拥有确切含义的、没有二义性的语言是非常重要的,这样的语言就是“通用语言”。在应用 DDD 的时候,团队成员必须对于系统内的每一个业务对象有确定的、无二义性的、公认的定义。通用语言离不开特定的语义环境,只有确定了通用语言所在的边界,才能没有歧义地描述一个业务对象。在学习 DDD 的时候,我们需要了解很多的名词,比如领域、子域、实体类、值对象、聚着唯一的含义合等,尽管这些概念比较难懂,但是所有学习和应用 DDD 的人员拥有同样一套通用语言,所有人员都用同样的通用语言进行表述和沟通,可以减少误解。同时,DDD 中的这些概念也是在 DDD 这个界限上下文中才有这些含义的,在其他界限上下文中可能就有其他含义了。

六、实体类与值对象

       在 DDD 中,“标识符”用来唯一定位一个对象,在数据库中我们一般用表的主键来实现标识符。当谈到标识符的时候,我们是站在业务的角度思考问题,而谈到主键的时候,我们是站在技术的角度思考问题。
        在 DDD 中大量存在着这样一类对象,它们拥有唯一的标识符,标识符的值不会改变,而对象的其他状态则会经历各种变化,这样的对象可能会被持久化地保存在存储设备中,即使软件重启,我们也可以把持久化在存储设备中的对象还原出来,我们把这样的对象称为实体类(entity)。标识符是用来跟踪对象状态变化的,一个实体类的对象无论经历怎样的变化,只要看到标识符的值没有变化,我们就知道它们还是那个对象。在具体实现 DDD 的时候,实体类一般的表现形式就是 EF Core 中的实体类,实体类的Id属性一般就是标识符,Id 属性的值不会变化,它标识着唯一的对象,实体类的其他属性则可能在运行时被修改,但是只要 Id 不变,我们就知道前后两个对象指的是同一个对象。我们可以把实体类的对象保存到数据库中,也可以把它从数据库中读取出来。在 DDD 中还存在着一些没有标识符的对象,它们也有多个属性,它们依附于某个实体类对象而存在,这些没有标识符的对象叫作值对象。同一个值对象不会被多个实体类对象引用值对象一般是不可变的,也就是值对象的属性不可以修改。因此如果我们要修改实体类中的一个值对象属性,我们只能创建一个新的值对象来替换旧的值对象。

       实体类帮助我们跟踪对象的变更,而值对象则帮助我们把多个相关属性当做一个整体。

七、聚合与聚合根

       一个系统中会有很多的实体类(包含值对象),这些实体类之间有的关系紧密,有的关系很弱,有的没有关系。面向对象设计的一个重要原则就是“高内聚,低耦合"。因此,我们把关系紧密的实体类放到一个聚合(aggregate) 中。聚合体现的是现实世界中整体和部分的关系,比如订单与订单明细。整体封装了对部分的操作,部分与整体有相同的生命周期。部分不会单独与外部系统交互,与外部系统的交互都由整体来负责。
       比如,订单与订单明细之间显然是整体和部分的关系,因为删除了订单,订单明细也就洋失了,而且外部系统不会直接引用订单明细,只会引用订单。因此我们把订单和订单明细设计为一个聚合,并且把订单作为聚合根,外部系统只能引用订单,对订单明细的操作都通过订单来进行。
而用户和订单之间的关系就不是整体与部分的关系,因为删除了订单,用户还是可以存在的。有人可能会认为,删除了用户,这个用户的订单也就消失了,因此用户和订单是整体和部分的关系。但是聚合关系还有一个判断标准就是“实体类能否单独和外部系统交互”,很显然,在系统中订单是可以单独和外部系统交互的,比如支付系统中就可以直接引用订单,因此用户和订单之间不是聚合关系。
       有的情况下,聚合关系的划分也不是一成不变的,不同的业务流程决定了不同的划分方式。比如新闻和新闻的评论就既可以设计成同一个聚合,也可以放到不同的聚合中。如果在网站中,新闻和评论都是一起出现的,评论不会单独出现,我们就可以把它们设计成同一个聚合,把新闻设置为聚合根。但是如果在网站中,有“全站热门评论榜”“分享评论到朋友圈”等把评作为一个独立的实体类看待的情况,我们就可以把新闻和评论设置为两个聚合。
       在设计聚合的时候,要尽量把聚合设计得小一点儿,一个聚合只包含一个聚合根实体类和I不可分的实体类,实体类中只包含最小数量的属性。小聚合有助于进行微服务的拆分,也有助减少数据修改冲突。设计聚合的一个原则就是: 聚合宁愿设计得小一点儿,也不要设计得太大。

八、领域服务与应用服务

       聚合根的实体类中没有业务逻辑代码,只有对象的创建、对象的初始化、状态管理等与个体相关的代码。对于聚合内的业务逻辑,我们编写领域服务 (domain service),而对于跨聚合协作的逻辑,我们编写应用服务 (application service)。应用服务协调多个领域服务来完成一个用例.在 DDD 中,一个典型的用例的处理流程如下。

第1步,准备业务操作所需要的数据,一般是从数据库或者其他外部数据源获取数据。

第2步,执行由一个或者多个领域模型做出的业务操作,这些操作会修改实体类的状态或者生成一些操作结果。

第3 步,把对实体类的改变或者操作结果应用于外部系统,比如保存对实体类的修改到数据库或者把计算结果通过短信发送给用户。

       聚合内数据操作的关系是非常紧密的,我们要保证事务的强一致性,而聚合间的协作是关系不紧密的,因此我们只要保证事务的最终一致性即可。聚合内的若干相关联的操作组成一个“工作单元”,这些工作单元要么全部成功,要么全部失败。因为领域服务不依赖外部系统、不保存状态,所以领域服务比应用服务更容易进行单元测试,对于提高系统的质量有帮助。

猜你喜欢

转载自blog.csdn.net/xxxcAxx/article/details/128425148
ddd
今日推荐