添加文档
This commit is contained in:
@@ -0,0 +1,859 @@
|
||||
# Adminive 设计理念与架构原则
|
||||
|
||||
本文描述 Adminive 当前架构的设计目标、职责边界和长期演进原则。它不是 API 速查表;具体接口和完整示例仍以项目根目录 `README.md` 为准。
|
||||
|
||||
## 1. 项目定位
|
||||
|
||||
Adminive 的目标不是重新发明对象模型、反射系统或属性系统,而是把后端已经存在的 C++ 结构投影成后台管理能力。
|
||||
|
||||
可以把两层职责概括为:
|
||||
|
||||
```text
|
||||
Structive
|
||||
描述并管理“这个 C++ 对象本身是什么”
|
||||
|
||||
Adminive
|
||||
描述“后台管理系统应该怎样展示、编辑、传输和提交这个对象”
|
||||
```
|
||||
|
||||
因此 Adminive 更接近一个 backend-driven administration projection layer,而不是一个新的 Property Core。
|
||||
|
||||
它围绕同一份后端描述连接这些领域:
|
||||
|
||||
```text
|
||||
C++ business type
|
||||
│
|
||||
▼
|
||||
Adminive descriptor
|
||||
│
|
||||
├── JSON encode / decode
|
||||
├── frontend schema
|
||||
├── AMIS CRUD / form / table
|
||||
├── HTTP resource service
|
||||
├── validation / error path
|
||||
├── managed update
|
||||
└── external transaction
|
||||
```
|
||||
|
||||
前端消费后端生成的描述结果,而不是重新维护一份业务字段定义。
|
||||
|
||||
## 2. 最核心的设计原则
|
||||
|
||||
Adminive 的核心原则可以浓缩为以下几条:
|
||||
|
||||
1. **Structive 管结构事实,Adminive 管后台管理语义。**
|
||||
2. **属性信息必须能够影响真实行为,而不只是生成 UI 文本。**
|
||||
3. **默认只读,显式声明可写。**
|
||||
4. **同步只保护 mutable consistency,不承担权限、事务或持久化职责。**
|
||||
5. **JSON、HTTP、枚举反射、结构体反射都是 Adapter,不进入核心协议。**
|
||||
6. **外部系统是否暴露字段由外部系统自己决定,Property Core 不做访问控制。**
|
||||
7. **静态可知的事实尽量在编译期表达,动态输入才留到运行期处理。**
|
||||
8. **前端是后端描述的消费者,不成为第二个业务规则中心。**
|
||||
9. **外部副作用事务与内存对象同步保持正交。**
|
||||
10. **不为了统一形式制造重复抽象;已经由 Structive 解决的问题不在 Adminive 再实现一次。**
|
||||
|
||||
后续新增功能如果违反其中任意一条,都应该先重新确认它是否真的属于 Adminive Core。
|
||||
|
||||
## 3. Structive 与 Adminive 的边界
|
||||
|
||||
### 3.1 Structive 负责什么
|
||||
|
||||
`third_party/Structive` 负责对象自身的结构能力:
|
||||
|
||||
```text
|
||||
Property intrinsic capability
|
||||
Schema
|
||||
read / write
|
||||
read-only optimization
|
||||
synchronization topology
|
||||
lock slot
|
||||
mutex storage
|
||||
guard
|
||||
runtime property access
|
||||
constraint / generic attribute infrastructure
|
||||
```
|
||||
|
||||
这些能力与 HTTP、JSON、AMIS、数据库、GUI 没有关系。
|
||||
|
||||
例如一个 Property 被声明为 intrinsic read-only,Structive 可以直接利用这个事实:
|
||||
|
||||
```text
|
||||
read_only
|
||||
↓
|
||||
managed write 不存在
|
||||
↓
|
||||
不进入 writable synchronization topology
|
||||
↓
|
||||
不贡献 lock slot
|
||||
↓
|
||||
不创建对应 mutex
|
||||
↓
|
||||
managed read 走零锁路径
|
||||
```
|
||||
|
||||
这类优化属于结构本身,因此应由 Structive 完成。
|
||||
|
||||
### 3.2 Adminive 负责什么
|
||||
|
||||
Adminive 负责后台管理领域信息和适配:
|
||||
|
||||
```text
|
||||
字段名称与显示名称
|
||||
editable / creatable
|
||||
sensitive
|
||||
required
|
||||
widget
|
||||
visible / visible_on
|
||||
list column metadata
|
||||
排序信息
|
||||
JSON 描述协议
|
||||
AMIS schema
|
||||
HTTP resource semantics
|
||||
frontend patch
|
||||
Object_Adapter
|
||||
Resource_Transaction
|
||||
```
|
||||
|
||||
这些信息表达的是“后台系统怎样消费一个结构”,而不是改变 C++ Property 的基本定义。
|
||||
|
||||
### 3.3 不允许再次出现第二套 Property Core
|
||||
|
||||
Adminive 不应该重新拥有:
|
||||
|
||||
```text
|
||||
另一套 Property Descriptor
|
||||
另一套 per-field mutex tree
|
||||
另一套 lock slot
|
||||
另一套 managed access mode
|
||||
另一套 runtime type erasure core
|
||||
```
|
||||
|
||||
如果某个需求本质上属于 Property/Schema/synchronization,应优先落到 Structive;如果它属于后台管理领域,则留在 Adminive。
|
||||
|
||||
这种分层避免两个库同时维护相似概念并逐渐产生不同语义。
|
||||
|
||||
## 4. “结构事实”与“后台元数据”必须分开
|
||||
|
||||
Adminive 字段同时包含两类信息,但这两类信息不能混为一谈。
|
||||
|
||||
第一类是结构事实:
|
||||
|
||||
```text
|
||||
这个字段在 managed model 中是否 writable
|
||||
这个字段是否参与 synchronization
|
||||
字段值的真实 C++ 类型
|
||||
字段 accessor
|
||||
```
|
||||
|
||||
第二类是后台领域元数据:
|
||||
|
||||
```text
|
||||
是否允许前端编辑
|
||||
是否允许创建时输入
|
||||
是否在列表中展示
|
||||
是否敏感
|
||||
使用什么 widget
|
||||
显示什么 label
|
||||
```
|
||||
|
||||
例如:
|
||||
|
||||
```cpp
|
||||
ADMINIVE_FIELD(Config, backend_value).read_write()
|
||||
```
|
||||
|
||||
表示 `backend_value` 在 Adminive managed model 中可以被修改,但并没有因此自动成为前端可编辑字段。
|
||||
|
||||
而:
|
||||
|
||||
```cpp
|
||||
ADMINIVE_FIELD(Config, frontend_value).editable()
|
||||
```
|
||||
|
||||
既使它成为 managed writable,也附加“前端 update 可以编辑”的领域语义。
|
||||
|
||||
因此:
|
||||
|
||||
```text
|
||||
managed writable
|
||||
≠ frontend editable
|
||||
```
|
||||
|
||||
这条边界必须长期保持。
|
||||
|
||||
## 5. 默认只读,显式声明可写
|
||||
|
||||
Adminive 字段默认是 intrinsic read-only。
|
||||
|
||||
这是刻意选择,而不是缺省值碰巧如此。
|
||||
|
||||
理由有三点:
|
||||
|
||||
### 5.1 安全的默认语义
|
||||
|
||||
后台描述中出现一个字段,并不意味着 HTTP/GUI 更新应该天然拥有修改它的能力。
|
||||
|
||||
新增字段时默认只读,可以避免因为“忘记配置”而无意进入 managed mutation path。
|
||||
|
||||
### 5.2 让 metadata 真正参与性能优化
|
||||
|
||||
只读信息会一路传递到 Structive,同步系统因此可以跳过无意义的锁资源。
|
||||
|
||||
这比“所有字段都建 mutex,只是在 write 时检查权限”更符合结构描述系统的价值。
|
||||
|
||||
### 5.3 writable 是结构承诺
|
||||
|
||||
一旦调用:
|
||||
|
||||
```cpp
|
||||
.editable()
|
||||
.creatable()
|
||||
.read_write()
|
||||
```
|
||||
|
||||
就意味着 Adminive 正式承认这个字段存在 managed mutation path。
|
||||
|
||||
同步、事务和适配器可以据此建立正确行为。
|
||||
|
||||
## 6. 四种字段声明语义
|
||||
|
||||
### 6.1 普通字段
|
||||
|
||||
```cpp
|
||||
ADMINIVE_FIELD(Config, id)
|
||||
```
|
||||
|
||||
语义:
|
||||
|
||||
```text
|
||||
intrinsic read-only
|
||||
不产生 managed write
|
||||
不为自身贡献 Structive mutex
|
||||
```
|
||||
|
||||
它仍然是普通 C++ 成员。直接修改 raw member 属于 raw C++ path,不属于 Adminive/Structive managed contract。
|
||||
|
||||
### 6.2 `.editable()`
|
||||
|
||||
```cpp
|
||||
ADMINIVE_FIELD(Config, worker_count).editable()
|
||||
```
|
||||
|
||||
语义:
|
||||
|
||||
```text
|
||||
intrinsic read-write
|
||||
frontend update editable
|
||||
默认参加 Structive synchronization
|
||||
```
|
||||
|
||||
这是后台表单最常见的可编辑字段。
|
||||
|
||||
### 6.3 `.creatable()`
|
||||
|
||||
```cpp
|
||||
ADMINIVE_FIELD(Device, address).creatable()
|
||||
```
|
||||
|
||||
语义:
|
||||
|
||||
```text
|
||||
intrinsic read-write
|
||||
允许 create input
|
||||
默认参加 Structive synchronization
|
||||
```
|
||||
|
||||
它表达创建流程的领域能力,不等同于普通 update editable。
|
||||
|
||||
### 6.4 `.read_write()`
|
||||
|
||||
```cpp
|
||||
ADMINIVE_FIELD(Config, backend_state).read_write()
|
||||
```
|
||||
|
||||
语义:
|
||||
|
||||
```text
|
||||
intrinsic read-write
|
||||
不因为这一声明自动开放 frontend edit
|
||||
默认参加 Structive synchronization
|
||||
```
|
||||
|
||||
适合配置加载、后台代码、服务内部更新等受控路径。
|
||||
|
||||
### 6.5 `.unsynchronized()`
|
||||
|
||||
```cpp
|
||||
ADMINIVE_FIELD(Config, atomic_counter).read_write().unsynchronized()
|
||||
```
|
||||
|
||||
语义:
|
||||
|
||||
```text
|
||||
intrinsic writable
|
||||
不为该路径创建 Structive mutex
|
||||
```
|
||||
|
||||
只有以下情况适合使用:
|
||||
|
||||
```text
|
||||
字段自身已经是原子类型
|
||||
更高层已经提供同步
|
||||
对象生命周期保证单线程
|
||||
或业务明确接受无同步访问
|
||||
```
|
||||
|
||||
`.unsynchronized()` 不是性能开关,更不是“我觉得这里应该没事”。它是对同步责任的显式转移。
|
||||
|
||||
## 7. Adminive 不做访问控制系统
|
||||
|
||||
Adminive 中的 `editable`、`readable`、`sensitive` 等信息服务于具体后台适配语义,但它们不构成一个通用安全边界。
|
||||
|
||||
Structive 更不会定义:
|
||||
|
||||
```text
|
||||
internal
|
||||
external
|
||||
persistence
|
||||
RPC
|
||||
GUI
|
||||
admin
|
||||
user
|
||||
```
|
||||
|
||||
这类访问角色。
|
||||
|
||||
原因是不同消费者拥有不同边界:
|
||||
|
||||
```text
|
||||
GUI
|
||||
HTTP API
|
||||
RPC
|
||||
plugin
|
||||
script
|
||||
debug tooling
|
||||
persistence
|
||||
```
|
||||
|
||||
是否向某个消费者暴露字段,应由那个消费者或更高层业务策略决定。
|
||||
|
||||
因此 Adminive 的设计原则是:
|
||||
|
||||
> Property 描述自身能做什么;外部系统决定自己允许做什么。
|
||||
|
||||
`sensitive()` 可以指导 Adminive JSON/HTTP 等适配器避免泄漏敏感内容,但不应被误解为 C++ 内存级权限机制。
|
||||
|
||||
## 8. Managed path 与 raw C++ path
|
||||
|
||||
Adminive 不试图取代普通 C++ 对象。
|
||||
|
||||
例如:
|
||||
|
||||
```cpp
|
||||
config.worker_count = 8;
|
||||
```
|
||||
|
||||
如果成员本身允许直接访问,这仍然是合法 C++。
|
||||
|
||||
而:
|
||||
|
||||
```cpp
|
||||
managed.member<&Config::worker_count>().write([](auto& value) {
|
||||
value = 8;
|
||||
});
|
||||
```
|
||||
|
||||
属于 managed path。
|
||||
|
||||
managed path 才承诺:
|
||||
|
||||
```text
|
||||
遵守 intrinsic writable
|
||||
遵守 Structive synchronization
|
||||
维持回调生命周期边界
|
||||
与 Resource_Service 的事务流程协作
|
||||
```
|
||||
|
||||
直接绕过 managed API 修改 raw member,也同时绕过这些保证。
|
||||
|
||||
这符合 Adminive/Structive 的共同原则:
|
||||
|
||||
> 增强原生 C++ 结构,而不是建立一堵无法绕过的对象墙。
|
||||
|
||||
## 9. 为什么 Managed 回调不能泄漏引用
|
||||
|
||||
Managed read/write 的锁只在回调作用域中有效。
|
||||
|
||||
如果允许:
|
||||
|
||||
```cpp
|
||||
const auto& value = field.read([](const auto& current) -> const auto& {
|
||||
return current;
|
||||
});
|
||||
```
|
||||
|
||||
那么引用在返回后仍存在,但锁已经释放,同步契约就失效了。
|
||||
|
||||
因此 Adminive 在编译期拒绝这些回调结果:
|
||||
|
||||
```text
|
||||
reference
|
||||
pointer
|
||||
reference_wrapper
|
||||
ranges view
|
||||
```
|
||||
|
||||
需要把结果带出 managed scope 时,应:
|
||||
|
||||
```text
|
||||
返回值副本
|
||||
或使用 snapshot()
|
||||
```
|
||||
|
||||
这不是 API 限制,而是锁生命周期本身决定的安全边界。
|
||||
|
||||
## 10. 同步模型:只保护 mutable consistency
|
||||
|
||||
Structive synchronization 只解决一个问题:
|
||||
|
||||
> 当前进程内,对可变对象状态进行 managed access 时如何保持一致性。
|
||||
|
||||
它不负责:
|
||||
|
||||
```text
|
||||
HTTP 权限
|
||||
数据库事务
|
||||
配置文件原子替换
|
||||
业务 validator
|
||||
外部设备写入
|
||||
前端确认流程
|
||||
```
|
||||
|
||||
默认情况下,可写属性可以拥有独立同步域,以保留并发性。
|
||||
|
||||
当一个具体对象实例确实要求跨多个属性的一致快照时,可以使用 shared topology,例如 `Config_Store` 对完整根配置的持久化场景。
|
||||
|
||||
这种选择应该由一致性需求驱动,而不是为了“简单”把所有对象默认放到一把全局锁下。
|
||||
|
||||
## 11. 嵌套结构的同步原则
|
||||
|
||||
嵌套结构不能简单采用“父对象只要有一个 writable 子字段,所有后代访问都锁父对象”的粗粒度规则。
|
||||
|
||||
Adminive/Structive 应尽量利用真实 intrinsic information:
|
||||
|
||||
```text
|
||||
只读叶子
|
||||
→ 不应因为 writable sibling 而产生无意义锁
|
||||
|
||||
unsynchronized writable leaf
|
||||
→ 不应因为同级字段可写而自动获取无关父锁
|
||||
|
||||
可整体替换的 writable ancestor
|
||||
→ ancestor 的一致性域覆盖其子树
|
||||
```
|
||||
|
||||
最后一条很重要:如果祖先字段本身允许整体赋值,那么对子树任意成员的访问都可能与祖先整体替换发生竞争,此时祖先同步域自然必须参与。
|
||||
|
||||
同步边界来自真实 mutation possibility,而不是单纯来自对象树层级。
|
||||
|
||||
## 12. Resource_Service 的职责
|
||||
|
||||
`Resource_Service` 是 Adminive 的应用事务编排层,不是新的 Property Core。
|
||||
|
||||
一次 managed update 可以包含:
|
||||
|
||||
```text
|
||||
取得一致性快照
|
||||
应用 frontend patch
|
||||
校验候选对象
|
||||
prepare external state
|
||||
提交 runtime model
|
||||
commit external side effect
|
||||
失败时恢复
|
||||
生成最终 response snapshot
|
||||
```
|
||||
|
||||
当绑定 `Managed_Value` / `Managed_Field` 时,对象一致性由 Structive synchronization 保证。
|
||||
|
||||
`Resource_Service` 不应该再创建一棵重复的 per-field mutex 系统。
|
||||
|
||||
## 13. 三种“事务”不能混为一谈
|
||||
|
||||
Adminive 中至少存在三类不同一致性问题:
|
||||
|
||||
### 13.1 内存对象同步
|
||||
|
||||
由 Structive synchronization 负责。
|
||||
|
||||
```text
|
||||
shared/unique lock
|
||||
lock slot
|
||||
synchronization topology
|
||||
```
|
||||
|
||||
### 13.2 Adminive 外部副作用事务
|
||||
|
||||
由 `Resource_Transaction<Model>` 负责。
|
||||
|
||||
```text
|
||||
prepare
|
||||
commit
|
||||
rollback
|
||||
```
|
||||
|
||||
它可以代表配置文件、设备、远端服务等外部动作。
|
||||
|
||||
### 13.3 数据库事务
|
||||
|
||||
由数据库自身负责。
|
||||
|
||||
数据库 transaction 不能替代进程内对象锁;对象 mutex 也不能替代数据库 ACID。
|
||||
|
||||
三者可以在一次业务流程中协作,但职责必须保持独立。
|
||||
|
||||
## 14. Adapter First
|
||||
|
||||
Adminive Core 不应直接绑定具体第三方库。
|
||||
|
||||
当前设计通过 Adapter 连接外部生态:
|
||||
|
||||
```text
|
||||
Json_Adapter<Json>
|
||||
Value_Adapter<T, Json>
|
||||
Enum_Adapter<Enum>
|
||||
Reflection_Adapter<T>
|
||||
Object_Adapter<T>
|
||||
Control_Adapter<T, Json>
|
||||
Polymorphic_Adapter<T, Json>
|
||||
HTTP adapter
|
||||
```
|
||||
|
||||
这条原则有几个直接收益:
|
||||
|
||||
1. 核心协议不因为换 JSON 库而变化。
|
||||
2. 枚举反射和结构体反射可以独立替换。
|
||||
3. Runtime object 不必为了框架继承某个基类。
|
||||
4. 第三方依赖停留在桥接层,不污染业务类型。
|
||||
5. 可以针对单个类型提供精确适配,而不是扩大 Core 特例。
|
||||
|
||||
如果未来接入新的 JSON/HTTP/Reflection 实现,应优先增加 Adapter,而不是修改 Adminive 的中心算法。
|
||||
|
||||
## 15. Object_Adapter:运行时对象与配置模型分离
|
||||
|
||||
不是所有运行时对象都适合作为可复制配置对象。
|
||||
|
||||
例如对象可能包含:
|
||||
|
||||
```text
|
||||
atomic
|
||||
mutex
|
||||
socket
|
||||
thread
|
||||
runtime cache
|
||||
file handle
|
||||
```
|
||||
|
||||
`Object_Adapter<T>` 的意义是建立:
|
||||
|
||||
```text
|
||||
Runtime Object
|
||||
⇅
|
||||
Pure Model
|
||||
```
|
||||
|
||||
Adminive 通过 model 完成:
|
||||
|
||||
```text
|
||||
JSON
|
||||
validation
|
||||
patch
|
||||
transaction candidate
|
||||
frontend schema
|
||||
```
|
||||
|
||||
然后由 `commit()` 将结果应用回真正的 runtime object。
|
||||
|
||||
这比要求所有业务类型变成“为了后台框架而设计的 DTO”更符合低侵入原则。
|
||||
|
||||
## 16. Frontend 应保持被动
|
||||
|
||||
Adminive 是 backend-driven 系统,因此前端不应该重新维护:
|
||||
|
||||
```text
|
||||
字段名称
|
||||
业务枚举
|
||||
显示条件
|
||||
校验规则
|
||||
CRUD schema
|
||||
配置树结构
|
||||
```
|
||||
|
||||
前端的职责是:
|
||||
|
||||
```text
|
||||
获取后端描述
|
||||
渲染页面
|
||||
提交用户输入
|
||||
展示后端错误
|
||||
```
|
||||
|
||||
如果一个业务字段修改后必须同时修改 C++ descriptor 和 TypeScript hard-coded schema,说明 backend-driven 边界已经被破坏。
|
||||
|
||||
前端可以拥有纯 UI 行为,但不应该成为第二份业务 Schema。
|
||||
|
||||
## 17. 编译期与运行期的分工
|
||||
|
||||
Adminive 建议遵守以下规则:
|
||||
|
||||
```text
|
||||
编译期已知
|
||||
→ template / concept / requires / descriptor type
|
||||
|
||||
运行期才知道
|
||||
→ JSON key / HTTP input / runtime resource / dynamic value
|
||||
```
|
||||
|
||||
例如 managed writability 是 descriptor type 的静态事实,不应该等一次 HTTP 请求进入以后才通过字符串判断它到底是不是 writable。
|
||||
|
||||
而 JSON 字段是否存在、用户输入是否合法,则天然只能在运行时处理。
|
||||
|
||||
这可以同时获得:
|
||||
|
||||
```text
|
||||
更早的错误
|
||||
更清晰的 API
|
||||
更少的运行时分支
|
||||
更好的优化机会
|
||||
```
|
||||
|
||||
## 18. Metadata 必须具有真实语义
|
||||
|
||||
对 Adminive 来说,metadata 不应该只是“最终转成 JSON 的标签集合”。
|
||||
|
||||
高质量 metadata 应至少满足一个条件:
|
||||
|
||||
> 它能被某个明确的系统消费,并改变真实行为。
|
||||
|
||||
例如:
|
||||
|
||||
```text
|
||||
read_only
|
||||
→ 改变 Structive capability 和同步成本
|
||||
|
||||
editable
|
||||
→ 改变 update schema 和 managed mutability
|
||||
|
||||
sensitive
|
||||
→ 改变序列化/默认值暴露行为
|
||||
|
||||
required
|
||||
→ 改变输入验证
|
||||
|
||||
widget
|
||||
→ 改变前端控件
|
||||
```
|
||||
|
||||
如果新增 metadata 没有明确消费者,应谨慎加入 Core descriptor,避免逐渐变成无边界的“万能标签袋”。
|
||||
|
||||
## 19. Core 的非目标
|
||||
|
||||
Adminive Core 不应该演化成:
|
||||
|
||||
```text
|
||||
通用 ORM
|
||||
通用 RPC framework
|
||||
权限系统
|
||||
数据库 abstraction
|
||||
GUI framework
|
||||
全功能 serialization library
|
||||
Structive 的替代品
|
||||
全局 transaction manager
|
||||
```
|
||||
|
||||
这些领域可以通过 Adapter 或上层模块与 Adminive 协作,但不应因为某个项目需要就全部进入核心协议。
|
||||
|
||||
保持非目标清晰,是长期保持 API 简洁的重要手段。
|
||||
|
||||
## 20. 新功能应该放在哪里
|
||||
|
||||
新增能力时,可以按下面的判断顺序决定归属。
|
||||
|
||||
### 20.1 它描述 Property 本身吗?
|
||||
|
||||
例如:
|
||||
|
||||
```text
|
||||
intrinsic readable/writable
|
||||
synchronization topology
|
||||
generic constraint
|
||||
```
|
||||
|
||||
优先考虑 Structive。
|
||||
|
||||
### 20.2 它描述后台管理领域吗?
|
||||
|
||||
例如:
|
||||
|
||||
```text
|
||||
editable
|
||||
widget
|
||||
list column
|
||||
AMIS schema
|
||||
frontend visible condition
|
||||
```
|
||||
|
||||
属于 Adminive。
|
||||
|
||||
### 20.3 它绑定具体第三方库吗?
|
||||
|
||||
例如:
|
||||
|
||||
```text
|
||||
nlohmann::json
|
||||
Drogon
|
||||
cpp-httplib
|
||||
magic_enum
|
||||
Boost.PFR
|
||||
```
|
||||
|
||||
放 Adapter/bridge 层。
|
||||
|
||||
### 20.4 它属于具体业务项目吗?
|
||||
|
||||
例如:
|
||||
|
||||
```text
|
||||
某设备必须先断电才能修改配置
|
||||
某用户角色才能执行操作
|
||||
某数据库表的迁移规则
|
||||
```
|
||||
|
||||
留在业务层,不进入 Adminive Core。
|
||||
|
||||
## 21. API 演进原则
|
||||
|
||||
后续修改 Adminive API 时建议遵守:
|
||||
|
||||
1. 不破坏“Structive 管结构,Adminive 管后台领域”的依赖方向。
|
||||
2. 不重新引入重复同步系统。
|
||||
3. 不把 frontend permission 当成 Property intrinsic capability。
|
||||
4. 静态事实使用类型系统表达,不退化成运行时 bool 判断。
|
||||
5. Runtime adapter 保持动态,但不要把动态复杂度传播到普通 typed API。
|
||||
6. 一个概念只保留一套主要表达方式,避免 `_key`、mode、view 等重复表面接口无边界增长。
|
||||
7. 不为了少几个函数制造难以理解的模板 DSL。
|
||||
8. metadata 必须有清晰消费者。
|
||||
9. 高级能力允许高级 API,但应与普通使用路径分层。
|
||||
10. 对行为语义的修改必须同时补契约测试和文档。
|
||||
|
||||
## 22. 测试原则
|
||||
|
||||
Adminive 的测试不应只验证“JSON 长得对”。
|
||||
|
||||
至少应该覆盖四类契约:
|
||||
|
||||
### 22.1 Descriptor contract
|
||||
|
||||
```text
|
||||
字段 metadata
|
||||
managed writable
|
||||
synchronized
|
||||
nested descriptor
|
||||
schema validation
|
||||
```
|
||||
|
||||
### 22.2 Managed synchronization contract
|
||||
|
||||
```text
|
||||
read-only zero-lock
|
||||
writable locking
|
||||
unsynchronized path
|
||||
nested lock propagation
|
||||
callback lifetime
|
||||
multi-thread blocking relationship
|
||||
```
|
||||
|
||||
### 22.3 Adapter contract
|
||||
|
||||
```text
|
||||
JSON encode/decode
|
||||
Enum Adapter
|
||||
Reflection Adapter
|
||||
Object Adapter
|
||||
HTTP Adapter
|
||||
```
|
||||
|
||||
### 22.4 Transaction contract
|
||||
|
||||
```text
|
||||
prepare failure
|
||||
runtime commit failure
|
||||
external commit failure
|
||||
rollback
|
||||
snapshot consistency
|
||||
Config Store persistence
|
||||
```
|
||||
|
||||
这些测试保护的是设计边界,而不仅是当前实现细节。
|
||||
|
||||
## 23. 设计判断清单
|
||||
|
||||
在提交一个新设计前,可以快速检查:
|
||||
|
||||
```text
|
||||
[ ] 这是 Structive 已经解决的问题吗?
|
||||
[ ] 这是 Adminive 后台领域真正需要的概念吗?
|
||||
[ ] 它是否把第三方库耦合进 Core?
|
||||
[ ] 它是否让 metadata 真正改变行为?
|
||||
[ ] 它是否把访问控制错误塞进 Property Core?
|
||||
[ ] 它是否创建了第二套同步或事务机制?
|
||||
[ ] 静态事实是否能在编译期表达?
|
||||
[ ] 普通用户是否必须理解这个高级机制?
|
||||
[ ] 是否存在更合适的 Adapter 层?
|
||||
[ ] 是否补充了对应契约测试和文档?
|
||||
```
|
||||
|
||||
如果多数问题无法明确回答,通常意味着抽象边界还没有收敛。
|
||||
|
||||
## 24. 最终架构图
|
||||
|
||||
```text
|
||||
C++ Business Types
|
||||
│
|
||||
▼
|
||||
Adminive Descriptor
|
||||
│
|
||||
┌─────────────────┴─────────────────┐
|
||||
│ │
|
||||
▼ ▼
|
||||
Adminive domain metadata Structive schema
|
||||
editable/widget/sensitive/... capability/synchronization
|
||||
│ │
|
||||
└─────────────────┬─────────────────┘
|
||||
▼
|
||||
Managed object view
|
||||
│
|
||||
┌─────────────────────┼─────────────────────┐
|
||||
▼ ▼ ▼
|
||||
JSON Adapter Resource Service AMIS Schema
|
||||
│ │ │
|
||||
▼ ▼ ▼
|
||||
serialization HTTP transaction Frontend
|
||||
│
|
||||
▼
|
||||
Resource_Transaction
|
||||
prepare/commit/rollback
|
||||
```
|
||||
|
||||
最重要的依赖方向始终是:
|
||||
|
||||
```text
|
||||
Structive
|
||||
↑
|
||||
Adminive Core
|
||||
↑
|
||||
Adapters / Service
|
||||
↑
|
||||
Application
|
||||
```
|
||||
|
||||
底层不知道上层领域,上层消费底层能力。这是 Adminive 后续演进时最需要守住的架构边界。
|
||||
Reference in New Issue
Block a user