You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@causeway.apache.org by da...@apache.org on 2026/06/08 17:29:00 UTC
(causeway) 23/25: CAUSEWAY-4032: proposes default refdata marker service
This is an automated email from the ASF dual-hosted git repository.
danhaywood pushed a commit to branch CAUSEWAY-4032
in repository https://gitbox.apache.org/repos/asf/causeway.git
commit e9420f1b1757fc0c3da1063e7127fef91f203037
Author: Dan Haywood <da...@haywood-associates.co.uk>
AuthorDate: Mon Jun 8 15:03:58 2026 +0100
CAUSEWAY-4032: proposes default refdata marker service
---
.../default-refdata-marker-service/.openspec.yaml | 2 +
.../default-refdata-marker-service/design.md | 54 ++++++++++++++++++++++
.../default-refdata-marker-service/proposal.md | 27 +++++++++++
.../specs/command-export-refdata-marker/spec.md | 46 ++++++++++++++++++
.../spec.md | 41 ++++++++++++++++
.../default-refdata-marker-service/tasks.md | 26 +++++++++++
6 files changed, 196 insertions(+)
diff --git a/openspec/changes/default-refdata-marker-service/.openspec.yaml b/openspec/changes/default-refdata-marker-service/.openspec.yaml
new file mode 100644
index 00000000000..e8d4ccfe905
--- /dev/null
+++ b/openspec/changes/default-refdata-marker-service/.openspec.yaml
@@ -0,0 +1,2 @@
+schema: spec-driven
+created: 2026-06-08
diff --git a/openspec/changes/default-refdata-marker-service/design.md b/openspec/changes/default-refdata-marker-service/design.md
new file mode 100644
index 00000000000..76c921701ee
--- /dev/null
+++ b/openspec/changes/default-refdata-marker-service/design.md
@@ -0,0 +1,54 @@
+## Context
+
+The command-log applib exposes `CommandReplayReferenceDataService` so applications can classify stable replay reference data from bookmarks.
+The common case is simpler than a custom classifier: a domain class itself can declare that its instances are reference data.
+The implementation needs to classify from a bookmark without loading the entity instance, so it should use metamodel lookup rather than repository lookup.
+
+## Goals / Non-Goals
+
+**Goals:**
+
+- Add a public `RefData` marker interface for domain classes whose instances are stable replay reference data.
+- Register a default `CommandReplayReferenceDataService` implementation that recognizes bookmarked classes assignable to `RefData`.
+- Use `SpecificationLoader` to resolve a bookmark logical type to an `ObjectSpecification` and then inspect its corresponding class.
+- Preserve the existing multi-implementation SPI behavior so custom classifiers still participate.
+
+**Non-Goals:**
+
+- Infer reference data from annotations, packages, persistence tables, or local database rows.
+- Load or instantiate bookmarked entities during export validation.
+- Replace custom `CommandReplayReferenceDataService` implementations.
+- Change command export YAML or replay remapping semantics.
+
+## Decisions
+
+### Use a marker interface named `RefData`
+
+Add an empty public marker interface in the command-log applib API, most likely under the `spi` package alongside `CommandReplayReferenceDataService`.
+Applications opt in by implementing this marker on domain classes whose identifiers are stable across replay environments.
+
+Alternative considered: add an annotation.
+A marker interface is sufficient, cheap to test with `isAssignableFrom`, and avoids adding annotation processing rules.
+
+### Implement classification through `SpecificationLoader`
+
+The default service receives a `Bookmark`, extracts `logicalTypeName`, asks `SpecificationLoader` for the corresponding `ObjectSpecification`, obtains the corresponding class, and returns true when that class implements `RefData`.
+Missing logical types or specifications are treated as non-reference-data classifications.
+
+Alternative considered: use `MetaModelService.lookupLogicalTypeByName` directly.
+That can identify Java classes for known logical types, but `SpecificationLoader` is the requested integration point and provides the object specification used by the metamodel.
+
+### Register the default classifier as a normal SPI implementation
+
+The default marker classifier should itself implement `CommandReplayReferenceDataService` and be registered as a domain service or component in the command-log applib module.
+This lets the existing list-injection and OR composition include marker-based classification without special cases in export validation.
+
+Alternative considered: hard-code marker lookup in `CommandExportManager`.
+That would bypass the SPI composition model and make future classifiers harder to reason about.
+
+## Risks / Trade-offs
+
+- Marker interface is applied too broadly → Replay may rely on mutable data existing in target environments; document the marker's intended stability contract.
+- Specification lookup cannot resolve the logical type → The default service returns false so existing validation remains protective.
+- Default service registration changes behavior for applications that add the marker → This is the intended opt-in; unmarked classes keep current behavior.
+- Interface package choice affects API discoverability → Place it near `CommandReplayReferenceDataService` and document the relationship.
diff --git a/openspec/changes/default-refdata-marker-service/proposal.md b/openspec/changes/default-refdata-marker-service/proposal.md
new file mode 100644
index 00000000000..8771d1437f9
--- /dev/null
+++ b/openspec/changes/default-refdata-marker-service/proposal.md
@@ -0,0 +1,27 @@
+## Why
+
+Applications can now provide custom `CommandReplayReferenceDataService` implementations, but each application must write boilerplate to recognize ordinary reference-data classes.
+A built-in marker-interface approach gives applications a simple default path while preserving the existing SPI extension point for more specialized classification.
+
+## What Changes
+
+- Add a public marker interface named `RefData` for domain classes whose bookmarked instances are stable replay reference data.
+- Provide a default `CommandReplayReferenceDataService` implementation that classifies bookmarks by logical type.
+- Have the default implementation look up the bookmark logical type through `SpecificationLoader`, obtain the corresponding class from the `ObjectSpecification`, and accept classes assignable to `RefData`.
+- Keep custom `CommandReplayReferenceDataService` implementations supported and composable with the default implementation.
+
+## Capabilities
+
+### New Capabilities
+
+- `command-export-refdata-marker`: Defines the built-in marker-interface classifier for replay reference data.
+
+### Modified Capabilities
+
+- `command-export-reference-data-participants`: Reference-data SPI behavior now includes the built-in marker-interface implementation as a default classifier.
+
+## Impact
+
+- Adds a public `RefData` marker interface to the command-log applib API.
+- Adds a default domain service implementation of `CommandReplayReferenceDataService` in the command-log applib module.
+- Requires integration with `SpecificationLoader` and tests for marker matches, non-marker misses, unknown logical types, and continued composition with custom classifiers.
diff --git a/openspec/changes/default-refdata-marker-service/specs/command-export-refdata-marker/spec.md b/openspec/changes/default-refdata-marker-service/specs/command-export-refdata-marker/spec.md
new file mode 100644
index 00000000000..a327f50c2f4
--- /dev/null
+++ b/openspec/changes/default-refdata-marker-service/specs/command-export-refdata-marker/spec.md
@@ -0,0 +1,46 @@
+## ADDED Requirements
+
+### Requirement: Marker interface declares replay reference data
+The command-log extension SHALL provide a public `RefData` marker interface for domain classes whose bookmarked instances are replay reference data.
+A class that implements `RefData` SHALL declare that its instances have stable logical type and identifier values expected to exist in replay environments.
+The marker interface MUST NOT require methods or entity loading during command export validation.
+
+#### Scenario: Domain class implements marker
+- **GIVEN** a domain class implements `RefData`
+- **WHEN** command export validation classifies a bookmark whose logical type resolves to that class
+- **THEN** the bookmark is classified as replay reference data
+
+#### Scenario: Domain class does not implement marker
+- **GIVEN** a domain class does not implement `RefData`
+- **WHEN** command export validation classifies a bookmark whose logical type resolves to that class
+- **THEN** the marker-based classifier does not classify the bookmark as replay reference data
+
+### Requirement: Default service classifies marker implementations using the metamodel
+The command-log extension SHALL provide a default `CommandReplayReferenceDataService` implementation for the `RefData` marker interface.
+The default implementation SHALL extract the bookmark logical type name and use `SpecificationLoader` to obtain the corresponding `ObjectSpecification`.
+The default implementation SHALL obtain the `ObjectSpecification` corresponding class and classify the bookmark as replay reference data when that class implements `RefData`.
+The default implementation MUST NOT load, instantiate, or resolve the bookmarked entity instance.
+When the logical type cannot be resolved to an object specification or corresponding class, the default implementation MUST NOT classify the bookmark as replay reference data.
+
+#### Scenario: Bookmark resolves to marker class
+- **GIVEN** bookmark `demoCategory:STD` has logical type `demoCategory`
+- **AND** `SpecificationLoader` resolves `demoCategory` to an object specification whose corresponding class implements `RefData`
+- **WHEN** the default service classifies `demoCategory:STD`
+- **THEN** the service returns `true`
+
+#### Scenario: Bookmark resolves to non-marker class
+- **GIVEN** bookmark `demoCustomer:1` has logical type `demoCustomer`
+- **AND** `SpecificationLoader` resolves `demoCustomer` to an object specification whose corresponding class does not implement `RefData`
+- **WHEN** the default service classifies `demoCustomer:1`
+- **THEN** the service returns `false`
+
+#### Scenario: Bookmark logical type is unknown
+- **GIVEN** bookmark `missingType:1` has a logical type that `SpecificationLoader` cannot resolve to an object specification
+- **WHEN** the default service classifies `missingType:1`
+- **THEN** the service returns `false`
+
+#### Scenario: Entity instance is not loaded
+- **GIVEN** bookmark `demoCategory:STD` has logical type `demoCategory`
+- **WHEN** the default service classifies the bookmark
+- **THEN** the service uses metamodel type information only
+- **AND** the service does not load the entity identified by `STD`
diff --git a/openspec/changes/default-refdata-marker-service/specs/command-export-reference-data-participants/spec.md b/openspec/changes/default-refdata-marker-service/specs/command-export-reference-data-participants/spec.md
new file mode 100644
index 00000000000..78bdb2d9814
--- /dev/null
+++ b/openspec/changes/default-refdata-marker-service/specs/command-export-reference-data-participants/spec.md
@@ -0,0 +1,41 @@
+## MODIFIED Requirements
+
+### Requirement: Applications classify replay reference data through an SPI
+The command-log extension SHALL provide a public SPI that allows a consuming application to decide whether a bookmarked entity is replay reference data.
+The SPI SHALL receive the entity bookmark or equivalent logical type and identifier information needed to classify the entity without requiring the entity to have appeared on the exported dotted path.
+The command-log extension SHALL register its built-in `RefData` marker classifier as a default SPI implementation.
+The system SHALL treat an entity as replay reference data when at least one registered SPI implementation classifies its bookmark as reference data.
+The system MUST NOT treat an entity as replay reference data when no registered SPI implementation classifies it as reference data.
+The system SHALL allow zero, one, or many application-provided SPI implementations to be registered in addition to the default marker classifier.
+
+#### Scenario: Single implementation classifies a bookmark as reference data
+- **GIVEN** a consuming application registers one replay reference-data SPI implementation
+- **AND** the implementation classifies bookmark `demoCategory:STD` as reference data
+- **WHEN** command export validation asks whether `demoCategory:STD` is reference data
+- **THEN** the system treats `demoCategory:STD` as replay reference data
+
+#### Scenario: Default marker implementation classifies a bookmark as reference data
+- **GIVEN** bookmark `demoCategory:STD` resolves to a domain class that implements `RefData`
+- **WHEN** command export validation asks whether `demoCategory:STD` is reference data
+- **THEN** the system treats `demoCategory:STD` as replay reference data
+
+#### Scenario: Multiple implementations are consulted
+- **GIVEN** a consuming application registers two replay reference-data SPI implementations
+- **AND** the default marker classifier does not classify bookmark `demoCategory:STD` as reference data
+- **AND** the first application implementation does not classify bookmark `demoCategory:STD` as reference data
+- **AND** the second application implementation classifies bookmark `demoCategory:STD` as reference data
+- **WHEN** command export validation asks whether `demoCategory:STD` is reference data
+- **THEN** the system treats `demoCategory:STD` as replay reference data
+
+#### Scenario: No implementation accepts a bookmark
+- **GIVEN** a consuming application registers replay reference-data SPI implementations
+- **AND** the default marker classifier does not classify bookmark `demoCustomer:1` as reference data
+- **AND** none of the application implementations classify bookmark `demoCustomer:1` as reference data
+- **WHEN** command export validation asks whether `demoCustomer:1` is reference data
+- **THEN** the system does not treat `demoCustomer:1` as replay reference data
+
+#### Scenario: No application implementations are registered
+- **GIVEN** no application-provided replay reference-data SPI implementation is registered
+- **AND** the default marker classifier does not classify bookmark `demoCategory:STD` as reference data
+- **WHEN** command export validation asks whether bookmark `demoCategory:STD` is reference data
+- **THEN** the system does not treat `demoCategory:STD` as replay reference data
diff --git a/openspec/changes/default-refdata-marker-service/tasks.md b/openspec/changes/default-refdata-marker-service/tasks.md
new file mode 100644
index 00000000000..05f15071c7d
--- /dev/null
+++ b/openspec/changes/default-refdata-marker-service/tasks.md
@@ -0,0 +1,26 @@
+## 1. API Definition
+
+- [ ] 1.1 Add the public `RefData` marker interface in the command-log applib API.
+- [ ] 1.2 Document the marker contract that implementations must represent stable, well-known replay environment data.
+
+## 2. Default Classifier
+
+- [ ] 2.1 Add a default `CommandReplayReferenceDataService` implementation for `RefData` classes.
+- [ ] 2.2 Inject `SpecificationLoader` into the default implementation.
+- [ ] 2.3 Resolve bookmarks by logical type name to `ObjectSpecification` and corresponding class.
+- [ ] 2.4 Return true only when the corresponding class implements `RefData`.
+- [ ] 2.5 Return false for unknown logical types, missing specifications, null bookmarks, or non-marker classes.
+- [ ] 2.6 Register the default classifier so it participates in the existing list of `CommandReplayReferenceDataService` implementations.
+
+## 3. Tests
+
+- [ ] 3.1 Add tests showing a bookmark whose logical type resolves to a `RefData` class is classified as reference data.
+- [ ] 3.2 Add tests showing a bookmark whose logical type resolves to a non-marker class is not classified as reference data.
+- [ ] 3.3 Add tests showing an unknown logical type is not classified as reference data.
+- [ ] 3.4 Add tests showing the default marker classifier composes with custom SPI implementations through existing OR semantics.
+- [ ] 3.5 Add tests or assertions showing entity instances are not loaded during marker classification.
+
+## 4. Verification
+
+- [ ] 4.1 Run the relevant command-log applib test suite.
+- [ ] 4.2 Run `openspec status --change default-refdata-marker-service` and confirm the change remains apply-ready.