综合前两篇总结,这篇对领域服务和领域事件做一个梳理。先注明一下,领域服务和应用服务。SOA服务,或者应用间的RPC调用,Restful接口,或者通过消息中间件进行系统间的交互的,都可以归类为应用服务。相较之下,领域服务不一定涉及到远程调用或者重量级事务操作。所以上下文集成也就涉及到,怎样的方式去划分限界上下文,怎么样设计才能尽量减少应用服务的耦合,以及应用服务对于本地模型的防腐。
领域服务
概念
借用《Implementing Domain-driven Design》里面的对于领域服务的定义。领域的某个操作过程或转换过程不是实体或值对象的职责时,应该将操作放在一个单独的接口中,即领域服务,并且要和通用语言保持一致。这里的非实体或值对象操作会有很多种情况,比如某个操作需要对多个领域对象操作,输出一个值对象。在分层的架构中,有点类似于Manager。但是如果过渡抽象manager就会出现贫血,所以还需要确保领域服务是无状态的,并且做好和贫血模型的权衡。可能大多数情况,领域服务的参数都是比实际的领域模型小的,只有些关键属性的值对象。如果服务只操作领域的实体或值对象,则可以考虑下放到domain model中操作。
前面提到了Manager,但是很多应用中都会把Manager抽象成接口的形式,但大多数情况其实完全没有必要,可以通过服务Factory的方式解耦,或者用Spring的@Service注解来注入真正的服务实现类。对于一些简单的领域操作,还可以抽象一个迷你层,这个迷你层也可以称作是领域服务,只不过是无状态,无事务,安全的一个抽象层。
领域事件其实也可以归纳为领域服务,不过领域服务的事件是幂等的。因为领域服务是无事务的,所以事件也是无副作用的,这样在处理聚合依赖的时候,需要保证他们的最终一致性。
领域事件
将领域中发生的活动建模成一系列的离散事件,每个事件都用领域对象来表示。简而言之,领域事件就是领域中发生的事件。还拿租书为例,一本书被借走了,那么需要产生一个借书订单,并且对于租书者来说,需要能查看自己租书的列表和书籍详情,同时这本书也需要被标记为不能再借出的状态(因为已经被借走了)。这里面bookRent就可以作为一个领域事件来发出。
事件的聚合
对于上述的事件模型,我们可以创建具有聚合特性的领域事件。这里我们可以把这个事件本身建模成一个聚合(BookRentEvent 对象),并且有自己的持久化方式。唯一标识可以由一组属性决定,在客户方(Client)调用领域服务的时候创建这个领域事件{new bookRentEvent())},并添加到资源库中,然后再通过消息的方式进行发布。发布成功后再回调更新时间状态。但这里需要注意,消息发布最好和事件资源库在相同的上下文,或共享数据源,这样就可以保证事件的成功提交,在不同上下文系统,就需要做全局事务来保证。而唯一标识在这里的作用就是为了防止消息重发或者重复处理。所以订阅方需要检查重复消息,并且忽略。如果是本地上下文的事件,最好提供equals和hashcode 实现。
结合刚才的例子,在书籍管理上下文中,书被借走了,那么书籍唯一表示和书的状态(Rent被借出)就可以标识一个事件。这个事件中需要有借书人的信息(如id,nick等),那么在持久化这个事件后,可以post一个Eventbus的本地消息,由用户书籍领域服务监听,更新用户书籍列表等一系列操作。然后再Callback到事件源,更新事件状态,处理成功。如果需要处理事件都在本地上下文,处理起来并不麻烦。
发布领域事件
领域事件的发布可以用Observer模式。在本地上下文,也要尽量减少对基础设施或者消息中间件暴露领域模型,所以,需要将本地模型(领域模型)封装成事件的聚合。比如我们不能直接发布一个BookRent聚合的事件,而是一个BookRentEvent,这个Event对象,还会持有一些事件特有的属性,比如可能根据需要,会有occurTime(发生时间),isConsumed(是否已经被处理)。事件发布时,所有订阅方都会同步收到通知。领域事件的主要组件就是publisher和subscriber了。
发送者
发送者本身并不表达一种领域概念,而是作为一种服务的形态。无论用什么技术方式实现,用什么框架,处理事件发送的思路也都可能不尽相同。比如,在web应用中,可以在启动应用的时候处理订阅者向发送者的事件注册(避免注册和处理发送的线程同步问题)。比如可以将关注的事件registe到本地的一个ThreadLocal的publisher List中。应用启动完成后,开始处理领域事件的时候,就可以发送一个事件的聚合。这个事件的聚合是一个事件对象,而不是领域模型中的实体,因为我们要暴露需要暴露的事件给其他上下文,而不是暴露完整的领域对象。如果使用EventBus,我们可以在post的时候,封装一个事件作为参数。
订阅者
事件的订阅者可以作为应用服务的一个独立的组件。因为应用服务是在领域逻辑的外层,如果是纯粹的事件驱动,那么订阅者作为一种应用服务,也可以定位成具有单一职责的,负责事件存储的应用服务组件。
分布式领域事件
在处理分布式事件中,最重要也是最难处理的就是一致性。消息的延迟,处理的不幂等就会影响领域模型状态的准确性和事件的处理。但是我们在系统间交互的过程中,可以用一些技术方式来达到最终一致性。这其中可能就需要进行事件模型的持久化。处理方式可以
1. 领域模型和消息设施共享持久存储的数据源。这种需要事件作为一种本地事件模型存储在和本地领域模型的同一个数据库中。这样保证了本地事务的一致,性能较好,但是不能和其他上下文共享持久化存储。
2. 全局XA事务(两阶段提交)来控制。模型和消息的持久化可以分开,但是全局事务性能差,成本高。
3. 在领域模型的持久化存储中,单独一块存储区域(单独一张事件表)来存储领域事件。也就是做本地的EventStore。但是需要有一个发布事件的消息机制,消息事件是完全私有的。消息的发送可以交给消息中间件来处理。如果可以的话,还可以将时间存储作为Rest资源。事件就可以以一种存档日志的形式对外发布事件(消息队列,通过消息设施或者中间件发送RabitMQ,MetaQ等)。这样还保证了时间的可追溯性。
我们使用事件来解耦,是为了考虑尽量避免RPC,简化系统依赖,减少外部服务不可用对系统模型带来的状态影响。所以领域事件强调的是高度自治,但是也需要斟酌,通过事件处理的情况必须是容许延时的,并且消息的接收方需要是一个幂等接收器(可以自幂等,或者对于重复消息的拒绝处理),因为消息是可能重复发送的。
相关推荐
- **最终一致性**:对于分布式系统,领域事件可以帮助实现跨服务的数据一致性。 **总结** 领域事件是领域驱动设计中不可或缺的一部分,它提供了一种方式来记录和处理业务流程中的重要事件。`DomainEventExample`...
领域服务封装了跨越多个实体或值对象的业务逻辑,而领域事件则是对业务行为的响应,通过发布事件,可以解耦组件之间的依赖,实现异步处理和状态的同步。 事件风暴是一种DDD的实践方法,它是一种集体构思活动,让...
8. **领域服务**:领域服务封装了跨越多个实体或值对象的业务逻辑,当某个操作不属于任何单一实体的职责时,可以将其放在领域服务中。 9. **仓储**:仓储是持久化领域对象的接口,它提供了对数据存储的抽象,使得...
《实现领域驱动设计》共分为14 章,在DDD 战略部分,《实现领域驱动设计》向我们讲解了领域、限界上下文、上下文映射图和架构等内容,战术部分包括实体、值对象、领域服务、领域事件、聚合和资源库等内容。...
视频详细讲解,需要的小伙伴自行网盘下载,链接见附件,永久有效。...领域服务是什么? 通过用例分析法和领域事件梳理电商购物车核心流程 第4章 DDD进阶篇 DDD面向对象分析方法、CQRS、六边形架构特点,BAT实战落地
《CCKS2020金融领域篇章级事件主题抽取数据集详解》 在信息技术日新月异的今天,数据已经成为驱动科学研究、商业决策和技术创新的重要资源。金融领域作为信息密集型行业,对数据处理与分析的需求尤为强烈。CCKS...
在实践中,我们通过定义领域对象、领域服务、仓储和领域事件来构建领域模型,确保软件能准确地反映并执行业务规则。通过对给定的代码实现进行学习,开发者能够加深对领域驱动设计的理解,并提升在实际项目中的应用...
领域驱动设计(Domain-Driven Design,简称DDD)是一种软件开发方法,它强调通过与领域...通过理解和应用领域模型、领域事件、聚合、仓储、领域服务等核心概念,我们可以构建出更贴近业务需求、更易于维护的软件系统。
《实现领域驱动设计》共分为14 章,在DDD 战略部分,《实现领域驱动设计》向我们讲解了领域、限界上下文、上下文映射图和架构等内容,战术部分包括实体、值对象、领域服务、领域事件、聚合和资源库等内容。...
一个领域模型通常包含实体(Entities)、值对象(Value Objects)、聚合(Aggregates)、领域服务(Domain Services)和领域事件(Domain Events)等元素。 1. 实体:在领域模型中,实体是具有唯一标识的对象,它们...
2. **领域事件**(Domain Event):在源码中,可能会发现领域事件的实现,这是一种记录系统中重要业务变化的方式,常用于解耦不同的领域服务或实现异步处理。 3. **应用服务**:应用服务层是DDD架构中的一个重要...
领域模型是对业务领域的抽象,包含了业务实体、值对象、聚合、领域事件、领域服务等元素。领域模型是业务逻辑的载体,它将业务规则和操作封装其中,使得代码更具有可读性和可维护性。 1. **领域实体**:领域实体...
7. **领域事件**(Domain Event):记录领域内发生的有意义的事件,可用于实现松耦合的系统组件通信。 8. **持续集成与反馈**:在开发过程中不断集成代码,及时获取反馈,调整和完善模型。 在实际项目中,DDD可以...
1. **领域模型**(Domain Model):领域模型是业务逻辑的抽象,它包含了业务实体、值对象、领域事件、聚合、工厂、仓储等元素。这些元素共同构成了一个反映业务规则的模型。 2. **统一语言**(Ubiquitous Language...
11. **领域服务(Domain Services)**:当一个操作涉及多个领域实体,或者不适合放在实体内部时,可以将其封装为领域服务。 12. **应用服务(Application Services)**:作为业务流程的协调者,接收来自用户界面的...
DDD的关键要素包括实体(Entity)、值对象(Value Object)、聚合(Aggregate)、领域事件(Domain Event)等。 #### 二、领域模型的重要性及其特性 - **业务核心关注**:领域模型应当专注于特定的业务操作领域,...
领域模型应包含实体、值对象、聚合、领域事件和领域服务等元素。 2. **实体**:具有唯一标识的业务对象,其状态和行为由领域规则定义。实体的标识在生命周期中保持不变,是区分不同实体的关键。 3. **值对象**:...