领域驱动模型&CQRS学习(1),面试必备知识点

先自我介绍一下,小编浙江大学毕业,去过华为、字节跳动等大厂,目前阿里P7

深知大多数程序员,想要提升技能,往往是自己摸索成长,但自己不成体系的自学效果低效又漫长,而且极易碰到天花板技术停滞不前!

因此收集整理了一份《2024年最新Java开发全套学习资料》,初衷也很简单,就是希望能够帮助到想自学提升又不知道该从何学起的朋友。
img
img
img
img
img
img

既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,涵盖了95%以上Java开发知识点,真正体系化!

由于文件比较多,这里只是将部分目录截图出来,全套包含大厂面经、学习笔记、源码讲义、实战项目、大纲路线、讲解视频,并且后续会持续更新

如果你需要这些资料,可以添加V获取:vip1024b (备注Java)
img

正文

image

2.3、领域服务

领域中的一些概念不适合建模为对象,即不适合归类到实体对象或值对象,因为它们本质上就是一些操作或动作,而不是实物。这些操作或动作往往会涉及多个领域的对象,并且需要协调这些领域对象共同完成这个操作或动作。如果强行将这些操作职责分配给任何一个对象,则被分配的对象就会承担一些不该承担的职责,从而会导致对象的职责不明确。但是基于类的面向对象的语言规定,任何属性或行为都必须放在对象里面。所以我们需要寻找一种新的模式来表示这种跨多个对象的操作,DDD认为服务是一个很自然的范式,可用来对应这种跨多个对象的操作,所以就有了领域服务(Domain Service)这个模式。领域服务本来就是来处理这种场景的。比如要对密码进行解密,可以创建一个PasswordService来专门处理加解密的问题。
领域服务还有一个很重要的功能,就是可以避免领域逻辑泄露到应用层。因为如果没有领域服务,那么应用层会直接调用领域对象完成本该属于领域服务做的操作,这样一来,领域层可能会把一部分领域泄露到应用层。因此,引入领域服务可以有效防止领域层的逻辑泄露到应用层。对于应用层,从可理解的角度来讲,通过调用领域服务提供的简单、易懂、明确的接口肯定要比直接操纵领域对象容易得多。
那如何去识别领域服务呢?主要看它是否满足以下三个特征:
❑ 服务执行的操作代表了一个领域概念,这个领域概念无法自然隶属于一个实体或者值对象。
❑ 被执行的操作涉及领域中的其他的对象。
❑ 操作是无状态的。

2.4、聚合及聚合根

聚合通过定义对象之间清晰的所属关系和边界来实现领域模型的内聚,并避免了错综复杂的、难以维护的对象关系网的形成。聚合定义了一组具有内聚关系的相关对象的集合,我们把聚合看作一个修改数据的单元。聚合中所包含的对象之间具有密不可分的联系,它们是内聚在一起的。比如一辆汽车(Car)包含了引擎(Engine)、车轮(Wheel)和油箱(Tank)等组件,缺一不可。一个聚合中可以包含多个实体和值对象,因此聚合也被称为根实体。如下图所示就是一个聚合,Customer是聚合根也是实体,address是值对象,ContactInfo也是值对象。
image

聚合根(Aggregate Root)是DDD中的一个概念,是一种更大范围的封装,其把一组有相同生命周期、在业务上不可分隔的实体和值对象放在一起考虑,只有根实体可以对外暴露引用,也是一种内聚性的表现。但是要确定聚合边界要满足固定规则(Invariant),也就是在数据变化时必须保持一致性规则,具体规则如下:
❑ 根实体具有全局标识,最终负责检查规定规则。
❑ 聚合内的实体具有本地标识,这些标识在Aggregate内部才是唯一的。
❑ 外部对象不能引用除根Entity之外的任何内部对象。
❑ 只有Aggregate的根Entity才能直接通过数据库查询获取,其他对象必须通过遍历关联来发现。
❑ Aggegate内部的对象可以保持对其他Aggregate根的引用。
❑ 对Aggregate边界内的任何对象进行修改时,整个Aggregate的所有固定规则都必须满足。

2.5、边界上下文

领域实体是有边界上下文的,系统获取的数据是有界上下文(Bounded Context)下的数据。边界上下文(Bounded Context)在DDD里面是一个非常重要的概念,Bounded Context明确限定了模型的应用范围。在Context中,要保证模型在逻辑上统一,而不用考虑它是不是适用于边界之外的情况。在其他Context中,会使用其他模型,这些模型具有不同的术语、概念、规则和Ubiquitous Language。那么不同Context下的业务要互相通信怎么办?这就涉及跨边界的集成了,集成不能是简单的RPC服务调用,而需要一个专门的防腐层(Anti-Corruption)做转化。防腐层主要是对外部依赖解耦,以及避免外部领域概念污染Context内部实体语义。以我们真实的业务场景举个例子,比如会员这个概念在ICBU网站上指网站上的买主,但是在CRM领域中指客户,虽然很多的属性都是一样的,但是二者在不同的Context下其语义和概念是有差别的,我们需要用防腐层做一下转换,如图所示:

