一佳技术

Matrix Framework 设计与使用文档

目录

  1. 框架概述
  2. 架构设计
  3. 模块详解
  4. 快速上手
  5. 核心组件使用指南
  6. 配置参考
  7. 最佳实践

1. 框架概述

1.1 什么是 Matrix Framework

Matrix Framework 是一套基于 DDD(领域驱动设计)+ CQS(命令查询分离) 架构理念的企业级 Java 开发框架,专为复杂业务系统设计。框架提供了从领域建模、代码生成、数据持久化、消息通信到网关接入的完整技术栈。

1.2 设计理念

理念说明
DDD 分层严格按照 Adapter / Application / Domain / Infrastructure 四层划分
CQSCommand(写)走领域模型,Query(读)直查数据模型,读写分离
依赖倒置Domain 层只定义抽象(Gateway / Repository),实现位于 Infrastructure
防腐层通过 Gateway 接口隔离外部系统技术细节,保护领域模型纯净
插件化核心能力通过 easy-plugin 模块支持 PF4J 插件扩展

1.3 技术栈

类别技术
语言Java 17(编译目标,兼容 Java 8 语法)
框架Spring Boot 2.7.x
ORMMyBatis-Plus / TkMapper(双实现)
消息队列RocketMQ / Kafka / RabbitMQ / AutoMQ / Redis / DBMS / ZeroMQ(七 connector 可选)
APIGraphQL / REST / gRPC
规则引擎easy-rules(MVEL / JEXL / LiteFlow),easy-fsm(FSM),easy-flows(Warm-Flow 工作流)
代码生成AVDL(Avro IDL)领域建模描述
对象存储easy-oss(MinIO 后端,SPI 多后端可扩展)
分布式任务db-scheduler(嵌入式 SDK,MySQL 持久化)

2. 架构设计

2.1 整体分层架构

Matrix DDD 分层架构图(CQS + EDA)

2.2 DDD 模块分层

每个业务域(如 User、Asset、Alarm)都严格遵循以下模块划分:

模块职责依赖关系
clientCmd / Qry / Evt / 枚举定义无内部依赖(最底层)
domain聚合/实体/值对象、领域服务、Gateway 抽象→ client
applicationAppService(跨聚合编排)、QueryHandler、EventHandler→ domain + client
infrastructureManager、Convertor、GatewayImpl(实现)→ domain
api对外 API 契约(Dubbo 等),给二方 / 三方团队使用无内部依赖(同 client)
adapter-bff前端适配(GraphQL/REST)、用户态处理→ application + domain
adapter-bg后端适配(MQ/Job)→ application + domain

调用规则:

  • 简单场景(单聚合、无跨域协调):Adapter → DomainService
  • 复杂场景(跨聚合编排、多个领域服务协作):Adapter → AppService → DomainService
  • 查询场景:Adapter → QueryHandler(Application 层)→ Manager(Infrastructure 层)
  • 事件场景:MQ Consumer(Adapter 层)→ EventHandler(Application 层)→ DomainService

2.3 CQS 执行路径

写操作(Command)

CQS 写操作(Command)执行路径

读操作(Query)

读操作不走 Repository:Repository 是特殊的 Gateway,只供 DomainService 使用(写路径)。 Application 直接调用 Infrastructure 的 Manager 读取数据,读写隔离,避免读业务绑架写业务、影响核心写路径的稳定性。

CQS 读操作(Query)执行路径

3. 模块详解

3.1 框架模块结构

matrix-framework/
├── kernel-*                      # 内核层(强制 DDD/CQS 基础类)
│   ├── kernel-api/               #   Result / PageResult / MultiResult
│   ├── kernel-annotation/        #   DDD 建模注解(DomainObject/Command/Query 等,IDEA 插件配套)
│   ├── kernel-client/            #   Cmd / Qry / Evt 基类
│   ├── kernel-common/            #   JsonMapper / YamlMapper / 条件表达式 / 调度
│   ├── kernel-adapter/           #   异常包装 AOP / JWT / 权限
│   ├── kernel-domain/            #   AggregateRoot / StatefulDomainService / FsmRegistry
│   ├── kernel-dal/               #   数据访问层(mp / tk / es 实现)
│   └── kernel-dependencies/      #   BOM 版本管理
│
├── components/                    # 组件层(可插拔能力)
│   ├── easy-messaging/            #   消息多后端抽象(7 connector)
│   ├── easy-event-bus/            #   领域事件总线
│   ├── easy-flows/                #   工作流引擎(Warm-Flow 薄封装,含可选设计器)
│   ├── easy-fsm/                  #   有限状态机(cola-statemachine 封装)
│   ├── easy-rules/                #   规则引擎(MVEL / JEXL / LiteFlow)
│   ├── easy-store/                #   通用定义存储(内存/文件/Redis,可监听)
│   ├── easy-graphql/              #   GraphQL 服务端执行引擎
│   ├── easy-idgen/                #   分布式 ID 生成器(雪花/Leaf)
│   ├── easy-lock/                 #   分布式锁(Redis + JDBC 双实现)
│   ├── easy-validate/             #   流式校验器
│   ├── easy-plugin/               #   PF4J 插件化框架
│   ├── easy-rbac/                 #   RBAC 权限控制(可选 starter)
│   ├── easy-cache/                #   多级缓存(L1+Caffeine L2+Redis)+ 广播失效
│   ├── easy-oss/                  #   对象存储(MinIO 后端,SPI 多后端可扩展)
│   ├── easy-schedule/             #   分布式定时任务(db-scheduler 嵌入式)
│   └── easy-socketio/             #   Socket.IO 实时推送(握手鉴权 SPI + 房间协议 + Redis 集群模式)
│
└── middlewares/                   # 中间件层(面向外部接入)
    ├── easy-gateway/              #   API 网关(GraphQL 入口)
    └── easy-notification/         #   实时通知中心(基于 easy-socketio 重写:推送 API + chat 事件)

