首次提交
This commit is contained in:
@@ -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 的原有语义?
|
||||
|
||||
如果依赖方向或语义边界不成立,应先重新设计分层,再写代码。
|
||||
Reference in New Issue
Block a user