1- 以下你觉得怎么样
21# Nop平台动态模型加载与多租户支持架构设计
32
43## 设计概览
54
6- Nop平台采用统一的动态模型加载机制,支持多租户环境下的资源隔离。核心设计基于"Loader as Generator"模式:` Model = Loader(virtualPath × tenantId) ` ,通过标准化的Provider接口实现模型的按需加载和缓存管理。
5+ Nop平台采用统一的动态模型加载机制,支持多租户环境下的资源隔离。核心设计基于"Loader as Generator"模式:
6+ ` Model = Loader(virtualPath × tenantId) ` ,通过标准化的Provider接口实现模型的按需加载和缓存管理。
77
88## 核心架构
99
1010### 组件职责边界
1111
12- | 组件 | 职责 | 关键方法 |
13- | ---------------------------------| ------------------------| -----------------------------------------------|
14- | ** IDynamicResourceProvider ** | 虚拟路径 → 模型文件资源 | ` getResource(virtualPath) ` |
15- | ** IDynamicBizModelProvider ** | 业务对象名 → XMeta/XBiz模型路径 | ` getBizModel (bizObjName)` , ` getBizObjNames ()` |
16- | ** IOrmModelProvider** | 加载ORM模型 | ` getOrmModel(persistEnv) ` |
17- | ** IDynamicEntityModelProvider** | 实体名 → 实体模型 | ` getEntityModel (entityName)` |
18- | ** IDynamicModuleDiscovery ** | 发现可用模块 | ` getEnabledModules ()` |
12+ | 组件 | 职责 | 关键方法 |
13+ | ---------------------------------| ------------------------| ----------------------------------------------------------- |
14+ | ** ITenantResourceProvider ** | 虚拟路径 → 模型文件资源 | ` getTenantResourceStore(tenantId) ` |
15+ | ** ITenantBizModelProvider ** | 业务对象名 → XMeta/XBiz模型路径 | ` getTenantBizModel (bizObjName)` , ` getTenantBizObjNames ()` |
16+ | ** IOrmModelProvider** | 加载ORM模型 | ` getOrmModel(persistEnv) ` |
17+ | ** IDynamicEntityModelProvider** | 实体名 → 实体模型 | ` getDynamicEntityModel (entityName)` |
18+ | ** ITenantModuleDiscovery ** | 发现可用模块 | ` getEnabledTenantModules ()` |
1919
2020通过依赖注入获取这些接口的实现。
2121
2222### 核心访问模式
2323
24- ``` java
24+ ``` javascript
2525// 统一业务访问接口
2626IBizObject bizObj = bizObjectManager .getBizObject (bizObjName);
2727bizObj .invoke (actionName, request, selection, svcCtxt);
@@ -32,33 +32,36 @@ bizObj.invoke(actionName, request, selection, svcCtxt);
3232### 加载流程链
3333
3434``` text
35- 业务请求 → BizObjectManager → IDynamicBizModelProvider → 模型路径 → IDynamicResourceProvider → 资源内容
35+ 业务请求 → BizObjectManager → ITenantBizModelProvider → 模型路径 → ITenantResourceProvider → 资源内容
3636 ↓
3737数据库访问 → OrmSessionFactory → IOrmModelProvider → LazyLoadOrmModel → entityName → IDynamicEntityModelProvider → IEntityModel
3838```
3939
4040### 详细加载过程
4141
42421 . ** 业务对象加载**
43- - ` BizObjectManager ` 根据 ` bizObjName ` 查找缓存
44- - 未命中时通过 ` IDynamicBizModelProvider ` 加载 ` GraphQLBizModel `
45- - 根据返回的 ` bizPath ` 和 ` metaPath ` 加载模型文件
46- - ` ResourceComponentManager ` 调用 ` IDynamicResourceProvider ` 获取资源内容
43+
44+ - ` BizObjectManager ` 根据 ` bizObjName ` 查找缓存
45+ - 未命中时通过 ` ITenantBizModelProvider ` 加载 ` GraphQLBizModel `
46+ - 根据返回的 ` bizPath ` 和 ` metaPath ` 加载模型文件
47+ - ` ResourceComponentManager ` 调用 ` ITenantResourceProvider ` 获取资源内容
4748
48492 . ** 数据库访问**
49- - Action中通过 ` OrmSessionFactory ` 获取会话
50- - 通过 ` IOrmModelProvider ` 动态获取实体ORM模型
51- - 支持按模块粒度加载ORM模型
50+
51+ - Action中通过 ` OrmSessionFactory ` 获取会话
52+ - 通过 ` IOrmModelProvider ` 动态获取实体ORM模型
53+ - 支持按模块粒度加载ORM模型
5254
5355### 加载粒度策略
5456
55- | 粒度级别 | 描述 | 触发条件 |
56- | ----------| ------| ----------|
57- | ** 资源文件级** | 单个IResource资源文件 | ` IDynamicResourceProvider .getResource()` |
58- | ** 业务对象级** | XMeta + XBiz模型文件对 | ` BizObjectManager.getBizObject() ` |
59- | ** 模块级** | 一组相关业务对象及ORM模型 | 访问模块内任何资源 |
57+ | 粒度级别 | 描述 | 触发条件 |
58+ | ----------- | ------------------- | -------------------------- ----------|
59+ | ** 资源文件级** | 单个IResource资源文件 | ` IVirtualFileSystem .getResource()` |
60+ | ** 业务对象级** | XMeta + XBiz模型文件对 | ` BizObjectManager.getBizObject() ` |
61+ | ** 模块级** | 一组相关业务对象及ORM模型 | 访问模块内任何资源 |
6062
6163** 关键特性** :
64+
6265- 最小加载粒度为单个资源文件
6366- 所有加载操作均为Lazy模式
6467- 动态加载器作为最后回退方案
@@ -67,34 +70,42 @@ bizObj.invoke(actionName, request, selection, svcCtxt);
6770
6871### 租户隔离架构
6972
70- ``` java
73+ ``` shell
7174// 租户感知的缓存层次结构
7275TenantAwareResourceLoadingCache
73- ├── 租户缓存容器 (tenantCaches)
74- │ └── 各租户独立的ResourceLoadingCache
75- └── 共享缓存 (shareCache)
76+ ├── 租户缓存容器(tenantCaches)
77+ │ └──各租户独立的ResourceLoadingCache
78+ └──
79+
80+ 共享缓存(shareCache)
7681```
7782
7883### 缓存管理
7984
8085** 统一缓存接口** :
86+
8187``` java
8288public interface ICacheManagement <K> {
83- String getName ();
84- void remove (@Nonnull K key );
85- void clear ();
86- default void clearForTenant (String tenantId ) {}
89+ String getName ();
90+
91+ void remove (@Nonnull K key );
92+
93+ void clear ();
94+
95+ default void clearForTenant (String tenantId ) {
96+ }
8797}
8898```
8999
90100** 租户缓存路由逻辑** :
101+
91102``` java
92103protected ResourceLoadingCache<V > getCache(String path) {
93- String tenantId = getTenantId();
94- if (StringHelper . isEmpty(tenantId) || ! ResourceTenantManager . supportTenant(path)) {
95- return shareCache; // 无租户或路径不支持租户
96- }
97- return tenantCaches. get(tenantId); // 租户特定缓存
104+ String tenantId = getTenantId();
105+ if (StringHelper . isEmpty(tenantId) || ! ResourceTenantManager . supportTenant(path)) {
106+ return shareCache; // 无租户或路径不支持租户
107+ }
108+ return tenantCaches. get(tenantId); // 租户特定缓存
98109}
99110```
100111
@@ -113,17 +124,18 @@ protected ResourceLoadingCache<V> getCache(String path) {
113124// 无状态模块初始化流程
114125模块访问 → 执行模块初始化 → 生成模型文件 → 注册监听器
115126```
127+
116128- 基于模板 ` /nop/templates/dyn-module ` 动态生成
117129- 完全无状态,无初始化顺序依赖
118130- 按需触发,避免不必要的初始化开销
119131
120132### 模块间协作
121133
122- | 协作方式 | 机制 | 特点 |
123- | ----------| ------| ------|
134+ | 协作方式 | 机制 | 特点 |
135+ | ----------| ---------------- | ----- ------|
124136| ** 服务调用** | 通过IBizObject接口 | 松耦合,运行时发现 |
125- | ** 事件通信** | 消息总线 + 监听器 | 解耦,异步处理 |
126- | ** 数据共享** | 通过标准业务接口 | 避免直接数据访问 |
137+ | ** 事件通信** | 消息总线 + 监听器 | 解耦,异步处理 |
138+ | ** 数据共享** | 通过标准业务接口 | 避免直接数据访问 |
127139
128140** 监听器初始化** :首次需要事件处理时,搜集所有启用模块的监听器,触发相关模块初始化。
129141
@@ -156,11 +168,11 @@ protected ResourceLoadingCache<V> getCache(String path) {
156168
157169### 典型错误场景
158170
159- | 场景 | 处理方式 | 恢复策略 |
160- | ------| ----------| -- --------|
171+ | 场景 | 处理方式 | 恢复策略 |
172+ | --------- | ---------------- | --------|
161173| 模型文件未找到 | 抛出NopException | 检查路径配置 |
162- | 加载过程异常 | 直接抛出异常 | 修复底层问题 |
163- | 租户资源隔离 | 租户特定异常 | 检查租户权限 |
174+ | 加载过程异常 | 直接抛出异常 | 修复底层问题 |
175+ | 租户资源隔离 | 租户特定异常 | 检查租户权限 |
164176
165177## 性能优化
166178
@@ -177,6 +189,7 @@ protected ResourceLoadingCache<V> getCache(String path) {
177189- ** 模块级优化** :ORM模型按模块整体加载
178190
179191## 其他
192+
180193ConfigProvider目前考虑不进行动态化。首先,动态配置已经通过Nacos配置中心支持。第二,每个租户如果有特殊配置应该使用另外一个配置获取接口,而不是使用全局的ConfigProvider。
181194全局的ConfigProvider在所有模型加载的时候都会被读取,如果混合使用,有可能全局模型动态加载的时候读取到了租户的配置。
182195
0 commit comments