PR-002: Persistence Adapters — in-memory, zero external dependencies
- Repository interfaces defined by domain (@landvex/domain) - 6 in-memory adapters: Session, Mission, DecisionCase, Artifact, EventStore, UnitOfWork - 9 tests verifying adapter contracts - Domain unchanged — infrastructure depends on domain, never reverse - ADR-006: In-Memory Adapters for Testing Definition of Done met: - All adapters compile against domain interfaces - Unit tests pass (9/9) - No PostgreSQL, S3, Express, AI in this PR - Ready for PR-003: PostgreSQL adapters
This commit is contained in:
@@ -0,0 +1,98 @@
|
||||
/**
|
||||
* In-Memory Unit of Work
|
||||
*
|
||||
* Coordinates multiple in-memory repositories.
|
||||
* Simulates transaction boundaries for testing.
|
||||
*
|
||||
* ADR-006: In-memory adapters for testing
|
||||
* - commit() persists all changes
|
||||
* - rollback() discards all changes
|
||||
* - NOT atomic in production sense, but sufficient for tests
|
||||
*/
|
||||
|
||||
import {
|
||||
UnitOfWork,
|
||||
FieldSessionRepository,
|
||||
MissionRepository,
|
||||
DecisionCaseRepository,
|
||||
ArtifactRegistry,
|
||||
EventStore,
|
||||
} from '../repositories/repository-interfaces';
|
||||
import { InMemoryFieldSessionRepository } from './in-memory-session-repository';
|
||||
import { InMemoryMissionRepository } from './in-memory-mission-repository';
|
||||
import { InMemoryDecisionCaseRepository } from './in-memory-decision-case-repository';
|
||||
import { InMemoryArtifactRegistry } from './in-memory-artifact-registry';
|
||||
import { InMemoryEventStore } from './in-memory-event-store';
|
||||
|
||||
export class InMemoryUnitOfWork implements UnitOfWork {
|
||||
sessions: FieldSessionRepository;
|
||||
missions: MissionRepository;
|
||||
decisionCases: DecisionCaseRepository;
|
||||
artifacts: ArtifactRegistry;
|
||||
events: EventStore;
|
||||
|
||||
private snapshots: {
|
||||
sessions: Map<string, unknown>;
|
||||
missions: Map<string, unknown>;
|
||||
cases: Map<string, unknown>;
|
||||
artifacts: Map<string, unknown>;
|
||||
events: unknown[];
|
||||
} | null = null;
|
||||
|
||||
constructor() {
|
||||
this.sessions = new InMemoryFieldSessionRepository();
|
||||
this.missions = new InMemoryMissionRepository();
|
||||
this.decisionCases = new InMemoryDecisionCaseRepository();
|
||||
this.artifacts = new InMemoryArtifactRegistry();
|
||||
this.events = new InMemoryEventStore();
|
||||
}
|
||||
|
||||
async commit(): Promise<void> {
|
||||
// In-memory: changes are already applied
|
||||
// Real implementation would flush to database
|
||||
this.snapshots = null;
|
||||
}
|
||||
|
||||
async rollback(): Promise<void> {
|
||||
if (!this.snapshots) {
|
||||
throw new Error('No transaction in progress');
|
||||
}
|
||||
|
||||
// Restore from snapshots
|
||||
(this.sessions as InMemoryFieldSessionRepository).clear();
|
||||
(this.missions as InMemoryMissionRepository).clear();
|
||||
(this.decisionCases as InMemoryDecisionCaseRepository).clear();
|
||||
(this.artifacts as InMemoryArtifactRegistry).clear();
|
||||
(this.events as InMemoryEventStore).clear();
|
||||
|
||||
// Re-populate from snapshots
|
||||
for (const [id, session] of this.snapshots.sessions) {
|
||||
(this.sessions as InMemoryFieldSessionRepository).save(session as any);
|
||||
}
|
||||
for (const [id, mission] of this.snapshots.missions) {
|
||||
(this.missions as InMemoryMissionRepository).save(mission as any);
|
||||
}
|
||||
for (const [id, c] of this.snapshots.cases) {
|
||||
(this.decisionCases as InMemoryDecisionCaseRepository).save(c as any);
|
||||
}
|
||||
for (const [id, artifact] of this.snapshots.artifacts) {
|
||||
(this.artifacts as InMemoryArtifactRegistry).register(artifact as any);
|
||||
}
|
||||
for (const event of this.snapshots.events) {
|
||||
(this.events as InMemoryEventStore).append(event as any);
|
||||
}
|
||||
|
||||
this.snapshots = null;
|
||||
}
|
||||
|
||||
/** Begin transaction: capture snapshot */
|
||||
begin(): void {
|
||||
this.snapshots = {
|
||||
sessions: new Map((this.sessions as InMemoryFieldSessionRepository as any).sessions),
|
||||
missions: new Map((this.missions as InMemoryMissionRepository as any).missions),
|
||||
cases: new Map((this.decisionCases as InMemoryDecisionCaseRepository as any).cases),
|
||||
artifacts: new Map((this.artifacts as InMemoryArtifactRegistry as any).artifacts),
|
||||
events: [...(this.events as InMemoryEventStore as any).events],
|
||||
};
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user