3.2 Kernel 内核模块

kernel-client — 领域基础类

提供 DDD + CQS 所需的所有基类和接口:

类/接口用途
BaseCommand命令基类,继承 HasTransactor(操作人信息)
BaseQuery / BasePageQuery查询基类,支持分页
BaseDomainEvent领域事件基类
BaseDO数据对象基类
Pagination分页结果封装
HasTransactor操作人信息接口(userId、tenantId 等)
HasBound数据绑定接口

使用示例:

// 定义命令
@Data
public class CreateUserCmd extends BaseCommand {
    private String name;
    private String email;
}

// 定义查询
@Data
public class PageUserQry extends BasePageQuery {
    private String name;
}

// 定义领域事件
@Data
public class UserCreatedEvt extends BaseDomainEvent {
    private String userId;
    private String name;
}

kernel-common — 公共工具

模块说明
util.JsonMapperJSON 序列化/反序列化工具
util.HttpClientUtilHTTP 客户端工具
condition.*条件表达式 AST(Compare / Join / Func)
time.DateUtil日期工具
parameter.Parameters参数处理
scheduling.RedisSchedulerRedis 调度器

kernel-dal-mp — MyBatis-Plus 数据访问

核心类 RepositorySupport 提供了完整的 CRUD + 分页能力:

方法说明
insert(entity, hasTransactor)插入,自动填充操作人
update(entity, hasTransactor)更新,自动填充操作人
deleteById(id, hasTransactor)删除,自动填充操作人
findById(id)根据 ID 查询
findOne(wrapper)条件查询单条
list(wrapper)条件查询列表
page(page, wrapper)分页查询
toPagination(page, convertor)将 MyBatis-Plus Page 转为框架 Pagination

kernel-dal-tk — TkMapper 数据访问

与 kernel-dal-mp 功能相同,基于 TkMapper 实现。项目可根据需要选择使用。


3.3 Components 组件层

easy-messaging — 消息总线(通用消息)

提供通用的消息发送/消费能力,支持 7 种消息中间件:

Connector配置前缀说明
RocketMQeasy.rocketmq.*阿里开源消息队列
Kafkaeasy.kafka.*Apache Kafka
RabbitMQeasy.rabbitmq.*AMQP 消息队列
AutoMQeasy.automq.*云上 Kafka 兼容队列
Redis-基于 Redis Pub/Sub
DBMS-基于数据库表轮询
ZeroMQeasy.zeromq.*轻量级嵌入式消息队列

核心接口:

  • BackendProducer — 消息生产者(同步/异步/单向,有序/无序)
  • BackendConsumer — 消息消费者(注册监听器、启动消费)
  • MessageListener<T> — 消息监听器基类
  • @Consumer — 消费者注解(指定 topic、tag 等)

使用示例:

// 发送消息
@Autowired
private BackendProducer producer;

public void sendMessage() {
    Result result = producer.syncSend("order.created", 
        new GenericMessage<>(orderData));
}

// 消费消息
@Consumer(topic = "order.created")
public class OrderCreatedListener extends MessageListener<OrderCreatedEvent> {
    @Override
    public void onMessage(Headers headers, OrderCreatedEvent message) {
        log.info("收到订单创建事件: {}", message);
    }
}

easy-event-bus — 领域事件总线

专为 DDD/CQS 架构设计的事件总线,用于领域事件的发布与订阅:

配置前缀只有一个 cqrs.*,且只描述「聚合类型 → Topic」的通道映射;各 MQ 的连接参数属于 easy-messaging 的 connector 前缀(easy.rocketmq.* / easy.kafka.* / easy.zeromq.* / easy.rabbitmq.* / easy.automq.*)。 完整配置见 §6.1 与 easy-event-bus USAGE。

配置项说明
cqrs.channels[*].aggregates该通道承载的聚合类型名列表
cqrs.channels[*].topic映射到的后端 topic / channel
cqrs.channels[*].eventClass可选,通道事件基类全限定名
cqrs.default-channel未命中映射时的兜底通道

消费语义:单个 handler 异常只记日志、不阻断同一消息的其他 handler;幂等默认关闭(DuplicateMessageDetector 默认 Noop,防重由业务侧可靠手段承担)。详见 USAGE §6/§8。