image

2.6、工厂

DDD中的工厂(Factory)也是一种体现封装思想的模式。DDD中引入工厂模式的原因是:有时创建一个领域对象是一件比较复杂的事情,而不是仅仅进行简单的new操作就可以。正如对象封装了内部实现一样(我们无须知道对象的内部实现就可以使用对象的行为),工厂则用来封装创建一个复杂对象的操作。工厂的作用是将创建对象的细节隐藏起来。

工厂在创建一个复杂的领域对象时,通常会知道该满足什么业务规则(它知道先怎样实例化一个对象,然后对这个对象做哪些初始化操作,这些规则就是创建对象的细节),如果传递进来的参数符合创建对象的业务规则,则可以顺利创建相应的对象;但是如果由于参数无效等不能创建出期望的对象,则应该抛出一个异常,以确保不会创建出一个错误的对象。

当然也不是所有都需要通过工厂来创建对象,当构造器很简单或者构造对象不依赖于其他对象来创建时,我们只需要简单地使用构造函数创建对象就可以。隐藏创建对象的好处是显而易见的,这样可以不让领域层的业务逻辑泄露到应用层,同时也减轻了应用层的负担,它只需要简单地调用领域工厂创建符合期望的对象即可。

2.7、仓储/资源库

领域模型中的对象自从被创建出来后不会一直在内存中活动,当它不活动时会被持久化到数据库中,然后当需要的时候我们会重建该对象。重建对象就是根据数据库中已存储的对象的状态重新创建对象。所以重建对象是一个和数据库打交道的过程。从更广义的角度来理解,我们经常会像集合一样从某个类似集合的地方根据某个条件获取一个或一些对象,往集合中添加对象或移除对象。也就是说,我们需要提供一种机制,可以提供类似集合的接口来帮助我们管理对象。仓储(Repository)就是基于这样的思想被设计出来的。

仓储里面存放的对象一定是聚合,原因是领域模型中是以聚合的概念去划分边界的。聚合是我们更新对象的一个边界,事实上我们把整个聚合看成一个整体概念,要么一起被取出来,要么一起被删除。我们永远不会单独对某个聚合内的子对象进行单独查询或做更新操作。因此,我们只为聚合设计仓储。

仓储还有一个重要的特征就是分为仓储定义部分和仓储实现部分,在领域模型中我们定义仓储的接口,而在基础设施层实现具体的仓储。这样设计的原因是:仓储背后的实现都是在和数据库打交道,但是我们又不希望调用方(如应用层)把重点放在如何从数据库获取数据的问题上,因为这样做会导致调用方(应用层)代码混乱,很可能会因此而忽略了领域模型的存在。所以我们需要提供一个简单明了的接口供调用方使用,确保客户能以最简单的方式获取领域对象,从而可以让它在不被数据访问代码打扰的情况下协调领域对象以完成业务逻辑。这种通过接口来隔离封装变化的做法其实很常见。由于对外暴露的是抽象的接口并不是具体的实现,所以可以随时替换仓储的真实实现。

2.8、CQRS架构

CQRS的核心思想是将应用程序的查询部分和命令部分完全分离,这两部分可以用完全不同的模型和技术去实现。比如命令部分可以通过领域驱动设计来实现;查询部分可以直接用最快的非面向对象的方式来实现,比如用SQL。这样的思想有很多好处:
❑ 实现命令部分的领域模型,不用经常为了考虑领域对象可能会被如何查询而做一些折中处理。
❑ 由于命令和查询是完全分离的,所以这两部分可以用不同的技术架构实现,包括数据库设计理论上都可以分开设计,每一部分可以充分发挥其长处。
❑ 因为命令端没有返回值,所以可以像消息队列一样接受命令,放在队列中,慢慢处理;处理完后,可以通过异步的方式通知查询端,这样查询端可以做数据同步的处理。

CQRS架构的优缺点如下表所示:

image

2.9、领域事件

领域事件(Domain Event)是最近几年才加入DDD生态系统的,通过领域事件的方式达到各个组件之间的数据一致性。领域事件的额外好处在于它可以记录发生在软件系统中的所有重要修改,这样可以很好地支持程序调试和商业智能化。在CQRS架构的软件系统中,领域事件还用于写模型和读模型之间的数据同步。再进一步发展,事件驱动架构可以演变成事件源(Event Sourcing),即对聚合的获取并不是通过加载数据库中的瞬时状态实现的,而是通过重放发生在聚合生命周期中的所有领域事件完成的。

