273 lines
6.6 KiB
Markdown
273 lines
6.6 KiB
Markdown
# Structive Extension Architecture
|
|
|
|
[中文](EXTENSIONS.zh-CN.md)
|
|
|
|
## 1. Extension role
|
|
|
|
Extensions add domain metadata and domain interpretation without making Property Core depend on that domain.
|
|
|
|
The dependency direction is:
|
|
|
|
```text
|
|
Extension → Property Core
|
|
Property Core -X→ Extension
|
|
```
|
|
|
|
Core supplies:
|
|
|
|
- schema and property descriptors;
|
|
- intrinsic readable/writable capability;
|
|
- the Attribute protocol;
|
|
- constraints;
|
|
- synchronization metadata and managed access;
|
|
- runtime structural access.
|
|
|
|
Extensions add their own categories and interpretation.
|
|
|
|
## 2. Extension principle
|
|
|
|
An extension should own only its domain semantics.
|
|
|
|
For example, Presentation owns:
|
|
|
|
```text
|
|
label
|
|
description
|
|
group
|
|
order
|
|
```
|
|
|
|
It does not need Property Core to understand presentation.
|
|
|
|
Likewise, a future persistence extension may own persistence-specific metadata, and an RPC extension may own protocol metadata.
|
|
|
|
## 3. External policy stays external
|
|
|
|
Property Core has no built-in access-control mode.
|
|
|
|
An extension or application layer is responsible for its own policy:
|
|
|
|
```text
|
|
Should this property be visible?
|
|
Should this endpoint permit a write?
|
|
Should this property be persisted?
|
|
Does the current user have permission?
|
|
```
|
|
|
|
An extension may inspect structural facts such as:
|
|
|
|
```text
|
|
Property::readable
|
|
Property::writable
|
|
sensitive metadata
|
|
extension-owned metadata
|
|
```
|
|
|
|
but the final policy belongs to the consumer.
|
|
|
|
A `read_only` property means the Structive managed object itself has no write operation for that property. It does not mean every external system must expose it.
|
|
|
|
## 4. Current presentation extension
|
|
|
|
Include:
|
|
|
|
```cpp
|
|
#include <structive/property/extensions/presentation.hpp>
|
|
```
|
|
|
|
Link:
|
|
|
|
```cmake
|
|
target_link_libraries(my_target PRIVATE structive::property_extensions)
|
|
```
|
|
|
|
Available Attributes:
|
|
|
|
```cpp
|
|
presentation::label<"Temperature">
|
|
presentation::description<"Current device temperature">
|
|
presentation::group<"Environment">
|
|
presentation::order<2>
|
|
```
|
|
|
|
Example:
|
|
|
|
```cpp
|
|
field<&Device::temperature>(
|
|
key<"temperature">,
|
|
unit<"C">,
|
|
presentation::label<"Temperature">,
|
|
presentation::description<"Current device temperature">,
|
|
presentation::group<"Environment">,
|
|
presentation::order<2>
|
|
)
|
|
```
|
|
|
|
## 5. Presentation interpretation
|
|
|
|
```cpp
|
|
auto info = presentation::describe<&Device::temperature>(device.schema());
|
|
```
|
|
|
|
`Presentation_Info` contains:
|
|
|
|
```text
|
|
key
|
|
label
|
|
description
|
|
group
|
|
order
|
|
has_order
|
|
```
|
|
|
|
If no label is supplied, the presentation extension falls back to the property key. That fallback is presentation behavior, not Core behavior.
|
|
|
|
## 6. Inheritable extension Attributes
|
|
|
|
An extension category may choose to be inheritable.
|
|
|
|
```cpp
|
|
defaults(
|
|
presentation::group<"Environment">
|
|
)
|
|
```
|
|
|
|
Property-level metadata overrides an inheritable single-valued default from the same category.
|
|
|
|
Use inheritance only for metadata whose semantics genuinely support inheritance.
|
|
|
|
## 7. Designing a new extension Attribute
|
|
|
|
A simple extension Attribute can be defined as:
|
|
|
|
```cpp
|
|
namespace my_extension {
|
|
struct Format_Category {};
|
|
template <Fixed_String Value>
|
|
struct Format_Attribute {
|
|
using attribute_category = Format_Category;
|
|
static constexpr bool single_valued = true;
|
|
static constexpr bool inheritable = false;
|
|
static constexpr auto value = Value;
|
|
};
|
|
template <Fixed_String Value>
|
|
inline constexpr Format_Attribute<Value> format{};
|
|
}
|
|
```
|
|
|
|
Then attach it normally:
|
|
|
|
```cpp
|
|
field<&Device::temperature>(
|
|
key<"temperature">,
|
|
my_extension::format<"0.0 C">
|
|
)
|
|
```
|
|
|
|
Core does not require modification.
|
|
|
|
## 8. Reading extension Attributes
|
|
|
|
Descriptor-local metadata:
|
|
|
|
```cpp
|
|
using Property = std::remove_cvref_t<decltype(schema.property<&Device::temperature>())>;
|
|
static_assert(Property::template has_attribute<my_extension::Format_Category>);
|
|
```
|
|
|
|
Effective inheritable metadata can use Core's generic effective-Attribute utilities.
|
|
|
|
The extension owns fallback and interpretation rules.
|
|
|
|
## 9. Multi-valued metadata
|
|
|
|
If a category is not single-valued, an extension may allow multiple Attributes from that category.
|
|
|
|
Do not mark a category `single_valued` unless duplicate declarations are structurally invalid.
|
|
|
|
## 10. Recommended extension boundaries
|
|
|
|
Good extension domains include:
|
|
|
|
- presentation and editor hints;
|
|
- serialization naming and format hints;
|
|
- persistence mapping metadata;
|
|
- RPC/protocol naming metadata;
|
|
- diagnostics and telemetry metadata;
|
|
- documentation metadata.
|
|
|
|
The domain implementation should remain outside Property Core.
|
|
|
|
## 11. Using intrinsic capability
|
|
|
|
A consumer may use intrinsic capability as a structural fact.
|
|
|
|
For example, an editor can decide that a property is only eligible for an editable widget when:
|
|
|
|
```cpp
|
|
Property::writable
|
|
```
|
|
|
|
That is only eligibility. The editor may still apply additional application policy.
|
|
|
|
A persistence adapter may choose not to store a property even when `Property::readable` is true.
|
|
|
|
An RPC endpoint may choose not to expose a writable property at all.
|
|
|
|
This separation is intentional.
|
|
|
|
## 12. Read-only optimization is a Core fact
|
|
|
|
Extensions do not need to implement special locking for `read_only` stored properties.
|
|
|
|
Core guarantees that such properties do not contribute lock slots or mutexes to the managed object and managed reads use the no-lock path.
|
|
|
|
An extension that deliberately performs raw C++ writes bypasses that guarantee and owns synchronization itself.
|
|
|
|
## 13. Fallback behavior belongs to the consumer
|
|
|
|
Core should not decide domain fallbacks such as:
|
|
|
|
```text
|
|
missing label -> key
|
|
missing database column -> key
|
|
missing RPC name -> key
|
|
```
|
|
|
|
Those rules belong to the corresponding extension or adapter.
|
|
|
|
## 14. Avoid semantic leakage into Core
|
|
|
|
Do not add a Core enum or special branch only because one extension needs it.
|
|
|
|
Prefer:
|
|
|
|
```text
|
|
extension-owned Attribute category
|
|
+ extension-owned interpretation
|
|
```
|
|
|
|
over:
|
|
|
|
```text
|
|
Core learns the extension domain
|
|
```
|
|
|
|
## 15. Linked code versus header-only metadata
|
|
|
|
Metadata types can usually remain header-only. Linked extension code is appropriate when a domain needs non-template runtime behavior.
|
|
|
|
The current presentation extension keeps metadata in headers and provides `make_presentation_info` in the linked target.
|
|
|
|
## 16. Extension review checklist
|
|
|
|
Before adding an extension feature, ask:
|
|
|
|
1. Does Core already provide the required generic structural mechanism?
|
|
2. Can the feature be expressed as an extension-owned Attribute?
|
|
3. Is its fallback rule owned by the extension?
|
|
4. Is access/exposure policy being kept outside Core?
|
|
5. Is intrinsic `readable/writable` being treated as structure rather than authorization?
|
|
6. Does the extension avoid raw writes that would bypass managed synchronization unless intentional?
|
|
7. Does Core remain independent of the extension?
|