核心概念:

  • DomainEventPublisher — 领域事件发布器
  • DomainEventDispatcher — 领域事件分发器
  • MessageProducerImplementation — 消息生产者接口(各 Connector 实现)
  • MessageConsumerImplementation — 消息消费者接口(各 Connector 实现)
  • ChannelMapping — 聚合类型 → Topic 映射

使用示例:

// 在 DomainService 中发布事件
@DomainService(domain = "User")
public class UserDomainService extends BaseDomainService<User, UserRepository> {
    
    @CommandHandler(name = "createUser")
    public User createUser(CreateUserCmd cmd) {
        User user = convertor.toDomain(cmd);
        repository.insert(user, cmd);
        // 发布领域事件:首参是聚合根类型(决定通道映射),不是事件类型
        publish(User.class, user.getUserId(),
            new UserCreatedEvt(user.getUserId(), user.getName()));
        return user;
    }
}

// 在 ApplicationService 中订阅事件
@ApplicationService(domain = "User")
public class UserAppService extends BaseAppService {
    
    @EventHandler
    public void handleUserCreated(UserCreatedEvt event) {
        log.info("用户创建事件: userId={}", event.getUserId());
    }
}

easy-flows — 工作流引擎(Warm-Flow)

基于 Warm-Flow 的轻量、可内嵌工作流引擎薄封装,ORM 对接 kernel-dal-mp(MyBatis-Plus), 作为 DDD 的流程 gateway 在基础设施层接入,不包含业务 Service / Cmd / Qry。

流程定义为 JSON,表数量少(7 张):

能力说明
WorkflowEngine引擎能力门面,返回 Result<T>
DefaultWorkflowEngine委托 Warm-Flow 原生 Service(Def/Ins/Task/HisTask/User)的默认实现
FlowTask引擎层任务视图(待办 / 已办查询结果)
StoreDefinitionLoader从 easy-store 加载流程定义并部署(支持热更新)

操作流 —— 统一为 Result<T>:deploy(json) / start(...) / complete(...) / reject(...) / transfer(...) / cancel(...) / terminate(...);待办、已办分页查询。

扩展点桥接(遵循「不造并行 SPI」,直接桥接 Warm-Flow 原生扩展点):

Warm-Flow 扩展点easy-flows 处理
PermissionHandler✅ PermissionBridge:由 CurrentUserProvider 扩展点(extends org.pf4j.ExtensionPoint)提供 kernel-client.User 解析当前办理人
GlobalListener✅ FlowEventListener:任务完成 → 发布 FlowTaskFinishedEvent(BaseDomainEvent)
DataFillHandler⛔ 默认实现已满足,无需桥接

依赖方向:easy-flows 仅依赖内核数据(kernel-api / kernel-client / kernel-common / kernel-dal-mp)与组件(easy-plugin / 可选 easy-store、easy-rules),不反向依赖接入层 (kernel-adapter)。「当前用户从哪来」抽象为 CurrentUserProvider 扩展点,由使用方在 infrastructure 内实现并提供;未提供时 @ConditionalOnBean 不触发,办理人桥接自动降级。

使用方接入示例(提供当前用户来源):

public class GatewayUserProvider implements CurrentUserProvider {
    @Override
    public User currentUser() {
        // 内部访问使用方自己的用户上下文(如 adapter 的 GatewayContext)
        ...
    }
}

