首次提交

This commit is contained in:
2026-08-07 16:21:44 +08:00
parent 396311246c
commit 1e903dfe1e
33 changed files with 5960 additions and 1 deletions
+267
View File
@@ -0,0 +1,267 @@
# Structive Extension 架构指南
[English](EXTENSIONS.md)
## 1. Extension 的职责
Structive Extension 的作用是在不修改 Core 结构模型的前提下增加领域语义解释。
推荐依赖方向:
```text
application / adapter
Structive extension
Structive Property Core
```
Core 不 include、不 link Extension。
## 2. Extension 的基本模式
一个 Extension 通常只需要增加两类东西:
1. 一个或多个属于自己领域的 Attribute category
2. 解释这些 Attribute 的实现代码。
如果已有 `Object_Schema` 已经包含需要的结构信息,就不应该再复制第二份 property registry。
## 3. 当前 Presentation Extension
当前扩展模块定义四个 Attribute category
```cpp
presentation::Label_Category
presentation::Description_Category
presentation::Group_Category
presentation::Order_Category
```
对应便利 Attribute
```cpp
presentation::label<"Temperature">
presentation::description<"Current device temperature">
presentation::group<"Environment">
presentation::order<10>
```
它们直接挂到普通 Core property 上:
```cpp
field<&Device::temperature>(
key<"temperature">,
unit<"C">,
presentation::label<"Temperature">,
presentation::description<"Current device temperature">,
presentation::group<"Environment">,
presentation::order<10>
)
```
不需要 `hint(...)` 一类额外包装。
## 4. Presentation 解释
Include
```cpp
#include <structive/property/extensions/presentation.hpp>
```
Link
```cmake
target_link_libraries(my_target PRIVATE structive::property_extensions)
```
通过成员指针描述 property
```cpp
auto info = presentation::describe<&Device::temperature>(device.schema());
```
返回:
```cpp
struct Presentation_Info {
std::string_view key;
std::string_view label;
std::string_view description;
std::string_view group;
std::size_t order;
bool has_order;
};
```
没有声明 presentation label 时,Presentation Extension 使用 property key 作为显示 label。这个 fallback 是 Presentation 自己的政策,不属于 Core。
## 5. 可继承 Extension Attribute
`presentation::group` 是 inheritable,因此可以放入 `defaults(...)`
```cpp
return object<Device>(
defaults(
external_access<External_Access::read>,
presentation::group<"Environment">
),
field<&Device::temperature>(
key<"temperature">,
presentation::label<"Temperature">
),
field<&Device::pressure>(
key<"pressure">,
presentation::label<"Pressure">
)
);
```
Extension 通过 Core 同一套 default 机制解析有效声明。
`label``description``order` 不是 inheritable,因此不能放进 `defaults(...)`
## 6. 设计一个新的 Extension Attribute
例如:
```cpp
namespace my_adapter {
struct Json_Name_Category {};
template <Fixed_String Value>
struct Json_Name_Attribute {
using attribute_category = Json_Name_Category;
static constexpr bool single_valued = true;
static constexpr bool inheritable = false;
static constexpr auto value = Value;
};
template <Fixed_String Value>
inline constexpr Json_Name_Attribute<Value> json_name{};
}
```
在 Schema 中直接使用:
```cpp
field<&Device::temperature>(
key<"temperature">,
my_adapter::json_name<"temp">
)
```
Core 负责存储它,但完全不需要知道 JSON 是什么。
## 7. 读取 Extension Attribute
单个 property descriptor
```cpp
using Property = std::remove_cvref_t<decltype(property)>;
if constexpr (Property::has_attribute<my_adapter::Json_Name_Category>) {
const auto& value = property.attribute<my_adapter::Json_Name_Category>();
}
```
可继承 category 使用 effective declared lookup
```cpp
if constexpr (has_declared_effective_attribute_v<Schema, Index, My_Category>) {
const auto& value = declared_effective_attribute<Index, My_Category>(schema);
}
```
它先检查 property declaration,再检查 object defaults。
## 8. Multi-Valued Metadata
Core 的唯一性由 category 协议决定。拥有非 void `attribute_category``single_valued = true` 的 Attribute,在同一个声明中只能出现一次。
如果某个领域天然需要重复 annotation,就不要错误地宣称它是 single-valued category;可以通过 `for_each_attribute(...)` 遍历并消费多项数据。
不要为了满足 single-valued 设计,把本来可以重复的独立元数据硬塞进一个巨大对象。
## 9. 合理的未来 Extension 边界
未来 Extension 可以合理拥有例如:
- serialization name / omission policy
- RPC exposure rule
- UI label / group / editor hint
- database column mapping
- configuration-file mapping
- domain documentation generation。
这些只是合理领域示例,不代表当前已经实现。
关键边界是 Core 不能因此直接依赖这些领域库或类型。
## 10. Extension 可以消费 Core Metadata
Extension 可以读取 Core-owned category,只要这些 category 本来就是它的输入契约。
例如 External Adapter 可以使用:
- property `key` 作为默认协议名字;
- `External_Access` 判断是否暴露;
- `sensitive` 决定自己的日志是否隐藏值。
Extension 可以基于这些信息建立自己领域的政策,但不能改变 Core 对这些 Attribute 的基础含义。
## 11. Fallback 行为属于 Consumer
Presentation Extension 当前就是示范:
```text
没有 label Attribute
Presentation Extension 使用 property key 作为 label
```
Core 不应该发明这个 fallback,因为另一个 consumer 完全可能需要另一种策略。
未来 Adapter 也应该遵守这个原则。只为了某个领域“好用”的默认逻辑应该留在那个领域内部。
## 12. 防止 Extension 语义泄漏进 Core
错误方向:
```text
JSON Adapter 需要 alias
Core 增加 JSON_Alias_Category 和 JSON 命名逻辑
```
正确方向:
```text
JSON Extension 定义 Json_Name_Category
JSON Extension 自己解释
Core 保持领域中立
```
## 13. Header-Only Metadata 与 Linked Implementation
Attribute 定义通常可以 header-only。具体解释逻辑可以根据实现需要选择 header-only 或 linked。
当前 Presentation Extension 使用 linked target`presentation::describe()` 在模板层完成 Attribute 选择,然后调用链接实现 `make_presentation_info()` 生成最终结果并处理 fallback。
不能因为扩展代码量小,就把它的 linked implementation 搬进 Core。
## 14. Extension 审核清单
新增 Extension 功能前检查:
1. 这个概念属于 Core,还是只属于某个 consumer domain
2. 能否直接使用现有 Attribute 协议表达?
3. Extension 是否拥有自己解释的 category?
4. Inheritable Attribute 是否真的属于对象级默认政策?
5. Fallback 是否保留在 Extension 内?
6. 是否复用已有 Schema,而不是复制结构树?
7. 依赖是否仍然从 Extension 指向 Core
8. Core 是否仍然没有第三方领域类型?
9. Extension 是否保持了 Core access、validation、synchronization 的原有语义?
如果依赖方向或语义边界不成立,应先重新设计分层,再写代码。