首页
3D照片墙
统计
留言
Search
1
1.OAuth 的简单理解
115 阅读
2
多个拦截器的执行顺序
105 阅读
3
基于Annotation方式的声明式事务
102 阅读
4
6.设计模式汇总
101 阅读
5
Unity 依赖注入
98 阅读
Java
JDBC
Spring
Spring MVC
SpringBoot
SpringCloud
MybatisPlus
Mybatis
Maven
SpringSecurity
JVM
java注解与反射
Java JUC并发编程
SSM
.NET
IdentityServer4
EF
.Net Core
AbpVNext + DDD
.NET MVC Api
前端
Jquery&JavaScript
uniapp
VUE
Echars
Vue底层原理
Python
Django
软考笔记
软件设计师
1.计算机组成与体系结构
10.面向对象技术
11.UML类图建模
12.面向对象程序设计
13.数据结构
14.算法基础
16.知识产权标准化
17.程序设计语言
2.操作系统
3.数据库
4.数据库设计
5.计算机网络
6.信息安全
7.系统开发基础
8.项目管理
9.数据流图
架构设计
CQRS架构
DDD架构
数据库技术
SQL锁
SqlServer
Oracle 主从备份
Oracle RAC集群
Mysql
云原生/容器技术
kubernetes
Docker
数据结构与算法
常用中间件
Redis
RabbitMQ 消息队列
ElasticSearch
其他
PHP
OAuth 2.0
WebSocket
ArkTs Harmony 开发
运维
Search
标签搜索
排序算法
vue
算法
遍历
docker
线性
数组
dom
synchronized
数据库
xml语言
log4j
bigint
静态函数
静态方法
哈夫曼树
const
冒泡排序
商标设计
命令模式
Bi8bo
累计撰写
304
篇文章
累计收到
6
条评论
首页
栏目
Java
JDBC
Spring
Spring MVC
SpringBoot
SpringCloud
MybatisPlus
Mybatis
Maven
SpringSecurity
JVM
java注解与反射
Java JUC并发编程
SSM
.NET
IdentityServer4
EF
.Net Core
AbpVNext + DDD
.NET MVC Api
前端
Jquery&JavaScript
uniapp
VUE
Echars
Vue底层原理
Python
Django
软考笔记
软件设计师
1.计算机组成与体系结构
10.面向对象技术
11.UML类图建模
12.面向对象程序设计
13.数据结构
14.算法基础
16.知识产权标准化
17.程序设计语言
2.操作系统
3.数据库
4.数据库设计
5.计算机网络
6.信息安全
7.系统开发基础
8.项目管理
9.数据流图
架构设计
CQRS架构
DDD架构
数据库技术
SQL锁
SqlServer
Oracle 主从备份
Oracle RAC集群
Mysql
云原生/容器技术
kubernetes
Docker
数据结构与算法
常用中间件
Redis
RabbitMQ 消息队列
ElasticSearch
其他
PHP
OAuth 2.0
WebSocket
ArkTs Harmony 开发
运维
页面
3D照片墙
统计
留言
搜索到
8
篇与
的结果
2022-07-29
基本概念 - 领域设计:领域事件
本文探讨如下问题: 什么是领域事件 领域事件的用途 何时使用领域事件 基于Spring事件的实现 什么是领域事件 在EDA风格与Reactor模式一文中,我们从观察者模式聊到了EDA架构风格,然后聊了Reactor架构模式,最后以redis为例聊了Event-driven programming编程模式。 这些内容都是技术层面的,其共性是通过事件来进行解耦,提高系统的性能、扩展性、伸缩性、可运维性和灵活性。 而领域事件顾名思义也是通过事件来进行解耦,只是它是设计层面的技术。用于解耦领域设计中的组件: 聚合与聚合之间的解耦。聚合请参考 限界上下文之间的解耦。限界上小文后面再聊 何时使用领域事件? 在《领域驱动设计》的第七章,举了一个货运的例子。其中有个HandlingEvent,这就是一个事件对象。当货物被装货、卸货、密封、存放以及其他活动时就会创建一个HandlingEvent。 ](https://p3-tt.byteimg.com/origin/pgc-image/a0fd500cbd8a478d887fc0db03ff2ca7?from=pc)) 在这个例子中,是由人在货物被处理时,使用系统输入了一条HandlingEvent记录下来而已!而如果当发生了一个HandlingEvent后,需要后续的处理(比如发送通知给相关人员),该怎么办呢?这就是领域事件所要解决的问题! 在《领域驱动设计》中并没有提到领域事件,领域事件是其后出现的。 实际上,当出现类似如下关键词汇时,都可以考虑使用领域事件: 当...... 如果发生...... 当......的时候,请通知我 发生......时 下面通过Spring的事件来实现上例中的具体流程。 Spring事件实例代码 新建HandlingEvent类,继承ApplicationEvent,表示这是一个事件 public class HandlingEvent extends ApplicationEvent { private String actionName; public VisitEvent(Object source,String actionName) { super(source); this.actionName = actionName; } } 编写事件发布类EventPublisher,实现ApplicationEventPublisherAware,用于注入ApplicationEventPublisher,通过publishEvent方法来发布事件 Spring默认是同步事件,如果需要异步事件,需要添加EnableAsync注解 @Service @EnableAsync public class EventPublisher implements ApplicationEventPublisherAware { private ApplicationEventPublisher publisher; @Override public void setApplicationEventPublisher(ApplicationEventPublisher applicationEventPublisher) { this.publisher = applicationEventPublisher; } public void publish(ApplicationEvent event) { publisher.publishEvent(event); } } 编写监听类,只需要在监听方法上添加eventListener注解,参数为需要监听的事件类即可 如果需要异步处理,添加Async注解 可以通过Async的value属性来选择线程池。 推荐选择自定义线程池,默认线程池没有限制线程数量,可能导致OOM @Component public class HandlingListener { @EventListener@Async("eventThread") public void processHandlingEvent(HandlingEvent event) {// 处理事件} 调用EventPublisher的publish方法,即可进行事件的发布 eventPublisher.publish(new HandlingEvent(this, "move")); 整体的流程如下: EventPublisher添加HandlingEvent HandlingListener监听到了HandlingEvent,执行对应的方法 我们看下Spring中如何实现这个流程的。 Spring事件实现 ](https://p1-tt.byteimg.com/origin/pgc-image/e460ab9325de4dca8233d8b916cfc932?from=pc)) Spring启动时,EventListenerMethodProcessor会将标识了EventListener注解的方法添加到Context中。这里就是HandlingListener中的processHandlingEvent方法 for (Method method : annotatedMethods.keySet()) { for (EventListenerFactory factory : factories) { if (factory.supportsMethod(method)) { Method methodToUse = AopUtils.selectInvocableMethod(method, context.getType(beanName)); ApplicationListener<?> applicationListener =factory.createApplicationListener(beanName, targetType, methodToUse); if (applicationListener instanceof ApplicationListenerMethodAdapter) { ((ApplicationListenerMethodAdapter) applicationListener).init(context, this.evaluator); } context.addApplicationListener(applicationListener);break; } } } AbstractApplicationContext实现了ApplicationContext接口,其中维护了扫描到的ApplicationListener @Override public void addApplicationListener(ApplicationListener<?> listener) { Assert.notNull(listener, "ApplicationListener must not be null"); if (this.applicationEventMulticaster != null) { this.applicationEventMulticaster.addApplicationListener(listener); } this.applicationListeners.add(listener); } 在EventPublisher发布事件时,触发ApplicationEventPublisher的publishEvent方法 ApplicationContext接口继承了ApplicationEventPublisher接口,最终触发AbstractApplicationContext的publishEvent方法,将事件广播出去 ApplicationContext接口为什么要继承ApplicationEventPublisher接口?因为Spring中定义了几个默认的事件ContextRefreshedEvent,ContextStartedEvent,ContextStoppedEvent,ContextClosedEvent,RequestHandledEvent,而ApplicationContext就是这些事件的布者。 less复制代码protected void publishEvent(Object event, @Nullable ResolvableType eventType) { ...... else { getApplicationEventMulticaster().multicastEvent(applicationEvent, eventType); // 广播事件 } ..... } 最终触发SimpleApplicationEventMulticaster的multicastEvent方法 @Override public void multicastEvent(final ApplicationEvent event, @Nullable ResolvableType eventType) { ResolvableType type = (eventType != null ? eventType : resolveDefaultEventType(event)); for (final ApplicationListener<?> listener : getApplicationListeners(event, type)) { Executor executor = getTaskExecutor(); if (executor != null) { executor.execute(() -> invokeListener(listener, event)); }else { invokeListener(listener, event); } } } 获取ApplicationListener,然后使用Executor去执行listener 参考资料 Spring源码 《领域驱动设计:软件核心复杂性应对之道》 《实现领域驱动设计》 作者:一瑜一琂 链接:https://juejin.cn/post/6895181027820601357 来源:稀土掘金 著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。
2022年07月29日
13 阅读
0 评论
33 点赞
2022-04-27
领域 - 驱动 - 设计 三大概念
什么是领域 领域就是问题域,用来解决特定的问题 领域怎么划分? 领域划分可以是一个项目,一个模块,一个BLL(业务逻辑) 领域谁来划分? 不是架构师,是领域专家(熟悉业务,需求分析师)主导,在交流中达成共识一大家都能理解的建模语言 领域就是在需求沟通环节,有需求方主导分析,开发认可,对需求的拆分建模,包括内部的一些规则---并不产出代码,只有模型---可大可小 驱动 按照领域设计去实现功能 拆分领域 设计领域 --- 清晰职责--- 做什么事 职责划分,定好规范 设计 项目按照领域来设计的,每个领域都是按照设计实现的,那么完成每个领域之后,组装起来就能完成全部需求,不会出现变形 设计思维: 程序员思维: 需求 --- 数据库---就开始逻辑开发 --- 界面 --- 完成 --- 容易变动 产品经理思维:一开始不管技术,只分析业务 --- 再考虑实现 --- 避免改动 以需求分析为核心,不谈技术,定好问题域和规范,再去落地代码
2022年04月27日
6 阅读
0 评论
22 点赞
2022-03-12
基本概念 - 领域设计:Entity与VO
什么是状态 什么是标识 什么是Entity 什么是VO(ValueObject) 在设计中如何识别Entity和VO 要理解Entity和VO,需要先理解两个概念:「状态」和「标识」!我们先来聊聊「状态」! 状态 大家肯定都在淘宝买过东西吧!在淘宝购买商品后,会有一个订单,记录了你购买的商品信息、价格、店铺信息、还有一个特别重要的信息,就是订单状态。通过这个订单状态,我们可以知道我们的购物流程现在进行到哪一步了。如果你犹豫了很久才下定决心购买了一件心仪已久的商品,你是不是很在意订单状态?时不时要刷新一下页面,看看订单状态是否显示已送达了? 开发过系统的都知道,一般订单状态都是使用一个字段来表示的,比如status,不同的状态就是给status赋不同的值。但是这个status就是「订单状态」吗?难道状态就是一个字段?! Order{ product location seller buyer status ... } 你有没有想过,当我们说「状态」的时候,我们实际上指的是什么? 我们在很多场景下会用到「状态」这个词,比如: 你今天「状态」不错哦 朋友又发朋友圈「状态」了 我在淘宝买的商品已经是发货「状态」了 REST 以「你今天状态不错」这句为例,如果状态就是一个字段!那么,「你今天状态不错」就是status=1?!「你今天状态不行」就是status=0?!很明显,这不合理! 如果「状态」不是简单的一个字段的话,那么「状态」到底是什么呢? 其实在架构风格:你真的懂REST吗?已经提过了!文中对REST的解释,有这么一句:一个由网页组成的网络(一个虚拟状态机),用户通过选择链接在应用中前进(状态迁移),导致下一个页面(应用的下一个状态的表述)被转移给用户,并且呈现给他们,以便他们来使用。 结合上面的几个场景,你有没有发现,「状态」实际上表示的是「目标对象在当前时刻所呈现出的内容」!在软件系统中通过一个字段来表示状态只是一种简化手段! 如无特殊说明,下面所提到的「状态」指的是「目标对象在当前时刻所呈现出的内容」,而不是指状态字段 你今天「状态」不错哦:你今天给人的感觉很好 朋友又发朋友圈「状态」了:朋友圈当前的内容 我在淘宝买的商品已经是发货「状态」了:你的购物流程目前所在的环节 REST 既然「状态」表示的是「当前时刻所呈现出的内容」!那么说明了「状态」是个快照/瞬态!也就是说,「目标对象」有多个「状态」,「当前状态」只是「目标对象」众多「状态」中的一个! 大家应该玩过定格动画吧?就像下面这样(下图截自《大侦探福尔摩斯2:诡影游戏》): 图中的小册子就是「目标对象」,册子的每一页就是「状态」,当前展示出来的那一页就是「当前状态」! 在理解了什么是「状态」以后,我们就可以来初步区分Entity和VO了: Entity在整个生命周期中,有多个「状态」,也就是说「状态」是可变的(至于变不变就看实际情况了) 而VO在整个生命周期中,只有一个「状态」,也就是说「状态」不变 现在,问题又来了,对于VO来说,因为「状态」是不可变的,我们就可以用其「状态」来表示VO!但是对于Entity来说,因为有多个「状态」,且「状态」是可变的,那我们如何来表示呢?以上面的Order为例,假设同一个买家在同一个卖家那里买了两个同样的商品,那两个订单里的信息都是一样的,但是它是两个不同的订单,我们如何区分这两个订单呢? 现在就轮到下一个主角登场了:「标识」! 标识 说到「标识」,我们最先想到的是编程语言中的「引用」或「指针」!比如下面的代码: Order orderA = new Order("productA",...); Order orderB = new Order("productA",...); orderA.productName = "productB"; 前面两行,orderA和orderB虽然订单信息(状态)都相同,但是这是两个不同的订单 第三行,即使改了orderA的产品名称(状态),依然还是相同的订单 这解决了「区分相同状态的不同Entity」的问题,但是没有解决Entity有多个状态的问题。因为「标识」指向的是目标对象的当前状态。而且,很多编程语言中有个很大的问题,就是不区分「标识」和「状态」!什么意思呢? 假设我们在看一部电影,当我们开始观看时,就是这部电影生命周期的开始,观看结束就是这部电影生命周期的结束,在这段时间里,电影的画面(状态)一帧帧的呈现在我们面前,我们可以通过播放、快进、后退、暂停改变电影的状态,每个状态都是相互独立的,类似这样: 随着时间的改变,我们能获取到电影的不同状态,每个状态是相互独立的。但是实际上我们的代码逻辑像下面这样: var movie1 = new Movie(); movie1.setCurrentFrame("第三帧"); var currentMovie = movie1 movie1.setCurrentFrame("第四帧"); currentMovie // 还是第三帧吗? 电影播放到第三帧,我们用一个变量currentMovie保存了电影的当前状态(第三帧),但是后面电影播放第四帧了,currentMovie也就变成了第四帧的状态了。 语言中的这种「标识」(我称为「隐式标识」)还有另外一个问题,就是无法跨系统。比如,在分布式系统中,需要保证两个系统中的对象是同一个对象,这种「隐式标识」是做不到的。 所以「隐式标识」并不能满足我们的需求。我们需要「显示标识」,「显示标识」在现实中很常见: 每个人都有身份证,即使有两个人名字相同、性别一样、身材相同、甚至整容了样貌都一样,但是身份证号码是不一样的,身份证号码就是每个人的「显示标识」 一个产品线上生产的产品可以说一模一样,但是都会有一个唯一的产品编号,这个产品编号就是产品的「显示标识」 在上面购物的列子中,就相当于给Order一个唯一标识,比如一个唯一的订单号: Order{ orderNo // 显示标识 product location seller buyer status ... } 给定订单号以后,无论订单的状态如何变化,只要订单号不变,那么它就是同一个订单。 所以,「标识」是另一个区分Entity和VO的关键点: Entity有标识 而VO没有标识 注意标识并不一定只是一个字段,可能是多个字段的组合,这需要根据不同的业务逻辑来确定。比如在一个学校系统里,可以通过学年+班级+学号来标识一个学生。 Entity和VO 理解了标识和状态,我们就可以来定义Entity和VO了: Entity是具有多个「状态」的对象,「状态」在其生命周期中可能会改变,通过「标识」来唯一确定这个对象 VO只有一个「状态」,且是在创建时就确定的,也就是说VO是不可变的 现在我们知道了什么是Entity,什么是VO,那么我们如何在系统中识别哪些对象是Entity,哪些对象又是VO呢? 如何识别Entity和VO 一个对象是表示成Entity还是VO,取决于系统的关注点。 我们还以淘宝购物为例,假设你在某家店铺买了个商品,质量很好。过了一段时间后,你想再买一个,但是你记不得是哪家店了,于是你从已完成的订单列表中点击商品想进去再次购买。但是你点进去后发现,商品下架了。 这是因为「商品」在「订单系统」中是个VO,而在「商品管理系统」中是Entity!其实很好理解: 在「商品管理系统」中,系统需要关注「商品」的「状态」,需要维护是否上架、库存多少、各种属性等信息(多种状态)。就是说在「商品管理系统」中,商品状态是可变的。所以它也有「标识」,即商品ID 而「订单系统」并不关心「商品」的「状态」变化,它只关注在创建订单时,这个「商品」的当前「状态」是什么,并且在订单创建完成后,这个「商品」的「状态」就不会再改变了 在「商品管理系统」中,商品可以这样表示: Product { id // 商品标识 name desc status ... } 而在「订单系统」中,订单是个Entity,商品是个VO,可以这么表示: Order{ orderNo // 订单标识 product:Product status ... } Product { id // 这里不是标识,只是状态 name desc status ... } 注意这里的id并不是标识,这里的id实际上退化成了状态的一部分,保留这个id是为了和「商品管理系统」进行交互,通过id从商品管理系统中查询商品。当然还有其它方式,例如保存「商品管理系统」中该商品的历史URL。 总结 本文从对「状态」和「标识」的理解开始,一步步来解释什么是Entity和VO,以及如何在系统中识别Entity和VO。后面将进一步讨论Entity与VO的关系,以及与其它组件的关系,例如DTO,Service,Resporitory,DAO等 参考资料 《领域驱动设计:软件核心复杂性应对之道》 《实现领域驱动设计》 《Clojure编程乐趣》 《七周七并发模型》
2022年03月12日
89 阅读
0 评论
31 点赞
1
2