流程设计器(可选):引入 warm-flow-plugin-ui-sb-web(optional 依赖,版本走 BOM)即可激活免搬家的可视化设计器——WarmFlowUiConfig 把 /warm-flow-ui/** 代理到 jar 内前端资源,WarmFlowController 提供 /warm-flow/** REST。需放行这两类路径;办理人/类别/表单三扩展点由使用方实现 HandlerSelectService / CategoryService / FormPathService。详见 easy-flows USAGE。

easy-fsm — 有限状态机

基于 cola-statemachine 的状态机薄封装,用于聚合根内部的状态流转(单实体状态):

组件说明
Fsm / FsmBuilder状态机定义与执行
State / Event / Transit状态、事件、迁移三元组
FsmRegistry状态机注册表(kernel-domain)
visitor.PlantUMLVisitor导出 PlantUML 状态图

easy-rules — 规则引擎

组件说明
RulesExecutor / EngineExecutor规则执行器
engine.MVELRuleExecutor / JEXLRuleExecutor / LiteFlowExecutor三种实现
loader.RulesLoader / MVELRulesLoader规则加载
config.EngineAutoConfiguration多引擎实例自动配置(依赖 easy-store)

配置示例:

# 规则引擎实例配置
easy.engines.instances[0].beanName=myRuleEngine
easy.engines.instances[0].type=rules
easy.engines.instances[0].implementation=mvel
easy.engines.instances[0].enabled=true
easy.engines.instances[0].namespace=test

easy-store — 通用定义存储

通用的「定义 / 配置」存取框架,供规则引擎、工作流等复用作为定义来源:

组件说明
ConfigStorage / WatchableConfigStorage定义存储 / 可监听存储
ConfigRepository存储仓库接口
repository.FileRepository / RedisRepository / InMemoryRepository文件 / Redis / 内存实现
ConfigChangeEvent / ConfigChangeListener / WatchHandle配置变更监听

easy-graphql — GraphQL 服务端

提供 GraphQL 的注解驱动开发。无自动配置,运行时通过 easy-gateway 的 GraphQlConfig 手动装配。

注解用途
@GqlComponentGraphQL 组件(DataFetcher 承载类)
@GqlFetcherDataFetcher 定义
@GqlMutationMutation 定义
@GqlSubscriptionSubscription 定义
@GqlRepository仓储定义
@GqlConnector连接器定义(@connect 自研链路)
@GqlArgumentDataFetcher 参数绑定
@GqlDirective自定义 Directive 定义
@GqlBatching / @BatchLoaderType批量加载定义

核心类:

  • ExecutionGraphQlService / GraphQlSource / DefaultExecutionGraphQlService — 执行引擎入口
  • GqlExecutionConfigurer / GqlExecutionDetectionSupport — 注解扫描与自动接线
  • FederationSchemaFactory — Apollo Federation schema 合成
  • observation/GraphQlObservationInstrumentation — 可观测性 Instrumentation(metrics/tracing,待接入)
  • data/connect/* — @connect 自研链路(HTTP/Dubbo/gRPC/Spring/GraphQL 5 种 Connector)

v1.2.0 清理:删除了废弃的 pagination 链(data/pagination/ 整包 + 依赖它的 7 个 query 孤儿类 + gateway 死方法),删除 support 孤儿(DocumentSource/FutureUtil 等 4 个)。注解包、@connect 链路、observation 包均保留。

easy-cache — 多级缓存

L1 本地 + L2 分布式的两级缓存,支持 Redis Pub/Sub 广播失效保持多节点一致。

缓存层级:

层级实现说明
L1 LOCALCaffeineJVM 内本地缓存,读写最快
L2 REMOTERedis分布式缓存,跨节点共享

核心注解:

  • @EasyCacheable — 声明式缓存
  • @EasyCacheEvict — 声明式失效
  • @EasyCachePut — 声明式更新

配置:

easy.cache.enabled=true
easy.cache.broadcast.enabled=true
easy.cache.broadcast.channel=easy:cache:evict
# Caffeine L1
easy.cache.local.max-size=10000
easy.cache.local.expire-after-write=300s

v1.2.0 修复:广播订阅改为懒初始化,不再在 @PostConstruct 里硬连 Redis。首次调用缓存失效时才尝试订阅,Redis 不可达则静默降级为单机模式,启动日志干净无大段异常堆栈。

easy-oss — 对象存储

后端中立的对象存储客户端门面,复用「门面 → 抽象代理 → SPI → Connector」分层,业务只依赖 ObjectStorage 接口,后端可替换:

组件说明
ObjectStorage / DefaultObjectStorage存储门面 / 默认实现(key 生成 + 委托后端)
BackendObjectStorage后端 SPI,storage/minio/MinioObjectStorage 为首个实现
ObjectKeyFactory / DefaultObjectKeyFactory对象 key 生成({tenant}/{endpoint}/{yyyy/MM/dd}/{uuid}-{name})
FileObject上传返回值(objectName / bucket / size / metadata)
OssPropertieseasy.oss.* 配置(含 minio.endpoint/access-key/secret-key)

能力:上传(putString/putBytes/putStream/putFile)、下载、元数据、预签名 URL(直下/直传)、删除、桶管理与对象列表。MinIO 客户端为 optional scope,未配置时组件自动关闭。

配置:

easy.oss.enabled=true
easy.oss.default-bucket=matrix
easy.oss.minio.endpoint=http://127.0.0.1:9000
easy.oss.minio.access-key=admin
easy.oss.minio.secret-key=password

中间件服务端(规划):浏览器直传、统一鉴权、URL 代理三个阶段列入演进规划,本期(客户端组件)未实现。

easy-schedule — 分布式定时任务

基于 db-scheduler 的嵌入式分布式定时任务组件。不部署独立调度服务,依赖 MySQL 单表持久化,通过 DB 行锁保证集群单实例执行。

特性:

特性说明
Cron 周期@Job(cron = "0 */5 * * * *") 注解驱动
一次性/延迟fireOnce() 动态触发,支持 POJO payload
分片并行@Job(shardCount = 3) 多节点同时跑不同分片
集群单实例MySQL scheduled_tasks 表 + 行锁,多节点抢占
无 ZK/Redis仅依赖 MySQL

使用示例:

@Component
public class ExampleJobs {

    @Job(name = "clock", cron = "0 */1 * * * *", desc = "每分钟示例任务")
    public void clock(ExecutionContext ctx) {
        log.info("tick, instanceId={}", ctx.getInstanceId());
    }

    @Job(name = "shard-example", cron = "0 */2 * * * *", shardCount = 3)
    public void shardExample() {
        // 内部按 shardIndex/shardTotal 路由,各分片并行执行
    }
}

配置:

easy.schedule.enabled=true
easy.schedule.threads=10
easy.schedule.polling-interval=5s

MySQL DDL