事件溯源(Event Sourcing)是基于DDD设计的,对于聚合,不保存聚合的当前状态,而是保存对象上所发生的每个事件。当要重建一个聚合对象时,可以通过回溯这些事件(即让这些事件重新发生)来让对象恢复到某个特定的状态;因为有时一个聚合可能会发生很多事件,所以如果每次要在重建对象时都从头回溯事件,会导致性能低下,所以我们会在一定时候为聚合创建一个快照。这样,我们就可以基于某个快照开始创建聚合对象了。

2.10、 领域驱动模型的设计步骤

领域驱动模型的设计步骤如下:
(1)根据需求建立一个初步的领域模型,识别出一些明显的领域概念及它们之间的关联,关联可以暂时没有方向但需要有一对一、一对多、多对多这些关系。可以用文字精确且没有歧义地描述出每个领域概念的涵义及包含的主要信息。
(2)分析主要的软件应用程序功能,识别出主要的应用层的类,这样有助于及早发现哪些是应用层的职责,哪些是领域层的职责。
(3)进一步分析领域模型,识别出哪些是实体,哪些是值对象,哪些是领域服务。
(4)分析关联,通过对业务进行更深入分析及各种软件设计原则、性能方面的权衡,明确关联的方向或者去掉一些不需要的关联。
(5)找出聚合边界及聚合根,这是一件很有难度的事情,因为在分析的过程中往往会碰到很多难以清晰判断的问题,此时需要我们凭借经验找出正确的聚合根。
(6)为聚合根配备仓储,一般情况下是为一个聚合分配一个仓储,此时只要设计好仓储的接口即可。
(7)捋顺实际业务应用场景,确定我们设计的领域模型能够有效解决业务需求。
(8)考虑如何创建领域实体或值对象,明确是通过工厂还是直接通过构造函数实现。
虽然上面介绍了设计领域模型的步骤,但是领域建模是一个不断重构、持续完善模型的过程。大家会在讨论中将变化的部分反映到模型中,从而使模型不断细化并朝正确的方向走。领域建模是领域专家、设计人员、开发人员之间沟通交流的过程,是大家工作和思考问题的基础。

2.11、领域驱动框架现状

自从Eric Evan提出DDD领域驱动设计以来已经过了很多年了,现在已经有很多人在学习或实践DDD。但是目前来看能够支持DDD开发的框架并不多,至少在国内比较罕见。在Java平台上,国外比较受欢迎的领域驱动框架是Axon Frameworkhttp://github.com/AxonFramework/AxonFramework),该框架发展至今相对来说比较活跃,目前Github上星标已经超过1000。还有就是banq的Jdon framework(https://github.com/banq/jdonframework),这是基于DDD+CQRS+EventSourcing的开发,也是基于Java平台的。
上表中列举的领域驱动框架各有优点和缺点。如果就Java平台来讲,可以尝试使用Axon Framework,目前已经支持Spring Cloud。但是它不是目前最好的领域驱动框架,下面将介绍的Halo框架会更有优势。

3、Halo框架概述

Halo框架是基于领域驱动+CQRS+扩展点+流程编排的应用框架,致力于采用领域驱动的设计思想,规范控制程序员的随心所欲,从而解决软件的复杂性问题。架构设计原则非常简单,即在高内聚、低耦合、可扩展、易理解的大的指导思想下,尽可能贯彻面向对象的设计思想和原则。

最后

一次偶然,从朋友那里得到一份“java高分面试指南”,里面涵盖了25个分类的面试题以及详细的解析:JavaOOP、Java集合/泛型、Java中的IO与NIO、Java反射、Java序列化、Java注解、多线程&并发、JVM、Mysql、Redis、Memcached、MongoDB、Spring、Spring Boot、Spring Cloud、RabbitMQ、Dubbo 、MyBatis 、ZooKeeper 、数据结构、算法、Elasticsearch 、Kafka 、微服务、Linux。

这不,马上就要到招聘季了,很多朋友又开始准备“金三银四”的春招啦,那我想这份“java高分面试指南”应该起到不小的作用,所以今天想给大家分享一下。

image

请注意:关于这份“java高分面试指南”,每一个方向专题(25个)的题目这里几乎都会列举,在不看答案的情况下,大家可以自行测试一下水平 且由于篇幅原因,这边无法展示所有完整的答案解析

网上学习资料一大堆,但如果学到的知识不成体系,遇到问题时只是浅尝辄止,不再深入研究,那么很难做到真正的技术提升。

需要这份系统化的资料的朋友,可以添加V获取:vip1024b (备注Java)
img

一个人可以走的很快,但一群人才能走的更远!不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人,都欢迎加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!
-U42CPeos-1713563941884)]

一个人可以走的很快,但一群人才能走的更远!不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人,都欢迎加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!

  • 18
    点赞
  • 30
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值