7.5 KiB
Structive Extension Architecture
1. Extension role
Structive extensions add domain-specific interpretation without changing the Core structural model.
The intended dependency is:
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:
- one or more Attribute categories that express domain metadata;
- 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:
presentation::Label_Category
presentation::Description_Category
presentation::Group_Category
presentation::Order_Category
Convenience Attribute values:
presentation::label<"Temperature">
presentation::description<"Current device temperature">
presentation::group<"Environment">
presentation::order<10>
They can be attached directly to a normal Core property:
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:
#include <structive/property/extensions/presentation.hpp>
Link:
target_link_libraries(my_target PRIVATE structive::property_extensions)
Describe a property by member pointer:
auto info = presentation::describe<&Device::temperature>(device.schema());
The result contains:
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(...):
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:
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:
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:
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:
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
keyas the default protocol name; External_Accessto determine exposure;sensitiveto 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:
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:
JSON adapter needs alias support
↓
Core adds JSON_Alias_Category and JSON naming logic
Preferred direction:
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:
- Does this concept belong to Core or to one consumer domain?
- Can it be represented through the existing Attribute protocol?
- Does the extension own the Attribute category it interprets?
- Is an inheritable Attribute truly an object-wide default policy?
- Is fallback behavior kept inside the extension?
- Is the existing schema reused rather than duplicated?
- Does the dependency still point from extension to Core?
- Has Core remained free of third-party domain types?
- 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.