Skip to content

Commit 0d1326c

Browse files
committed
docs: synced via GitHub Actions
1 parent 615a60b commit 0d1326c

File tree

1 file changed

+57
-44
lines changed

1 file changed

+57
-44
lines changed

src/dev-guide/nocode/dyn-model-design.md

Lines changed: 57 additions & 44 deletions
Original file line numberDiff line numberDiff line change
@@ -1,27 +1,27 @@
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
// 统一业务访问接口
2626
IBizObject bizObj = bizObjectManager.getBizObject(bizObjName);
2727
bizObj.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

4242
1. **业务对象加载**
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

4849
2. **数据库访问**
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
// 租户感知的缓存层次结构
7275
TenantAwareResourceLoadingCache
73-
├── 租户缓存容器 (tenantCaches)
74-
│ └── 各租户独立的ResourceLoadingCache
75-
└── 共享缓存 (shareCache)
76+
├── 租户缓存容器(tenantCaches)
77+
│ └──各租户独立的ResourceLoadingCache
78+
└──
79+
80+
共享缓存(shareCache)
7681
```
7782

7883
### 缓存管理
7984

8085
**统一缓存接口**
86+
8187
```java
8288
public 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
92103
protected 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+
180193
ConfigProvider目前考虑不进行动态化。首先,动态配置已经通过Nacos配置中心支持。第二,每个租户如果有特殊配置应该使用另外一个配置获取接口,而不是使用全局的ConfigProvider。
181194
全局的ConfigProvider在所有模型加载的时候都会被读取,如果混合使用,有可能全局模型动态加载的时候读取到了租户的配置。
182195

0 commit comments

Comments
 (0)