ADR-019: LabRecord as Independent AggregateRoot¶
| Attribute | Value |
|---|---|
| Status | Accepted (Partially Superseded) |
| Date | 2026-02-10 (Amended 2026-02-18) |
| Deciders | Architecture Team |
| Related ADRs | ADR-017, ADR-018, ADR-020 |
| Partially Superseded by | ADR-020 (binding model) |
| Reference | LabRecord Architecture |
Context¶
LabRecord currently exists as a passive snapshot synced from CML labs every 30 minutes. This limits reuse, blocks multi-lab topologies, and conflates lab lifecycle with LabletInstance scheduling and grading.
Decision¶
LabRecord SHALL be promoted to an independent AggregateRoot with its own lifecycle state machine, runtime abstraction, and repository.
Binding Model Superseded by ADR-020
The original design proposed a LabletLabBinding join entity for many-to-many associations.
Per ADR-020, the binding model is simplified to a direct 1:1 field (lab_record_id) on
LabletSession. The LabletLabBinding entity and lablet_lab_bindings collection are eliminated.
Multiple LabletSessions may reference the same LabRecord over time (reuse via wipe-for-reuse),
but the relationship at any point in time is 1:1.
The relationship between LabRecord and LabletSession (formerly LabletInstance) is a direct lab_record_id field on LabletSession, enabling lab reuse across time and multi-lab sessions.
Rationale¶
- Reuse & performance: Wipe+restart is significantly faster than cold import for repeated labs.
- Multi-lab topologies: A single session may require multiple interconnected labs.
- Runtime abstraction: Enables CML today and K8s/Pod runtimes later.
- DDD boundary clarity: Prevents foreign key coupling between aggregates.
Consequences¶
Positive¶
- Lab lifecycle decoupled from timeslot lifecycle
- Enables lab reuse, versioning, and discovery
- Supports multi-lab sessions and cross-runtime scaling
Negative¶
- Additional aggregate and repository complexity
- ~~Requires migration from
cml_lab_idto binding entity~~ (Superseded: directlab_record_idfield on LabletSession per ADR-020)
Risks¶
- Inconsistent state if discovery and reconciliation are not coordinated
- Requires careful SSE/event semantics to avoid UI confusion
Implementation Notes¶
See LabRecord Architecture for the full design, including:
LabRecordStatuslifecycle- RuntimeBinding value object
- ~~LabletLabBinding join entity~~ (Eliminated per ADR-020 — replaced by direct
lab_record_idon LabletSession) - Discovery + reuse flow
- API surface and UI updates