Remove unused revision repository

This commit is contained in:
2026-07-26 11:40:04 +02:00
parent 4789e01aac
commit 0cedeaa959
7 changed files with 57 additions and 82 deletions
+2
View File
@@ -26,6 +26,8 @@ It must support circuits, device rows, project devices, drag-and-drop restructur
`src/db/repositories/project-command-transaction.persistence.ts` for the `src/db/repositories/project-command-transaction.persistence.ts` for the
atomic domain-write, revision and history transition boundary. Its applied atomic domain-write, revision and history transition boundary. Its applied
forward-command variant preserves derived CircuitDeviceRow override metadata. forward-command variant preserves derived CircuitDeviceRow override metadata.
- Low-level `appendProjectRevision` persistence is adapter-internal and tested
directly; do not reintroduce a standalone runtime revision repository.
- General Circuit, CircuitDeviceRow, CircuitList and DistributionBoard - General Circuit, CircuitDeviceRow, CircuitList and DistributionBoard
repositories expose only active reads. Runtime writes belong in typed command repositories expose only active reads. Runtime writes belong in typed command
repositories; direct integration fixtures belong under `tests/support`. repositories; direct integration fixtures belong under `tests/support`.
+3
View File
@@ -93,6 +93,9 @@ aus. Für CircuitDeviceRow-Feldänderungen übernimmt eine typisierte Variante d
während der Fachänderung um Override-Metadaten ergänzten Forward-Command. Die während der Fachänderung um Override-Metadaten ergänzten Forward-Command. Die
fachlichen Validierungs-, Forward- und Inversenregeln bleiben im jeweiligen fachlichen Validierungs-, Forward- und Inversenregeln bleiben im jeweiligen
Store. Store.
Die Low-Level-Funktion `appendProjectRevision` bleibt ein internes Detail dieser
Persistenzgrenze und wird direkt mit einer realen SQLite-Transaktion getestet.
Ein eigenständiges Runtime-Revisions-Repository existiert nicht.
`circuit-device-row.insert` und `circuit-device-row.delete` sind atomare `circuit-device-row.insert` und `circuit-device-row.delete` sind atomare
Strukturkommandos. Beim Löschen wird die vollständige Zeile im inversen Strukturkommandos. Beim Löschen wird die vollständige Zeile im inversen
Kommando gesichert, sodass Undo dieselbe UUID und alle Fachwerte wiederherstellt. Kommando gesichert, sodass Undo dieselbe UUID und alle Fachwerte wiederherstellt.
@@ -350,6 +350,8 @@ Implemented foundation:
restore, share one tested transaction wrapper for domain mutation, revision restore, share one tested transaction wrapper for domain mutation, revision
append and history transition; its applied-forward variant retains derived append and history transition; its applied-forward variant retains derived
CircuitDeviceRow override metadata CircuitDeviceRow override metadata
- low-level revision append persistence is tested directly; the unused
standalone runtime revision repository and store interface are removed
- the legacy consumer UI and application read/write endpoints are removed after verified data cutover - the legacy consumer UI and application read/write endpoints are removed after verified data cutover
- retained legacy rows are accessible only through explicit database upgrade tooling - retained legacy rows are accessible only through explicit database upgrade tooling
- project devices no longer persist duplicate legacy power, phase, cosPhi or remark fields - project devices no longer persist duplicate legacy power, phase, cosPhi or remark fields
+2 -2
View File
@@ -14,8 +14,8 @@
"build:api": "tsc -p tsconfig.json", "build:api": "tsc -p tsconfig.json",
"build:web": "next build", "build:web": "next build",
"start": "node dist/server/index.js", "start": "node dist/server/index.js",
"test": "tsx --test tests/project-device-schema.test.ts tests/project-device-schema-migration.test.ts tests/project-device-placement.service.test.ts tests/project-device-sync.service.test.ts tests/legacy-consumer-migration-planner.test.ts tests/legacy-consumer-migration.repository.test.ts tests/circuit-numbering.service.test.ts tests/circuit-power-calculation.test.ts tests/circuit-tree.controller.test.ts tests/circuit-editor-history.test.ts tests/circuit-grid-insertion.test.ts tests/circuit-grid-safety.test.ts tests/circuit-grid-model.test.ts tests/circuit-structure-command.test.ts tests/circuit-device-row-move-command.test.ts tests/circuit-section-reorder-command.test.ts tests/circuit-section-renumber-command.test.ts tests/circuit-grid-projection.test.ts tests/distribution-board-structure-project-command.repository.test.ts tests/project-location-structure-project-command.repository.test.ts tests/project-device-project-command.repository.test.ts tests/project-device-structure-project-command.repository.test.ts tests/project-device-row-sync-project-command.repository.test.ts tests/circuit-section-reorder-project-command.repository.test.ts tests/circuit-section-renumber-project-command.repository.test.ts tests/project-command.model.test.ts tests/project-revision.repository.test.ts tests/circuit-project-command.repository.test.ts tests/circuit-structure-project-command.repository.test.ts tests/circuit-device-row-project-command.repository.test.ts tests/circuit-device-row-structure-project-command.repository.test.ts tests/circuit-device-row-move-project-command.repository.test.ts tests/project-history.repository.test.ts tests/project-history-timeline.test.ts tests/project-version-history.test.ts tests/project-settings-project-command.repository.test.ts tests/project-state-snapshot.test.ts tests/project-snapshot-policy.test.ts tests/project-snapshot.repository.test.ts tests/project-state-restore-command.repository.test.ts tests/project-command.service.test.ts tests/database-backup.test.ts", "test": "tsx --test tests/project-device-schema.test.ts tests/project-device-schema-migration.test.ts tests/project-device-placement.service.test.ts tests/project-device-sync.service.test.ts tests/legacy-consumer-migration-planner.test.ts tests/legacy-consumer-migration.repository.test.ts tests/circuit-numbering.service.test.ts tests/circuit-power-calculation.test.ts tests/circuit-tree.controller.test.ts tests/circuit-editor-history.test.ts tests/circuit-grid-insertion.test.ts tests/circuit-grid-safety.test.ts tests/circuit-grid-model.test.ts tests/circuit-structure-command.test.ts tests/circuit-device-row-move-command.test.ts tests/circuit-section-reorder-command.test.ts tests/circuit-section-renumber-command.test.ts tests/circuit-grid-projection.test.ts tests/distribution-board-structure-project-command.repository.test.ts tests/project-location-structure-project-command.repository.test.ts tests/project-device-project-command.repository.test.ts tests/project-device-structure-project-command.repository.test.ts tests/project-device-row-sync-project-command.repository.test.ts tests/circuit-section-reorder-project-command.repository.test.ts tests/circuit-section-renumber-project-command.repository.test.ts tests/project-command.model.test.ts tests/project-revision.persistence.test.ts tests/circuit-project-command.repository.test.ts tests/circuit-structure-project-command.repository.test.ts tests/circuit-device-row-project-command.repository.test.ts tests/circuit-device-row-structure-project-command.repository.test.ts tests/circuit-device-row-move-project-command.repository.test.ts tests/project-history.repository.test.ts tests/project-history-timeline.test.ts tests/project-version-history.test.ts tests/project-settings-project-command.repository.test.ts tests/project-state-snapshot.test.ts tests/project-snapshot-policy.test.ts tests/project-snapshot.repository.test.ts tests/project-state-restore-command.repository.test.ts tests/project-command.service.test.ts tests/database-backup.test.ts",
"test:watch": "tsx --watch --test tests/project-device-schema.test.ts tests/project-device-schema-migration.test.ts tests/project-device-placement.service.test.ts tests/project-device-sync.service.test.ts tests/legacy-consumer-migration-planner.test.ts tests/legacy-consumer-migration.repository.test.ts tests/circuit-numbering.service.test.ts tests/circuit-power-calculation.test.ts tests/circuit-tree.controller.test.ts tests/circuit-editor-history.test.ts tests/circuit-grid-insertion.test.ts tests/circuit-grid-safety.test.ts tests/circuit-grid-model.test.ts tests/circuit-structure-command.test.ts tests/circuit-device-row-move-command.test.ts tests/circuit-section-reorder-command.test.ts tests/circuit-section-renumber-command.test.ts tests/circuit-grid-projection.test.ts tests/distribution-board-structure-project-command.repository.test.ts tests/project-location-structure-project-command.repository.test.ts tests/project-device-project-command.repository.test.ts tests/project-device-structure-project-command.repository.test.ts tests/project-device-row-sync-project-command.repository.test.ts tests/circuit-section-reorder-project-command.repository.test.ts tests/circuit-section-renumber-project-command.repository.test.ts tests/project-command.model.test.ts tests/project-revision.repository.test.ts tests/circuit-project-command.repository.test.ts tests/circuit-structure-project-command.repository.test.ts tests/circuit-device-row-project-command.repository.test.ts tests/circuit-device-row-structure-project-command.repository.test.ts tests/circuit-device-row-move-project-command.repository.test.ts tests/project-history.repository.test.ts tests/project-history-timeline.test.ts tests/project-version-history.test.ts tests/project-settings-project-command.repository.test.ts tests/project-state-snapshot.test.ts tests/project-snapshot-policy.test.ts tests/project-snapshot.repository.test.ts tests/project-state-restore-command.repository.test.ts tests/project-command.service.test.ts tests/database-backup.test.ts", "test:watch": "tsx --watch --test tests/project-device-schema.test.ts tests/project-device-schema-migration.test.ts tests/project-device-placement.service.test.ts tests/project-device-sync.service.test.ts tests/legacy-consumer-migration-planner.test.ts tests/legacy-consumer-migration.repository.test.ts tests/circuit-numbering.service.test.ts tests/circuit-power-calculation.test.ts tests/circuit-tree.controller.test.ts tests/circuit-editor-history.test.ts tests/circuit-grid-insertion.test.ts tests/circuit-grid-safety.test.ts tests/circuit-grid-model.test.ts tests/circuit-structure-command.test.ts tests/circuit-device-row-move-command.test.ts tests/circuit-section-reorder-command.test.ts tests/circuit-section-renumber-command.test.ts tests/circuit-grid-projection.test.ts tests/distribution-board-structure-project-command.repository.test.ts tests/project-location-structure-project-command.repository.test.ts tests/project-device-project-command.repository.test.ts tests/project-device-structure-project-command.repository.test.ts tests/project-device-row-sync-project-command.repository.test.ts tests/circuit-section-reorder-project-command.repository.test.ts tests/circuit-section-renumber-project-command.repository.test.ts tests/project-command.model.test.ts tests/project-revision.persistence.test.ts tests/circuit-project-command.repository.test.ts tests/circuit-structure-project-command.repository.test.ts tests/circuit-device-row-project-command.repository.test.ts tests/circuit-device-row-structure-project-command.repository.test.ts tests/circuit-device-row-move-project-command.repository.test.ts tests/project-history.repository.test.ts tests/project-history-timeline.test.ts tests/project-version-history.test.ts tests/project-settings-project-command.repository.test.ts tests/project-state-snapshot.test.ts tests/project-snapshot-policy.test.ts tests/project-snapshot.repository.test.ts tests/project-state-restore-command.repository.test.ts tests/project-command.service.test.ts tests/database-backup.test.ts",
"db:generate": "drizzle-kit generate", "db:generate": "drizzle-kit generate",
"db:migrate": "drizzle-kit migrate", "db:migrate": "drizzle-kit migrate",
"db:backup": "tsx scripts/db-backup.ts", "db:backup": "tsx scripts/db-backup.ts",
@@ -1,31 +0,0 @@
import { eq } from "drizzle-orm";
import { ProjectRevisionConflictError } from "../../domain/errors/project-revision-conflict.error.js";
import type {
AppendProjectRevisionInput,
AppendedProjectRevision,
ProjectRevisionStore,
} from "../../domain/ports/project-revision.store.js";
import type { AppDatabase } from "../database-context.js";
import { projects } from "../schema/projects.js";
import { appendProjectRevision } from "./project-revision.persistence.js";
export { ProjectRevisionConflictError };
export class ProjectRevisionRepository implements ProjectRevisionStore {
constructor(private readonly database: AppDatabase) {}
getCurrentRevision(projectId: string) {
const project = this.database
.select({ currentRevision: projects.currentRevision })
.from(projects)
.where(eq(projects.id, projectId))
.get();
return project?.currentRevision ?? null;
}
append(input: AppendProjectRevisionInput): AppendedProjectRevision {
return this.database.transaction((tx) =>
appendProjectRevision(tx, input)
);
}
}
@@ -24,8 +24,3 @@ export interface AppendedProjectRevision {
revisionNumber: number; revisionNumber: number;
createdAtIso: string; createdAtIso: string;
} }
export interface ProjectRevisionStore {
getCurrentRevision(projectId: string): number | null;
append(input: AppendProjectRevisionInput): AppendedProjectRevision;
}
@@ -5,12 +5,11 @@ import { eq } from "drizzle-orm";
import { migrate } from "drizzle-orm/better-sqlite3/migrator"; import { migrate } from "drizzle-orm/better-sqlite3/migrator";
import { import {
createDatabaseContext, createDatabaseContext,
type AppDatabase,
type DatabaseContext, type DatabaseContext,
} from "../src/db/database-context.js"; } from "../src/db/database-context.js";
import { import { ProjectRevisionConflictError } from "../src/domain/errors/project-revision-conflict.error.js";
ProjectRevisionConflictError, import { appendProjectRevision } from "../src/db/repositories/project-revision.persistence.js";
ProjectRevisionRepository,
} from "../src/db/repositories/project-revision.repository.js";
import { ProjectSnapshotRepository } from "../src/db/repositories/project-snapshot.repository.js"; import { ProjectSnapshotRepository } from "../src/db/repositories/project-snapshot.repository.js";
import { projectChangeSets } from "../src/db/schema/project-change-sets.js"; import { projectChangeSets } from "../src/db/schema/project-change-sets.js";
import { projectRevisions } from "../src/db/schema/project-revisions.js"; import { projectRevisions } from "../src/db/schema/project-revisions.js";
@@ -27,10 +26,11 @@ function createTestDatabase(): DatabaseContext {
} }
function appendTestRevision( function appendTestRevision(
repository: ProjectRevisionRepository, database: AppDatabase,
expectedRevision: number expectedRevision: number
) { ) {
return repository.append({ return database.transaction((tx) =>
appendProjectRevision(tx, {
projectId: "project-1", projectId: "project-1",
expectedRevision, expectedRevision,
source: "user", source: "user",
@@ -46,21 +46,30 @@ function appendTestRevision(
type: "circuit.update", type: "circuit.update",
payload: { circuitId: "circuit-1", displayName: "Alt" }, payload: { circuitId: "circuit-1", displayName: "Alt" },
}, },
}); })
);
} }
describe("project revision repository", () => { function getCurrentRevision(database: AppDatabase, projectId: string) {
return (
database
.select({ currentRevision: projects.currentRevision })
.from(projects)
.where(eq(projects.id, projectId))
.get()?.currentRevision ?? null
);
}
describe("project revision persistence", () => {
it("appends immutable revision metadata and payloads with a monotonic number", () => { it("appends immutable revision metadata and payloads with a monotonic number", () => {
const context = createTestDatabase(); const context = createTestDatabase();
try { try {
const repository = new ProjectRevisionRepository(context.db); assert.equal(getCurrentRevision(context.db, "project-1"), 0);
const appended = appendTestRevision(context.db, 0);
assert.equal(repository.getCurrentRevision("project-1"), 0);
const appended = appendTestRevision(repository, 0);
assert.equal(appended.projectId, "project-1"); assert.equal(appended.projectId, "project-1");
assert.equal(appended.revisionNumber, 1); assert.equal(appended.revisionNumber, 1);
assert.equal(repository.getCurrentRevision("project-1"), 1); assert.equal(getCurrentRevision(context.db, "project-1"), 1);
const revision = context.db const revision = context.db
.select() .select()
@@ -105,9 +114,9 @@ describe("project revision repository", () => {
}), }),
}); });
const next = appendTestRevision(repository, 1); const next = appendTestRevision(context.db, 1);
assert.equal(next.revisionNumber, 2); assert.equal(next.revisionNumber, 2);
assert.equal(repository.getCurrentRevision("project-1"), 2); assert.equal(getCurrentRevision(context.db, "project-1"), 2);
} finally { } finally {
context.close(); context.close();
} }
@@ -116,18 +125,17 @@ describe("project revision repository", () => {
it("rejects a stale expected revision without writing history", () => { it("rejects a stale expected revision without writing history", () => {
const context = createTestDatabase(); const context = createTestDatabase();
try { try {
const repository = new ProjectRevisionRepository(context.db); appendTestRevision(context.db, 0);
appendTestRevision(repository, 0);
assert.throws( assert.throws(
() => appendTestRevision(repository, 0), () => appendTestRevision(context.db, 0),
(error) => (error) =>
error instanceof ProjectRevisionConflictError && error instanceof ProjectRevisionConflictError &&
error.expectedRevision === 0 && error.expectedRevision === 0 &&
error.actualRevision === 1 error.actualRevision === 1
); );
assert.equal(repository.getCurrentRevision("project-1"), 1); assert.equal(getCurrentRevision(context.db, "project-1"), 1);
assert.equal(context.db.select().from(projectRevisions).all().length, 1); assert.equal(context.db.select().from(projectRevisions).all().length, 1);
assert.equal(context.db.select().from(projectChangeSets).all().length, 1); assert.equal(context.db.select().from(projectChangeSets).all().length, 1);
} finally { } finally {
@@ -145,14 +153,12 @@ describe("project revision repository", () => {
SELECT RAISE(ABORT, 'forced change-set failure'); SELECT RAISE(ABORT, 'forced change-set failure');
END; END;
`); `);
const repository = new ProjectRevisionRepository(context.db);
assert.throws( assert.throws(
() => appendTestRevision(repository, 0), () => appendTestRevision(context.db, 0),
/forced change-set failure/ /forced change-set failure/
); );
assert.equal(repository.getCurrentRevision("project-1"), 0); assert.equal(getCurrentRevision(context.db, "project-1"), 0);
assert.equal(context.db.select().from(projectRevisions).all().length, 0); assert.equal(context.db.select().from(projectRevisions).all().length, 0);
assert.equal(context.db.select().from(projectChangeSets).all().length, 0); assert.equal(context.db.select().from(projectChangeSets).all().length, 0);
} finally { } finally {
@@ -163,7 +169,6 @@ describe("project revision repository", () => {
it("captures and retains automatic snapshots without deleting named snapshots", () => { it("captures and retains automatic snapshots without deleting named snapshots", () => {
const context = createTestDatabase(); const context = createTestDatabase();
try { try {
const revisions = new ProjectRevisionRepository(context.db);
const snapshots = new ProjectSnapshotRepository(context.db); const snapshots = new ProjectSnapshotRepository(context.db);
const named = snapshots.createNamed({ const named = snapshots.createNamed({
projectId: "project-1", projectId: "project-1",
@@ -177,7 +182,7 @@ describe("project revision repository", () => {
expectedRevision < 325; expectedRevision < 325;
expectedRevision += 1 expectedRevision += 1
) { ) {
appendTestRevision(revisions, expectedRevision); appendTestRevision(context.db, expectedRevision);
} }
const stored = context.db const stored = context.db
@@ -234,13 +239,12 @@ describe("project revision repository", () => {
it("rolls back revision 25 when automatic snapshot persistence fails", () => { it("rolls back revision 25 when automatic snapshot persistence fails", () => {
const context = createTestDatabase(); const context = createTestDatabase();
try { try {
const repository = new ProjectRevisionRepository(context.db);
for ( for (
let expectedRevision = 0; let expectedRevision = 0;
expectedRevision < 24; expectedRevision < 24;
expectedRevision += 1 expectedRevision += 1
) { ) {
appendTestRevision(repository, expectedRevision); appendTestRevision(context.db, expectedRevision);
} }
context.sqlite.exec(` context.sqlite.exec(`
CREATE TRIGGER fail_automatic_project_snapshot CREATE TRIGGER fail_automatic_project_snapshot
@@ -252,10 +256,10 @@ describe("project revision repository", () => {
`); `);
assert.throws( assert.throws(
() => appendTestRevision(repository, 24), () => appendTestRevision(context.db, 24),
/forced automatic snapshot failure/ /forced automatic snapshot failure/
); );
assert.equal(repository.getCurrentRevision("project-1"), 24); assert.equal(getCurrentRevision(context.db, "project-1"), 24);
assert.equal( assert.equal(
context.db.select().from(projectRevisions).all().length, context.db.select().from(projectRevisions).all().length,
24 24