Security News
CISA Brings KEV Data to GitHub
CISA's KEV data is now on GitHub, offering easier access, API integration, commit history tracking, and automated updates for security teams and researchers.
com.zhaofujun.nest:nest-mybatis-pagination-plugin
Advanced tools
Nest 框架是一个帮助开发人员快速实现基于领域驱动设计的技术框架。在 Nest 中定义了领域驱动设计的基本概念,包括聚合根、实体、值对、领域服务、应用服务、服务事件、仓储等,使用 Nest 可以帮助你全面执行你在领域模型建模中的面向对象思维。有效指导开发人员按模型编写代码,为代码检查等提供必要的依据。
领域驱动设计(Domain-Driven Design,DDD)是一种软件开发方法,旨在将软件项目的核心关注点放在业务领域上,通过对业务领域的深入理解和建模,来指导软件的设计和开发,从而使软件系统能够更好地满足业务需求,并具有更好的可维护性、可扩展性和可理解性。
领域驱动设计通过聚焦业务领域、建立清晰的模型和架构,以及促进团队协作等方式,为软件开发带来了诸多好处,能够有效地提高软件系统的质量和适应性,助力企业更好地实现业务目标和战略发展。
优点:
缺点:
优点:
缺点:
如果你面向对项目业务领域复杂、业务需求变更频繁、多团队协作开发、需要长期维护和演进的系统时,我强烈建议你采用领域驱动设计方法,领域驱动设计能够更好的帮助理解和梳理业务,有效应对频繁变更的业务需求,明确多个团队间的业务边界,特别适合指导微服务中的服务划分问题,帮助你建立高可维护性与可扩展性的系统奠定基础。
如果你只需要处理简单的业务场景、需求明确且时间紧迫的短期项目,使用领域驱动设计给你带来的效益将大打折扣,甚至还会带来额外的成本。
在DDD中,有几个重要概念,它们分别是实体、聚合根、实体标识、值对象、仓储、工厂、限界上下文、应用服务、领域服务、事件、域&子域等。
其中域&子域是战略设计的重要成果,用来表达一个问题空间。一个子域可能会有一个或多个限界上下文组成,共同协调解决问题空间中的问题域。
限界上下文常常可以定义为一个可以独立部署的应用程序,对应于问题空间,限界上下文代表了一个问题空间的解决方案。
将限界上下文当成一个独立的黑盒系统,这个限界上下文所具备的能力由应用服务与事件来表达,可使用用例来表达。这也是多个限界上下文之间交互的重要手段。
而限界上下文的核心能力都是由内部的领域模型提供,领域模型是基于当前问题空间进行战术设计的产物。所以对领域模型的建模便是战术设计的工作核心。
领域模型由实体、实体标识、值对象、聚合根、领域服务组成。
而仓储、工厂分别是对领域模型的生命周期而服务,仓储负责对模型的持久化与重新加载,工厂封装了实体被创建的一系列细节。
Nest已经通过Maven中央仓库托管,你可以在项目中直接引用依赖。 Maven引用
<!-- https://mvnrepository.com/artifact/com.zhaofujun.nest/nest-ddd -->
<dependency>
<groupId>com.zhaofujun.nest</groupId>
<artifactId>nest-ddd</artifactId>
<version>3.0.0</version>
</dependency>
Gradle引用
// https://mvnrepository.com/artifact/com.zhaofujun.nest/nest-ddd
compile group: 'com.zhaofujun.nest', name: 'nest-ddd', version: '3.0.0'
本案例使用nest-ddd快速创建基于DDD的项目,并且完成创建用户,发送创建用户成功的事件,并且自己作为消费者处理事件。
项目由领域模型、应用服务和应用程序、事件处理器组成。
创建用户领域模型
package com.zhaofujun.nest.test;
import com.zhaofujun.nest.ddd.AggregateRoot;
import com.zhaofujun.nest.ddd.StringIdentifier;
public class User extends AggregateRoot<StringIdentifier> {
public User(StringIdentifier identifier) {
super(identifier);
}
private String name;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
@Override
public String toString() {
return "User [name=" + name + ", id=" + id.toValue() + "]"+this.hashCode();
}
}
User
类从AggregateRoot
聚合根继承,并且指定了User
类使用String
类型的标识为唯一标识。
创建应用服务
应用服务包括一个服务类以及一个DTO类。
package com.zhaofujun.nest.test;
public class UserDto {
private String id;
private String name;
public String getId() {
return id;
}
public void setId(String id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
package com.zhaofujun.nest.test;
import com.zhaofujun.nest.ddd.ApplicationService;
import com.zhaofujun.nest.ddd.StringIdentifier;
import com.zhaofujun.nest.utils.EventUtil;
public class DefaultUserAppService implements ApplicationService {
public UserDto create() {
User user = new User(new StringIdentifier("111"));
user.setName("name");
UserDto userDto = new UserDto();
userDto.setId(user.getId().toValue());
userDto.setName(user.getName());
EventUtil.publish("user_created", userDto,100);
return userDto;
}
}
应用服务需要继承至ApplicationService
类,在应用服务内创建用户模型、将用户模型转换为 DTO,并且通过EventUtil
发布用户创建完成的事件
创建应用 应用可以是 web,也可以是命令行工具等。 本例使用 junit
package com.zhaofujun.nest.test;
import static org.junit.jupiter.api.Assertions.*;
import org.junit.jupiter.api.BeforeAll;
import org.junit.jupiter.api.Test;
import com.zhaofujun.nest.NestEngine;
import com.zhaofujun.nest.ddd.event.EventAppService;
import com.zhaofujun.nest.inner.DefaultEventInfoRepository;
import com.zhaofujun.nest.utils.AppServiceUtil;
public class UserTest {
@BeforeAll
public void initEngine() throws Throwable {
NestEngine nestEngine = new NestEngine();
EventAppService eventAppService = AppServiceUtil.create(EventAppService.class);
eventAppService.setQuery(new DefaultEventInfoRepository());
nestEngine.setEventAppService(eventAppService);
// 注册事件处理器
nestEngine.registerEventHandler(new UserCreatedHandler());
// 启动引擎
nestEngine.start();
}
@Test
public void createUser() throws Throwable {
// 创建化用户应用服务
DefaultUserAppService userAppService = AppServiceUtil.create(DefaultUserAppService.class);
UserDto userDto = userAppService.create("1111", "Li lei");
assertEquals("111", userDto.getId());
assertEquals("Li lei", userDto.getName());
}
}
应用服务的创建不能直接使用使用new
关键字,必须使用AppServiceUtil.create
方法创建,因为Nest
框架将使用AOP
处理方法切面。
package com.zhaofujun.nest.test;
import com.zhaofujun.nest.ddd.EventHandler;
public class UserCreatedHandler implements EventHandler<UserDto> {
@Override
public String getEventName() {
return "user_created";
}
@Override
public Class<UserDto> getEventDataClass() {
return UserDto.class;
}
@Override
public void handle(UserDto eventData) {
System.out.println("接收到用户创建成功的事件:" + eventData.toString());
}
}
在本例中没有创建用户模型的仓储实现,Nest 将使用默认的仓储实现,默认仓储使用本地缓存存储数据,可用于单元测试或集成测试,请勿用于生产环境。
本例将使用Springboot3.0创建入门应用,案例创建通过仓储加载一个User
模型,修改name属性后发布事件。并且使用两个事务处理器处理事件。
添加nest-spring-boot-starter
依赖
Nest已经通过Maven中央仓库托管,你可以在项目中直接引用依赖。 Maven引用
<!-- https://mvnrepository.com/artifact/com.zhaofujun.nest/nest-ddd -->
<dependency>
<groupId>com.zhaofujun.nest</groupId>
<artifactId>nest-spring-boot-starter</artifactId>
<version>3.0.0</version>
</dependency>
Gradle引用
// https://mvnrepository.com/artifact/com.zhaofujun.nest/nest-ddd
compile group: 'com.zhaofujun.nest', name: 'nest-spring-boot-starter', version: '3.0.0'
创建User
模型
package com.zhaofujun.nest.demo.appservices.model;
import com.zhaofujun.nest.ddd.AggregateRoot;
import com.zhaofujun.nest.ddd.LongIdentifier;
public class User extends AggregateRoot<LongIdentifier> {
private String name;
private long age;
public User(LongIdentifier longIdentifier){
super(longIdentifier);
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public long getAge() {
return age;
}
public void setAge(long age) {
this.age = age;
}
}
创建模型方式与之前一样,需继承至AggregateRoot
创建应用服务
package com.zhaofujun.nest.demo.appservices;
public class UserDto {
private String id;
private String name;
public String getId() {
return id;
}
public void setId(String id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
package com.zhaofujun.nest.demo.appservices;
import com.zhaofujun.nest.boot.AppService;
import com.zhaofujun.nest.ddd.LongIdentifier;
import com.zhaofujun.nest.demo.appservices.model.User;
import com.zhaofujun.nest.utils.EntityUtil;
import com.zhaofujun.nest.utils.EventUtil;
@AppService
public class UserAppservice {
public UserDto changeName(long id) {
User user = EntityUtil.load(User.class, new LongIdentifier(id));
user.setName("name1");
var result = new UserDto();
result.setId(user.getId().toValue());
result.setName(user.getName());
EventUtil.publish("user_name_changed", result);
return result;
}
}
在Spring环境中有两种方式标识应用服务,可使用@AppService
注解或通过继承ApplicationService
接口。使用后者需要为应用服务类注解@Component
使其被Spring容器管理,而使用前者将自动被注册到Spring容器。
另外获取应用服务实例时使用Spring自动注入即可,不再需要AppServiceUtil.create
方法创建。
创建User仓储
package com.zhaofujun.nest.demo.repositories;
import java.lang.reflect.Type;
import org.springframework.stereotype.Component;
import com.zhaofujun.nest.ddd.Identifier;
import com.zhaofujun.nest.ddd.LongIdentifier;
import com.zhaofujun.nest.ddd.Repository;
import com.zhaofujun.nest.demo.appservices.model.User;
@Component
public class UserRepository implements Repository<User> {
@Override
public void insert(User t) {
System.out.println("新增 User,可以使用 DAO 方式操作数据库");
}
@Override
public void update(User t) {
System.out.println("修改 User,可以使用 DAO 方式操作数据库");
}
@Override
public void delete(User t) {
}
@Override
public Type getEntityType() {
return User.class;
}
@Override
public User getEntityById(Class<? extends User> tClass, Identifier identifier) {
System.out.println("直接创建一个 User 类,模拟数据库查询");
User user = new User((LongIdentifier) identifier);
user.setName("test");
user.setAge(10);
return user;
}
}
仓储的定义需要继承至Repository
接口并实现insert
,update
,delete
,getEntityType
和getEntityById
方法。
在Spring环境中需要为仓储类添加@Component
注解以便容器管理。
创建RestController
package com.zhaofujun.nest.demo.webapi;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RestController;
import com.zhaofujun.nest.demo.appservices.UserAppservice;
import com.zhaofujun.nest.demo.appservices.UserDto;
@RestController
public class UserController {
@Autowired
private UserAppservice userAppservice;
@PostMapping("/user/{id}")
public UserDto changeName(@PathVariable Long id) {
return userAppservice.changeName(id);
}
}
创建事务处理器
本案例使用两种方式处理事件,分别使用EventHandler
接口方式和@EventListener
注解方式。
package com.zhaofujun.nest.demo.handlers;
import org.springframework.stereotype.Component;
import com.zhaofujun.nest.ddd.EventHandler;
import com.zhaofujun.nest.demo.appservices.UserDto;
@Component
public class UserEventHandler implements EventHandler<UserDto> {
@Override
public String getEventName() {
return "user_name_changed";
}
@Override
public Class<UserDto> getEventDataClass() {
return UserDto.class;
}
@Override
public void handle(UserDto eventData) {
System.out.println("event handler");
}
}
package com.zhaofujun.nest.demo.handlers;
import org.springframework.stereotype.Component;
import com.zhaofujun.nest.boot.EventListener;
import com.zhaofujun.nest.demo.appservices.UserDto;
@Component
public class UserEventHandlerListener {
@EventListener(eventDataClass = UserDto.class, eventName = "user_name_changed")
public void userCreate(UserDto userDto) {
System.out.println("event listener");
}
}
启动Springboot应用程序
package com.zhaofujun.nest.demo;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
@SpringBootApplication
public class Application {
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
}
在使用了SpringBoot后,我们将不再关注 NestEngine 的初始化过程,nest-spring-boot-starter
将自动初始化NestEngine。也不需要手动注册事件处理器,只需要将处理器放入Spring容器中管理即可
领域建模是战术设计的工作核心,为了让大家更好完成领域建模,这里将对实体、值对象、聚合根、领域服务进行简单的介绍。
领域实体是领域模型中的核心概念,它是从通用语言中提炼出来的名词,它代表了业务领域中具有唯一标识且生命周期较长的事物。领域实体通常具有自己的属性和行为,并且其状态可以随着时间和业务操作而发生变化。
特性:
与实体相似的另一个概念是值对象,它也是从通用语言中提炼出来的名词。区别于实体,它没有唯一标识,其主要目的是为了传递和表示一些不可变的值,所以值对象往往不会独立存在,它常常用来描述实体的一些特性。值对象从创建之后就不应该有状态变化,他对于实体而言应该是没有负作用的,如果需要对一个值对象进行修改,常常是创建一个新的值对象去替代原来的值对象。
特点:
将一系列关系密切的实体、值对象联系在一起,组成的一个比较大的实体,我们将其称为一个聚合。而能够访问聚合内实体的根实体我们将之称为聚合根,聚合根负责维护聚合内其他实体和值对象的一致性.
特点:
tips1: 一个聚合内可能只有一个聚合根,也可能有多个聚合根。聚合可以很小,小到只有一个实体,那么该实体本身也是一个聚合根。
tips2: 聚合与实体的关系和实体与值对象的关系对比:
- 聚合与实体间对应于UML中的聚合关系
- 实体与值对象对应于UML中的组合关系
- 实体可以独立于聚合存在,呈弱引用关系
- 实体与值对象是组合关系,值对象跟随实体的生命周期,是包含关系
tips3: 虽然聚合才关注持久化,但在实现开发设计中往往会给实体提供仓储功能,由聚合统一调配,这样可以减少对仓储的操作。为了加快聚合对象的创建过程,往往推荐大家设计更小的聚合。
在通用语言中抽象出来的一些动作,这些动作往往需要协调多个聚合才能完成,而这样的行为放在其中任何一个聚合中都不那么和谐时,便可以用领域服务来处理。 但是我们不要太过于依赖领域服务,只有在没有办法的情况下才用领域服务,否则容易产生贫血模式。
领域建模是DDD战术设计的重要内容。其中最基础的模型过程便是区分实体与值对象。
分析一个名词是否是实体时需要在当前业务场景中来分析,比如同样是商品信息,在商品上下文中,他是一个实体,我们可以对这个商品进行上下架操作。我们可以通过商品的编号来唯一标识该商品信息。而在订单上下文中,他就成了一个值对象,他只是用来描述订单信息的一个组成部分,在订单被创建之后,这些商品信息就不会再发生变化,即使原来的商品已经被删除,这里的商品信息也不会受到影响。因为值对象的存储方式是一份副本,而不是基于实体标识的引用。
领域模型应该包括聚合根、实体、值对象、领域服务。并且可以通过领域模型图完成业务演练与代码开发指导。
领域建模可以使用UML语言,为了区分这四类模型,可以采用四种不同的颜色来区分,称之为四色原型法。
DomainObject
接口
领域对象接口,用于标识一个类是不是领域对象,值对象、实体、聚合根、领域服务都是一种领域对象。
ValueObject
类
值对象类,根据值对象的定义,重写了equals
和hashCode
方法,使其对比两个值对象值是否相同的方式不按引用,而是对比每个属性的值是否相同。为了不使用反射影响性能,定义为值对象的类继承ValueObject
时需要实现Object[] getPropertiesForComparison()
抽象方法。例如:
public class ConcreteValueObject extends ValueObject {
private String name;
private int age;
@Override
protected Object[] getPropertiesForComparison() {
return new Object[]{name, age};
}
}
Identifier
类
实体标识抽象类,用来代表实体的唯一标识,实体标识本身也是一个值对象。
toValue()
:使用字符串方式表达实体的唯一标识,在实现一个实体标识类时需要实现该方法Nest框架内置了StringIdentifier
、LongIdentifier
和UUIDIdentifier
,用户可以根据自己需要自定义Identifier
,如下:
package com.zhaofujun.nest.ddd.event;
import com.zhaofujun.nest.ddd.Identifier;
public class ProcessIdentifier extends Identifier {
private String messageId;
private String handlerId;
public ProcessIdentifier(String messageId, String handlerId) {
this.messageId = messageId;
this.handlerId = handlerId;
}
public String getMessageId() {
return messageId;
}
public String getHandlerId() {
return handlerId;
}
@Override
public String toValue() {
return messageId + "-" + handlerId;
}
}
ProcessIdentifier
类使用了两个属性作为联合标识来唯一确定一个标识。
Entity
类
定义一个领域对象是一个实体,Entity
类是一个泛型类,需要指定一个Identifier
的实现类来表示实体的唯一标识。
Entity
类不同于ValueObject
,在对比两个实体是否相等时,只对比其实体标识是否相同。
delete()
:删除一个实体verify()
:验证实体是否满足定义的规则,如果验证失败,将抛出VerifyFailedException
异常getId()
:获取实体的唯一标识getBeginSnapshot()
:获取实体在业务操作前的快照信息,快照以 json 格式表示getEndSnapshot()
:获取实体在业务操作之后的快照信息,快照以 json 格式表示AggregateRoot
类
定义一个领域对象是一个聚合根,聚合根本质上是一个实体,具有实体所有的特性。区别于实体,聚合根可以被EntityUtil.load()
方法加载,而实体不可以。
当一个聚合内只有一个实体时,请将该实体标识为一个聚合根。
在领域驱动设计(DDD)中,仓储(Repository)是一个用于管理领域对象持久化和检索的组件。它提供了一种统一的方式来访问数据存储(如数据库、文件系统等),使得领域层能够与底层的数据存储技术相分离,专注于业务逻辑的实现。
仓储位于数据访问层之上,数据访问层直接面向存储器而仓储使用数据访问层的能力将实体持久化到具体的存储器中。
Repository
接口
为实体定义仓储实现,Repository接口是一个泛型接口,根据要求需要指定一个Entity
的实现类。
getEntityType()
:返回当前仓储能处理的实体类型getEntityById()
:按实体标识找到具体的实体insert()
:插入实体update()
:更新实体delete()
:删除实体batchInsert()
:批量插入实体,当仓储实现该方法后,insert()
方法将不再被应用。batchUpdate()
:批量更新实现,当仓储实现该方法后,update()
方法将不再被应用。batchDelete()
:批量删除实体,当仓储实现该方法后,delete()
方法将不再被应用。当业务过程中发生了对实体的创建、修改或删除操作后,Nest将自动根据getEntityType()
方法来匹配具体的仓储实现并执行对应的插入、更新或删除行为。开发人员不用关注仓储何时被调用。
当使用getEntityType()
方法没有匹配到实体的仓储实现时,Nest会使用实体的父类去匹配仓储的实现,直到找到Entity
类对应的默认实现DefaultRepository
。这种使用父类的方式匹配仓储可以让我们对领域模型的单元测试时不用关心实体仓储的实现,而把精力放在领域模型的开发中。
如果你的业务中通过继承方式定义了多个实体,例如外卖订单、物流订单都通过订单基类扩展,我们可以为外卖订单和物流订单分别设计仓储,也可以只实现一个订单仓储,在仓储内同时处理多种不同的实现方式。
开发人员需要使用将仓储注册到
Query
接口
为查询接口提供标识,该接口是一个空接口,不需要实现任何内容。
使用Query接口标识的查询仓储将被查询上下文托管,以保证在查询仓储中创建的实体对象能够被Nest统一管理与维护。
当业务系统需要进行查询操作时,需要先自定义一个查询接口并继承至Query
接口。如下:
package com.zhaofujun.nest.ddd.event;
import java.util.List;
import com.zhaofujun.nest.ddd.Query;
public interface EventMessageQuery extends Query {
List<Long> getListToBeSent(int commonSize,int failSize, int maxFailTime);
}
查询接口的定义一般在应用服务层。如果查询内容将用于数据传输,推荐使用DTO对象,且勿直接返回领域对象,避免破坏或泄露领域对象的业务细节。
完成接口定义后,可以在仓储层实现该接口的查询仓储。实现仓储查询的技术方案可以根据系统架构自由选择,如 SQL、ES 或其它技术方案。
一个应用与其它应用之间的传递信息可以分为同步或异步。应用服务用于同步传递,而事件为异步传递。
应用服务是领域驱动设计(DDD)架构中的一个重要组成部分。它位于领域层之上,主要负责接收来自外部(如用户界面、其他系统等)的请求,协调领域层中的领域服务、领域实体和值对象来完成具体的业务操作,然后将处理结果返回给外部请求者。应用服务充当了外部世界与领域模型之间的桥梁。
职责:
tips1: 应用服务区别于领域服务:
- 应用服务是封装业务而领域服务是实现业务
- 应用服务面向业务用例设计而领域服务面向业务行为而设计
- 应用服务位于领域服务上层,应用服务可调用领域服务而领域服务不能调用应用服务
事件驱动架构(Event - Driven Architecture,EDA)是一种软件架构风格,是领域驱动设计(DDD)架构中的另一个重要组成部分。 它一般由事件生产者、事件消费者和事件通道组成,事件生产者一般由应用服务引发,用于与其它界限上下文异步处理业务以达到解耦的作用。
事件传递的消息与应用服务一样,都以数据传输对象(DTO)形式承载数据对象。
ApplicationService
接口
标识一个类是应用服务,使用nest-spring-boot-starter
组件的情况下,需要使用@Component
标识为spring bean。也可以用@AppService
注解标识应用服务,此时可以不用实现接口ApplicationService
。
tips1,可使用
ApplicationService
或@AppService
标识应用服务,如果服务类无任何标识,Nest将不会跟踪服务方法内对实体的任何操作行为。可能会导致事件和实体无法进入仓储而丢失数据。
getTransactionClass()
方法,定义该应用服务所使用的事务方法, 默认值Transaction.DefaultTransaction.class
,该方式不处理任何事务。使用nest-spring-boot-starter
组件的情况下,可以使用SpringTransaction.class
,该方式使用spring
的事务管理机制处理,你也可以实现Transaction
接口自定义你的事务管理方式,比如扩展一些分布式事务方法。@AppService
注解
该注解是nest-spring的增强,需要先引入nest-spring-boot-starter
组件。
与ApplicationService
接口一样,用于标识一个类为应用服务,使用ApplicationService
接口标识的应用服务需要手动将服务类注册或添加@Component
注解,以方便Spring IOC
统一管理,而使用@AppService
将自动注册到Spring IOC
。
transaction
属性,定义应用服务所使用的事务方法,默认值Transaction.DefaultTransaction.class
,该方式不处理任何事务。也可以使用SpringTransaction.class
,该方式使用spring
的事务管理机制处理,你也可以实现Transaction
接口自定义你的事务管理方式,比如扩展一些分布式事务方法。@AppServiceIgnore
注解
标识为忽略应用服务,标识该注解的方法在被调用时不会被Nest跟踪管理。
如果一个正常的应用服务方法内调用了一个注解该类的服务方法时,注解该类的服务方法内部的实体操作会被正常的应用服务方法接管而统一管理。
EventUtil
工具类
用于发布事件,使用该类发布的事件将通过消息中间件发布到不同应用的消费者。
在Nest 中,EventUtil 发布一个事件后,事件消息将跟随业务数据一起持久化到本地事件表,然后由 NestEngine 启动的事件发布工作线程从业务库的本地事件表获取未发布的事件并提交到对应的消息中间件。
publish
方法,用于发布一个事件,该方法有两个重载,区别在于可指定延迟发布事件的时间。事件发布时只需要指定事件名称和事件需要传递的 DTO对象。Nest将通过配置信息查找该事件将使用的事件通道,并使用相同的事件通道发布事件。 如果未配置通道,Nest使用默认的本地通道发布,这将可能导致跨应用的消费者无法获得事件通道。EventHandler
接口
定义一个事件处理器(事件消费者),用于接受并处理事件消息。
getEventName()
方法,定义该处理器处理的事件名称getEventDataClass()
方法,定义该处理器处理的事件数据的类型getConsumeMode()
方法,定义该处理器使用的消费模式,模式分推和拉两种handle()
方法,具体的处理事件消息的处理器在Nest中,定义了处理器后需要使用NestEngine.registerEventHandler()
方法注册。注意,注册事件需要在NestEngine.start()
前执行。在使用了nest-spring-boot-starter
组件情况下, 只需要将EventHandler
的实现类注册到Spring Ioc
中即可,比如@Component
注解。
package com.zhaofujun.nest.test;
import com.zhaofujun.nest.ddd.EventHandler;
public class UserCreatedHandler implements EventHandler<UserDto> {
@Override
public String getEventName() {
return "user_created";
}
@Override
public Class<UserDto> getEventDataClass() {
return UserDto.class;
}
@Override
public void handle(UserDto eventData) {
System.out.println("接收到用户创建成功的事件:" + eventData.toString());
}
}
@EventListener
注解
该注解是nest-spring的增强,需要先引入nest-spring-boot-starter
组件。
与EventHandler
一样,用于标识一个方法是消费者。标识该注解的类需要被 Spring 容器管理。服务方法签名是固定的
eventName
定义事件名称eventDataClass
定义事件接收消息的类型consumeMode
定义事件获取模式,包括推和拉两种模式package com.zhaofujun.nest.demo.handlers;
import org.springframework.stereotype.Component;
import com.zhaofujun.nest.boot.EventListener;
import com.zhaofujun.nest.demo.appservices.UserDto;
@Component
public class UserEventHandlerListener {
@EventListener(eventDataClass = UserDto.class, eventName = "user_created")
public void userCreate(UserDto userDto) {
System.out.println("event listener");
}
}
NestEngine
类
Nest框架的主引擎,用于初始化和管理 Nest 框架的默认核心组件,还支持注册事件处理器、第三方组件扩展、仓储、和缓存管理等。
initEventEnvironment()
方法:初始化事件驱动架构的相关环境,包括事件仓储与事件应用服务registerByContainer()
方法:通过容器自动注册缓存、事件、事件处理器、仓储、第三方扩展等registerProviders()
方法:手动注册第三方扩展组件,支持扩展缓存提供者、锁提供者、编号生成器、事件通道提供者等registerCacheItem()
方法:手动注册缓存项,支持定义某一组缓存策略,如缓存提供者、统一的过期时间等registerEventItem()
方法:手动注册事件项,支持定义一个事件的策略,如事件对应的消息中间件等registerRepository()
方法:手动注册仓储registerEventHandler()
方法:手动注册事件处理器start()
方法:启动引擎,引擎将开启内部消息的处理,并启动事件发布工作线程与事件处理工作线程stop()
方法:关闭引擎,引擎关闭的同时自动关闭事件发布工作线程与事件处理工作线程FAQs
A framework for DDD
We found that com.zhaofujun.nest:nest-mybatis-pagination-plugin demonstrated a not healthy version release cadence and project activity because the last version was released a year ago. It has 0 open source maintainers collaborating on the project.
Did you know?
Socket for GitHub automatically highlights issues in each pull request and monitors the health of all your open source dependencies. Discover the contents of your packages and block harmful activity before you install or update your dependencies.
Security News
CISA's KEV data is now on GitHub, offering easier access, API integration, commit history tracking, and automated updates for security teams and researchers.
Security News
Opengrep forks Semgrep to preserve open source SAST in response to controversial licensing changes.
Security News
Critics call the Node.js EOL CVE a misuse of the system, sparking debate over CVE standards and the growing noise in vulnerability databases.