-- db-scheduler 引擎表(组件固定使用 sch_tasks 表名)
CREATE TABLE sch_tasks (
    task_name VARCHAR(100),
    task_instance VARCHAR(100),
    task_data LONGBLOB,
    execution_time DATETIME(6),
    picked TINYINT(1),
    picked_by VARCHAR(50),
    last_success DATETIME(6),
    last_failure DATETIME(6),
    consecutive_failures INT,
    last_heartbeat DATETIME(6),
    version BIGINT,
    priority SMALLINT,
    PRIMARY KEY (task_name, task_instance),
    INDEX idx_execution_time_picked (execution_time, picked)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;

-- easy-schedule 动态 cron 配置表
CREATE TABLE sch_job_config (
    job_code     VARCHAR(96) PRIMARY KEY,
    cron         VARCHAR(64) NOT NULL COMMENT '6 位 cron 表达式',
    enabled      TINYINT(1) DEFAULT 1,
    shard_count  INT DEFAULT 1,
    payload_json JSON COMMENT '默认 payload(可选)',
    description  VARCHAR(256),
    owner        VARCHAR(64),
    created_at   DATETIME,
    updated_at   DATETIME,
    INDEX idx_enabled (enabled)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;

版本说明:v1 支持 cron + 一次性 + 分片并行。v2 规划 DAG 编排(jgrapht)、执行历史记录表、管理台可视化。

easy-idgen — ID 生成器

提供分布式 ID 生成:

实现配置前缀说明
Snowflakematrix.id-generator.snowflake.*雪花算法
Leaf-美团 Leaf 号段模式
Simple-本地简单 ID(测试用)

配置示例:

matrix.id-generator.snowflake.enabled=true
matrix.id-generator.snowflake.worker-id=1

easy-lock — 分布式锁

注解驱动的分布式锁:

@Locked(expression = "#userId", retry = @Retryable(interval = 100))
public void doSomething(String userId) {
    // 被保护的代码
}

easy-validate — 流式校验器

提供流畅的校验 API:

validate(user)
    .notNull("name")
    .length("email", 5, 100)
    .regex("phone", "^1[3-9]\\d{9}$")
    .throwIfInvalid();

easy-plugin — PF4J 插件扩展

基于 PF4J(Plugin Framework for Java)的插件化能力(Spring Boot Fat-Jar 场景二次封装):

  • FatJarPluginManager — 胖包插件管理器(重写 Loader / Repository / DescriptorFinder 三大工厂)
  • FatJarPluginLoader — 插件加载器(直接使用 Spring Boot 主 ClassLoader)
  • PluggableUtil / ExecutionUtil — 扩展旁路缓存 + 节点路径(Pattern)匹配执行
  • Pattern — ACCURATE / CHILDREN / DESCENDANTS / WILDCARD 匹配策略

升级策略:依赖已回退到官方 pf4j-spring 0.8.0 / pf4j 3.8.0,原定制逻辑以同 FQCN shadow 类内置在 easy-plugin 源码(Extension / ExtensionDescriptor / AbstractExtensionFinder / SpringPluginManager / FileUtils),由 classpath 优先级覆盖官方 jar,摆脱对定制 jar 的依赖。Fat-Jar 模式不支持插件热部署(需重启应用)。


3.4 Middlewares 中间件层

easy-gateway — API 网关

提供协议适配、路由、限流、鉴权等网关能力。

easy-notification — 实时通知中心

基于 easy-socketio 的实时通知中间件:服务器装配、握手鉴权(AuthListener SPI)、三级房间协议(user/broadcast/product)、Redis 分布式均由组件层提供,中间件只含业务层——NotificationPusher 推送门面(toUser / toProduct / broadcast)与 chat 事件处理器。独立部署形态(fatjar + main 入口)。详见 设计文档 与 使用文档。


4. 快速上手

4.1 创建一个 Matrix 项目

步骤 1:创建父项目

<!-- pom.xml -->
<parent>
    <groupId>io.matrix</groupId>
    <artifactId>components</artifactId>
    <version>1.2.1-SNAPSHOT</version>
</parent>

步骤 2:创建标准模块结构

your-project/
├── your-client/           # Cmd/Qry/Evt 定义
├── your-domain/           # 领域模型
├── your-application/      # 应用服务
├── your-infrastructure/   # 基础设施实现
├── your-api/              # 对外 API
├── your-adapter-bff/      # 前端适配
├── your-adapter-bg/       # 后端适配
└── pom.xml                # 父 POM

步骤 3:添加核心依赖

<!-- 内核模块 -->
<dependency>
    <groupId>io.matrix</groupId>
    <artifactId>kernel-client</artifactId>
</dependency>
<dependency>
    <groupId>io.matrix</groupId>
    <artifactId>kernel-common</artifactId>
</dependency>
<dependency>
    <groupId>io.matrix</groupId>
    <artifactId>kernel-dal-mp</artifactId>  <!-- 或 kernel-dal-tk -->
</dependency>

<!-- 消息总线(根据需要选择 Connector) -->
<dependency>
    <groupId>io.matrix</groupId>
    <artifactId>easy-event-bus</artifactId>
</dependency>
<dependency>
    <groupId>io.matrix</groupId>
    <artifactId>easy-messaging</artifactId>
</dependency>

<!-- Connector 依赖(根据选择的消息中间件) -->
<dependency>
    <groupId>org.apache.kafka</groupId>
    <artifactId>kafka-clients</artifactId>
    <version>3.4.1</version>
</dependency>

4.2 参考实现:iot-care 项目

iot-care(ic-backend 模块)是一个完整的 Matrix 框架实战项目(独立仓库,需另行获取),包含:

领域聚合根说明
UserTenant、Customer、User多租户用户体系
AssetAsset、Device资产与设备管理
AlarmAlarm告警与状态流转
DashboardDashboard数据仪表盘
WidgetWidget仪表盘组件
EntityViewEntityView跨实体视图

项目结构参考:

ic-backend/
├── eager-client/          # Cmd/Qry/Evt 定义
├── eager-domain/          # 领域模型 + Gateway 抽象
├── eager-application/     # AppService 用例编排
├── eager-infrastructure/  # Manager + Convertor + GatewayImpl
├── eager-api/             # 对外 API
├── eager-adapter-bff/     # GraphQL 前端适配
├── eager-adapter-bg/      # MQ 后端适配
└── design/
    ├── local/              # AVDL 领域建模文件
    └── sql/                # 数据库初始化脚本

5. 核心组件使用指南

5.1 领域建模(AVDL)

Matrix 框架使用 AVDL(Avro IDL)进行领域建模。建模文件存放于 design/local/ 目录。

文件结构:

design/local/
├── User.avdl         # 用户领域
├── Asset.avdl        # 资产领域
├── Alarm.avdl        # 告警领域
└── ...

AVDL 三段式结构:

// ============== 类型协议区 ==============
@namespace("com.example.user.domain")
protocol UserTypes {

  @DomainObject(domain = "User", type = AGGREGATE_ROOT)
  record User {
    @DomainId
    string userId;
    string name;
    string email;
  }

  enum UserAuthority { TENANT_ADMIN, CUSTOMER_USER }
}

---
// ============== 领域服务区 ==============
@namespace("com.example.user.service")
protocol UserDomainService {

  @DomainService(domain = "User")
  interface UserDomainService {

    @CommandHandler(name = "createUser")
    User createUser(CreateUserCmd cmd);

    @CommandHandler(name = "updateUser")
    User updateUser(UpdateUserCmd cmd);
  }
}

---
// ============== 应用服务区 ==============
@namespace("com.example.user.service")
protocol UserApplicationService {

  @ApplicationService(domain = "User")
  interface UserAppService {

    @QueryHandler(name = "pageUser")
    Pagination<User> pageUser(PageUserQry qry);
  }
}

5.2 领域服务实现(DomainService)

@Service
@DomainService(domain = "User")
@RequiredArgsConstructor
public class UserDomainService extends BaseDomainService<User, UserRepository> {

    private final UserDomainConvertor convertor;

    @CommandHandler(name = "createUser", desc = "创建用户", steps = {
        @Step(name = "参数校验"),
        @Step(name = "构建聚合根"),
        @Step(name = "持久化"),
        @Step(name = "发布事件")
    })
    public User createUser(CreateUserCmd cmd) {
        // 1. 参数校验:BaseService 继承 ValidateSupport,提供
        //    validate(entity, Consumer<ValidatorDelegate>) 与 validate(entity, yavi Validator<T>) 两种形式,
        //    用法见 easy-validate USAGE

        // 2. 构建聚合根
        User user = convertor.toDomain(cmd);

        // 3. 持久化:repository 由 BaseDomainService 注入(@Autowired(required = false)),
        //    insert / update 等方法在业务侧 UserRepository 上声明
        repository.insert(user, cmd);

        // 4. 发布领域事件:首参为聚合根(决定 aggregateType),事件之间不共享事务外副作用
        publish(user, new UserCreatedEvt(user.getUserId(), user.getName()));

        return user;
    }
}

5.3 应用服务实现(AppService)

@Service
@ApplicationService(domain = "User")
@RequiredArgsConstructor
public class UserAppService extends BaseAppService {

    private final UserRepository repository;

    @QueryHandler(name = "pageUser", desc = "分页查询用户")
    public Pagination<User> pageUser(PageUserQry qry) {
        return repository.pageUser(qry);
    }

    @EventHandler
    public void handleUserCreated(UserCreatedEvt event) {
        log.info("处理用户创建事件: userId={}", event.getUserId());
    }
}

5.4 仓储实现(Repository + Manager)

Domain 层接口:

public interface UserRepository extends BaseRepository<User> {
    Boolean insert(User user, HasTransactor hasTransactor);
    Boolean update(User user, HasTransactor hasTransactor);
    User findByUserId(String userId);
    Pagination<User> pageUser(PageUserQry qry);
}

BaseRepository<T> 是空标记接口(只做类型约束,便于 BaseDomainService<T, R> 约束泛型), CRUD 方法由业务侧按需声明,实现在 Infrastructure 的 UserInfraRepository + UserManager。

Infrastructure 层 Manager:

@Component
@RequiredArgsConstructor
public class UserManager extends RepositorySupport<UserMapper, UserEntity> {

    private final UserInfraConvertor convertor;

    public Pagination<User> pageUser(PageUserQry qry) {
        Page<UserEntity> page = new Page<>(qry.getPage(), qry.getSize());
        QueryWrapper<UserEntity> wrapper = new QueryWrapper<>();
        if (StrUtil.isNotBlank(qry.getName())) {
            wrapper.like("name", qry.getName());
        }
        wrapper.orderByDesc("created_time");
        return toPagination(this.page(page, wrapper), convertor::toUserDomainList);
    }
}

Infrastructure 层 Repository 实现:

@Component
@RequiredArgsConstructor
public class UserInfraRepository implements UserRepository {
    private final UserInfraConvertor convertor;
    private final UserManager manager;

    @Override
    public Pagination<User> pageUser(PageUserQry qry) {
        return manager.pageUser(qry);
    }

    @Override
    public Boolean insert(User user, HasTransactor hasTransactor) {
        return manager.insert(convertor.toUserEntity(user), hasTransactor);
    }
}

5.5 Convertor 转换(MapStruct)

@Mapper(componentModel = "spring")
public interface UserInfraConvertor {
    UserEntity toUserEntity(User domain);
    User toUserDomain(UserEntity entity);
    List<User> toUserDomainList(List<UserEntity> entities);
}

5.6 前端适配(Adapter-BFF)

BFF 主协议是 GraphQL:由 easy-graphql(注解驱动执行引擎)+ easy-gateway(GraphQlConfig 装配)承载, 注解清单见 §3.3 easy-graphql 与 GRAPHQL_COMPONENT_TRIAGE.md。下面给一个纯 REST 的等价写法:

@RestController
@RequestMapping("/api/user")
@RequiredArgsConstructor
public class UserRestController {

    private final UserAppService userAppService;

    @PostMapping("/page")
    public Result<PageResult<User>> pageUser(@RequestBody PageUserQry qry) {
        Pagination<User> page = userAppService.pageUser(qry);
        return Result.success(PageResult.of(page.getItems(), page.getTotal()));
    }
}

5.7 MQ 后端适配(Adapter-BG)

@Component
@Consumer(topic = "telemetry.submitted")
public class TelemetryConsumer extends MessageListener<TelemetrySubmittedEvt> {

    private final RuleChainAppService ruleChainAppService;

    @Override
    public void onMessage(Headers headers, TelemetrySubmittedEvt message) {
        log.info("收到遥测数据: {}", message.getDeviceId());
        ruleChainAppService.handleTelemetrySubmitted(message);
    }
}

6. 配置参考

6.1 消息总线配置

配置分两层:事件总线只描述「聚合类型 -> Topic」的通道映射(唯一前缀 cqrs.*);各 MQ 后端连接参数由 easy-messaging 的 connector 前缀承载。两者相互独立,可任意组合。

事件总线:通道映射(easy-event-bus)

# 聚合类型 -> Topic 通道映射
cqrs.channels[0].aggregates[0]=Device
cqrs.channels[0].topic=device_events
# 可选:该通道的事件基类全限定名
cqrs.channels[0].event-class=io.matrix.event.DeviceEvent
# 未命中映射时的兜底通道(同样是 Channel 结构)
cqrs.default-channel.topic=matrix_default_events

各 MQ 后端连接参数(easy-messaging connector)

# ZeroMQ
easy.zeromq.enabled=true
easy.zeromq.pub-address=tcp://*:5555
easy.zeromq.sub-address=tcp://localhost:5555

# Kafka
easy.kafka.enabled=true
easy.kafka.bootstrap-servers=localhost:9092

# RocketMQ
easy.rocketmq.enabled=true
easy.rocketmq.server-url=localhost:9876

# RabbitMQ
easy.rabbitmq.enabled=true

Redis / DBMS connector 无独立配置类,随对应基础设施装配启用。

6.2 ID 生成器配置

matrix.id-generator.snowflake.enabled=true
matrix.id-generator.snowflake.epoch=1784476800000
matrix.id-generator.snowflake.worker-id=1
matrix.id-generator.snowflake.worker_id_bits=2
matrix.id-generator.snowflake.sequence_bits=1
matrix.id-generator.snowflake.allow-clock-backward=true
matrix.id-generator.snowflake.max-clock-backward-ms=5

6.3 规则引擎配置

# 文件存储配置
easy.engines.storages[0].bean-name=exampleGqlStorage
easy.engines.storages[0].repository-bean-name=fileRepository
easy.engines.storages[0].enabled=true
easy.engines.storages[0].namespace=example-gql

# 规则引擎实例
easy.engines.instances[0].beanName=myRuleEngine
easy.engines.instances[0].type=rules
easy.engines.instances[0].implementation=mvel
easy.engines.instances[0].enabled=true
easy.engines.instances[0].namespace=test
easy.engines.instances[0].repositoryBeanName=fileRepository

6.4 数据源配置

spring.datasource.url=jdbc:mysql://localhost:3306/ic-eager?useSSL=true&characterEncoding=utf8&serverTimezone=Asia/Chongqing
spring.datasource.username=root
spring.datasource.password=xxx
spring.datasource.hikari.connectionTimeout=10000

7. 最佳实践

7.1 分层约束

规则原因
Domain 层不依赖任何外部技术框架保护领域模型不受技术变迁影响
Domain 层只能定义 Gateway/Repository 抽象依赖倒置,具体实现放 Infrastructure
Adapter 层不写业务规则只做协议适配、参数校验、用户态处理
Application 层不做状态校验只编排用例,业务规则放 Domain
单聚合场景直接调用 DomainService无需经过 AppService,减少不必要的封装
跨聚合场景必须经过 AppService保证跨域协调在同一层完成,避免领域耦合
Entity 不回渗到 Domain 层防腐层隔离

7.2 命名约定

类型命名后缀示例
命令CmdCreateUserCmd
查询QryPageUserQry
领域事件EvtUserCreatedEvt
聚合根名词User、Device
领域服务DomainServiceUserDomainService
应用服务AppServiceUserAppService
仓储接口RepositoryUserRepository
仓储实现InfraRepositoryUserInfraRepository
ManagerManagerUserManager
转换器ConvertorUserInfraConvertor
持久化实体EntityUserEntity
监听器Listener/ConsumerUserCreatedListener

7.3 聚合设计原则

  1. 聚合边界内强一致:同一聚合内的对象在同一个事务中更新
  2. 跨聚合用事件:跨聚合的协作通过领域事件异步完成
  3. 聚合引用用 ID:聚合之间通过 ID 引用,不直接持有对象引用
  4. 聚合大小适中:避免上帝聚合,也避免过度碎片化

7.4 消息总线选型指南

场景推荐 Connector原因
生产环境高吞吐量Kafka高吞吐、持久化、消费者组
企业级功能(事务消息、定时消息)RocketMQ功能最全
小型部署 / 开发环境ZeroMQ无需额外部署中间件
已有 Redis 基础设施Redis复用现有资源
极简场景(无额外中间件)DBMS基于数据库表

7.5 常见问题

问题原因解决方案
BackendConsumer bean not found消息 Connector 未激活检查是否添加了对应的 Connector 依赖和配置
ConsumeConfig must be set@Consumer 注解未生效确保监听器类被 Spring 扫描,且有 @Component 注解
循环依赖错误的分层引用检查 Domain 是否引用了 Application
编译时类找不到模块依赖顺序问题从父项目执行 mvn clean install -DskipTests
Stream.toList() 找不到Java 8 限制改为 .collect(Collectors.toList())
Pagination.of() 参数类型错MyBatis-Plus 返回 long使用 (int) 强转
无效的目标发行版: 17本地 JDK 不是 17export JAVA_HOME=$(/usr/libexec/java_home -v 17)
Lombok 编译 crashLombok < 1.18.20 不兼容 JDK 17升到 1.18.32 或更高
javax.annotation.* 类找不到JDK 11+ 移除了 javax.annotation显式声明 javax.annotation-api 1.3.2(provided)
easy-cache 启动大段 WARNRedis 没起,@PostConstruct 硬连抛异常v1.2.0 已改为懒初始化,首次广播时才尝试订阅
easy-cache 跨节点缓存永远不一致(隐蔽 bug)手动 new RedisMessageListenerContainer() 没调 afterPropertiesSet() 导致 Subscriber 未创建v1.2.0 已修复:先 afterPropertiesSet() 再 start()

7.6 JDK 17 迁移注意事项(SB 2.7 下)

项说明
语言等级编译目标 17,但 SB 2.7 仍是 javax 包,不涉及 jakarta 迁移
Lombok必须 ≥ 1.18.20,推荐 1.18.32
javax.annotation-api1.3.2 provided,kernel-domain 仍在用 @Resource
slf4j-api建议 ≥ 1.7.36(某些 JDK 17 版本有兼容 bug)
db-schedulerv16 硬要求 JDK 17+,正好匹配
maven.compiler.compilerVersiondeprecated 属性,可删;source/target 走版本号即可
guava框架已清理,用 java.util.function.Function 替代
pac4j-core 4.5.6框架已清理,全仓零引用

附录

A. 完整项目依赖图

Matrix 完整项目依赖图

B. 相关资源

资源位置
框架规范(铁律 + 架构约定)AGENTS.md
组件使用文档docs/components/
领域建模注解(Avro 转换规范)ddd-annotations.md
插件扩展指南pf4j-extension-guide.md
iot-care 实战项目独立仓库(外部参考实现,非本框架依赖)

C. 架构演进 TODO

#模块待办项
1kernel-adapter添加幂等提交功能(防重复提交),基于 Token + Redis 去重机制
2components/easy-notify新建通知组件(邮件/短信/站内信多通道,区别于已建的 easy-notification 实时推送)

已落地:components/easy-oss —— 文件存储组件(MinIO 后端),客户端能力已完成;浏览器直传 / 统一鉴权 / URL 代理等中间件服务端能力列入 easy-oss 演进规划。