9734eec63c
Generated by per-group subagents covering all 52 modules across: core (18), infrastructure (10), apps (5), interfaces (3), plugins (7), testing (9) Documentation format: - AsciiDoc (.adoc) files in docs/modules/<group>/ - PlantUML (.puml) files in docs/diagrams/ - .adoc files reference diagrams via include:: directives Each doc covers: purpose, responsibilities, non-responsibilities, key types, event flow, integration points, invariants, PlantUML diagram, known issues, and open questions.
85 lines
4.0 KiB
Plaintext
85 lines
4.0 KiB
Plaintext
= infra-persistence
|
|
|
|
== purpose
|
|
|
|
Provides concrete `EventStore` implementations for persisting and replaying the event log. Supports in-memory storage (for testing and ephemeral sessions) and SQLite-backed durable storage. Also provides a live artifact repository that subscribes to events and maintains an up-to-date in-memory view of artifact state per session.
|
|
|
|
== responsibilities
|
|
|
|
* Implement the `EventStore` interface for both transient and durable storage
|
|
* Enforce duplicate event ID detection and sequence ordering
|
|
* Provide reactive subscriptions via `Flow<StoredEvent>` per session and globally
|
|
* Maintain an artifact state projection that stays current via event subscription
|
|
* Support transactional batch appends for atomic event persistence
|
|
|
|
== non-responsibilities
|
|
|
|
* Does not define event serialization — uses `JsonEventSerializer` from `core:events`
|
|
* Does not implement CAS content-addressable storage — that belongs to `infra:artifacts-cas`
|
|
* Does not handle event compaction or retention
|
|
* Does not implement cross-session querying beyond `allSessionIds()`
|
|
|
|
== key types
|
|
|
|
=== InMemoryEventStore
|
|
* **kind**: class
|
|
* **purpose**: Thread-safe, in-memory event store backed by `ConcurrentHashMap`. Supports duplicate detection via `seenEventIds` set and session-scoped sequence numbers.
|
|
* **fields**: `streams` (per-session event lists), `sequences` (per-session sequence counters), `globalSeq` (global counter), `seenEventIds` (dedup set)
|
|
|
|
=== SqliteEventStore
|
|
* **kind**: class
|
|
* **purpose**: Durable event store backed by a SQLite database with WAL mode. Uses JDBC directly. Emits events to reactive flows after append.
|
|
* **fields**: `connection` (JDBC SQLite connection), `jsonSerializer` (serialization bridge), `artifactStore` (used for `flushBefore` coordination)
|
|
|
|
=== LiveArtifactRepository
|
|
* **kind**: class
|
|
* **purpose**: Subscribes to an `EventStore` per session and maintains an up-to-date in-memory `ConcurrentHashMap` of `ArtifactState` values. Supports lookup by session, stage, and artifact ID.
|
|
* **fields**: `artifactCache` (session → artifactId → state), `stageIndex` (session → artifactId → stageId), `subscriptions` (session → coroutine Job), `projector` (reduces events to state)
|
|
|
|
=== JDBCHelper
|
|
* **kind**: object
|
|
* **purpose**: Utility providing `Connection.transaction()` inline helper with automatic commit/rollback.
|
|
|
|
== event flow
|
|
|
|
*Inbound:*
|
|
* `ArtifactCreatedEvent`, `ArtifactValidatingEvent`, `ArtifactValidatedEvent`, `ArtifactRejectedEvent`, `ArtifactSupersededEvent`, `ArtifactArchivedEvent`, `ArtifactRelationshipAddedEvent` — consumed by `LiveArtifactRepository` to maintain artifact projections
|
|
* `NewEvent` — appended to event stores via `append()` / `appendAll()`
|
|
|
|
*Outbound:*
|
|
* `StoredEvent` — emitted via `Flow<StoredEvent>` subscriptions after each append
|
|
|
|
== integration points
|
|
|
|
* `core:events` — `EventStore`, `StoredEvent`, `NewEvent`, all artifact event payloads, `EventId`, `SessionId`
|
|
* `core:artifacts` — `ArtifactProjector`, `ArtifactReducer`, `ArtifactState`, `ArtifactRepository`
|
|
* `core:artifactstore` — `ArtifactStore` (used by `SqliteEventStore` for flush coordination)
|
|
|
|
== invariants
|
|
|
|
* Duplicate `eventId` values are rejected with an error
|
|
* Session sequence numbers are strictly monotonic — violations throw `error()`
|
|
* `InMemoryEventStore.append()` is synchronized per session for thread safety
|
|
* `SqliteEventStore` wraps appends in a JDBC transaction and coordinates with `ArtifactStore.flushBefore()`
|
|
* `LiveArtifactRepository` lazily subscribes on first query and never unsubscribes
|
|
|
|
== PlantUML diagram
|
|
|
|
[plantuml, infra-persistence, "png"]
|
|
----
|
|
include::../../diagrams/infra-persistence.puml[]
|
|
----
|
|
|
|
|
|
|
|
|
|
== known issues
|
|
|
|
* `SqliteEventStore` holds an open JDBC `Connection` for the lifetime of the application
|
|
* No connection pooling or retry logic for SQLite busy conditions
|
|
|
|
== open questions
|
|
|
|
* Should event stores support snapshot-based recovery for long event streams?
|
|
* Should there be a read-replica event store for projections to avoid write-contention?
|