Centralize update command transactions

This commit is contained in:
2026-07-26 11:24:19 +02:00
parent 0f306d9a90
commit e4d22caf09
6 changed files with 175 additions and 195 deletions
@@ -18,8 +18,7 @@ import {
toCircuitPatchValues,
type CircuitPatchPersistenceInput,
} from "./circuit.persistence.js";
import { applyProjectHistoryTransition } from "./project-history.persistence.js";
import { appendProjectRevision } from "./project-revision.persistence.js";
import { executeProjectCommandTransaction } from "./project-command-transaction.persistence.js";
type CircuitRow = typeof circuits.$inferSelect;
@@ -31,81 +30,74 @@ export class CircuitProjectCommandRepository
executeUpdate(input: ExecuteCircuitUpdateCommandInput) {
assertCircuitUpdateProjectCommand(input.command);
return this.database.transaction((tx) => {
const current = tx
.select()
.from(circuits)
.where(eq(circuits.id, input.command.payload.circuitId))
.get();
if (!current) {
throw new Error("Invalid circuit id.");
}
return executeProjectCommandTransaction(
this.database,
input,
(tx) => this.applyCommand(tx, input)
);
}
const owningList = tx
.select({ id: circuitLists.id })
.from(circuitLists)
.where(
and(
eq(circuitLists.id, current.circuitListId),
eq(circuitLists.projectId, input.projectId)
)
private applyCommand(
tx: AppDatabase,
input: ExecuteCircuitUpdateCommandInput
) {
const current = tx
.select()
.from(circuits)
.where(eq(circuits.id, input.command.payload.circuitId))
.get();
if (!current) {
throw new Error("Invalid circuit id.");
}
const owningList = tx
.select({ id: circuitLists.id })
.from(circuitLists)
.where(
and(
eq(circuitLists.id, current.circuitListId),
eq(circuitLists.projectId, input.projectId)
)
.get();
if (!owningList) {
throw new Error("Circuit does not belong to project.");
}
)
.get();
if (!owningList) {
throw new Error("Circuit does not belong to project.");
}
const patch = Object.fromEntries(
input.command.payload.changes.map((change) => [
change.field,
change.value,
])
) as CircuitPatchPersistenceInput;
this.assertSectionInCircuitList(tx, current, patch.sectionId);
this.assertUniqueEquipmentIdentifier(
tx,
current,
patch.equipmentIdentifier
);
const patch = Object.fromEntries(
input.command.payload.changes.map((change) => [
change.field,
change.value,
])
) as CircuitPatchPersistenceInput;
this.assertSectionInCircuitList(tx, current, patch.sectionId);
this.assertUniqueEquipmentIdentifier(
tx,
current,
patch.equipmentIdentifier
);
const inversePatch = Object.fromEntries(
input.command.payload.changes.map((change) => [
change.field,
getCircuitFieldValue(current, change.field),
])
) as CircuitUpdatePatch;
const inverse = createCircuitUpdateProjectCommand(
current.id,
inversePatch
);
const inversePatch = Object.fromEntries(
input.command.payload.changes.map((change) => [
change.field,
getCircuitFieldValue(current, change.field),
])
) as CircuitUpdatePatch;
const inverse = createCircuitUpdateProjectCommand(
current.id,
inversePatch
);
const update = tx
.update(circuits)
.set(toCircuitPatchValues(patch))
.where(eq(circuits.id, current.id))
.run();
if (update.changes !== 1) {
throw new Error("Circuit changed before command execution.");
}
const update = tx
.update(circuits)
.set(toCircuitPatchValues(patch))
.where(eq(circuits.id, current.id))
.run();
if (update.changes !== 1) {
throw new Error("Circuit changed before command execution.");
}
const revision = appendProjectRevision(tx, {
projectId: input.projectId,
expectedRevision: input.expectedRevision,
source: input.source,
description: input.description,
actorId: input.actorId,
forward: input.command,
inverse,
});
applyProjectHistoryTransition(tx, {
projectId: input.projectId,
source: input.source,
recordedChangeSetId: revision.changeSetId,
targetChangeSetId: input.historyTargetChangeSetId,
});
return { revision, inverse };
});
return inverse;
}
private assertSectionInCircuitList(