Files
Structive/docs/EXTENSIONS.md
T
2026-08-07 16:21:44 +08:00

268 lines
7.5 KiB
Markdown

# Structive Extension Architecture
[中文](EXTENSIONS.zh-CN.md)
## 1. Extension role
Structive extensions add domain-specific interpretation without changing the Core structural model.
The intended dependency is:
```text
application / adapter
Structive extension
Structive Property Core
```
Core does not include or link extension code.
## 2. Extension principle
An extension should normally add two things:
1. one or more Attribute categories that express domain metadata;
2. interpretation code that consumes those Attributes.
It should not create a parallel property registry when the existing `Object_Schema` already contains the required structure.
## 3. Current presentation extension
The current extension module defines four Attribute categories:
```cpp
presentation::Label_Category
presentation::Description_Category
presentation::Group_Category
presentation::Order_Category
```
Convenience Attribute values:
```cpp
presentation::label<"Temperature">
presentation::description<"Current device temperature">
presentation::group<"Environment">
presentation::order<10>
```
They can be attached directly to a normal Core property:
```cpp
field<&Device::temperature>(
key<"temperature">,
unit<"C">,
presentation::label<"Temperature">,
presentation::description<"Current device temperature">,
presentation::group<"Environment">,
presentation::order<10>
)
```
No wrapper such as `hint(...)` is required.
## 4. Presentation interpretation
Include:
```cpp
#include <structive/property/extensions/presentation.hpp>
```
Link:
```cmake
target_link_libraries(my_target PRIVATE structive::property_extensions)
```
Describe a property by member pointer:
```cpp
auto info = presentation::describe<&Device::temperature>(device.schema());
```
The result contains:
```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;
};
```
If no presentation label is declared, the extension uses the property key as the display label. This fallback is presentation policy and intentionally lives outside Core.
## 5. Inheritable extension Attributes
The presentation `group` Attribute is inheritable, so it may be placed in `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">
)
);
```
The extension resolves an effective declared value through the same Core default mechanism.
`label`, `description` and `order` are not inheritable and therefore cannot be placed in `defaults(...)`.
## 6. Designing a new extension Attribute
Example:
```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{};
}
```
Use it in a schema:
```cpp
field<&Device::temperature>(
key<"temperature">,
my_adapter::json_name<"temp">
)
```
The Core stores it without needing to know what JSON means.
## 7. Reading extension Attributes
For a 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>();
}
```
For inheritable categories, use 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);
}
```
This checks the property declaration first and then object defaults.
## 8. Multi-valued metadata
Core uniqueness is category-driven. An Attribute with `single_valued = true` and a non-void `attribute_category` may only appear once in one declaration.
When a domain needs repeated annotations, design that metadata so it does not claim single-valued category uniqueness, then consume it through `for_each_attribute(...)`.
Do not force naturally repeated metadata into one large unrelated object merely to satisfy a single-valued design.
## 9. Recommended extension boundaries
A future extension may reasonably own metadata and interpretation for areas such as:
- serialization naming and omission rules;
- RPC exposure rules;
- UI labels, groups and editor hints;
- database column mapping;
- configuration-file mapping;
- domain documentation generation.
These are examples of extension domains, not currently implemented features.
The important boundary is that Core should not gain a direct dependency on their libraries or domain types.
## 10. Core metadata may still be consumed
An extension is allowed to interpret Core-owned categories when those categories are part of the extension's input contract.
For example, an external adapter may use:
- property `key` as the default protocol name;
- `External_Access` to determine exposure;
- `sensitive` to decide whether its own logs should omit a value.
The extension may choose a policy based on those attributes, but it should not change what Core itself means by them.
## 11. Fallback behavior belongs to the consumer
The presentation extension demonstrates the intended rule:
```text
no label Attribute
presentation extension chooses property key as label
```
Core does not invent that fallback because a different consumer may want a different behavior.
The same principle should apply to future adapters. Defaults that exist only to make one domain pleasant should remain in that domain.
## 12. Avoid extension-to-Core semantic leakage
Bad direction:
```text
JSON adapter needs alias support
Core adds JSON_Alias_Category and JSON naming logic
```
Preferred direction:
```text
JSON extension defines Json_Name_Category
JSON extension interprets it
Core remains domain-neutral
```
## 13. Linked code versus header-only metadata
Attribute definitions can often be header-only. Interpretation may be header-only or linked depending on implementation needs.
The current presentation extension demonstrates a linked extension target. `presentation::describe()` performs compile-time Attribute selection and calls linked `make_presentation_info()` for the concrete result construction/fallback step.
Do not move linked implementation into Core merely because the extension is small.
## 14. Extension review checklist
Before accepting a new extension feature, ask:
1. Does this concept belong to Core or to one consumer domain?
2. Can it be represented through the existing Attribute protocol?
3. Does the extension own the Attribute category it interprets?
4. Is an inheritable Attribute truly an object-wide default policy?
5. Is fallback behavior kept inside the extension?
6. Is the existing schema reused rather than duplicated?
7. Does the dependency still point from extension to Core?
8. Has Core remained free of third-party domain types?
9. Does the extension preserve the existing meaning of Core access, validation and synchronization?
If the answer to the dependency or semantic-boundary questions is no, the layering should be redesigned before code is added.