Decouple runtime domain services

This commit is contained in:
2026-07-26 12:04:50 +02:00
parent 0cedeaa959
commit c1be85e408
9 changed files with 103 additions and 35 deletions
@@ -1,5 +1,3 @@
import { CircuitDeviceRowRepository } from "../../db/repositories/circuit-device-row.repository.js";
import { ProjectDeviceRepository } from "../../db/repositories/project-device.repository.js";
import {
createProjectDeviceRowSyncProjectCommand,
projectDeviceSyncRowSnapshotFields,
@@ -20,15 +18,44 @@ export {
serializeOverriddenFields,
} from "./project-device-overrides.js";
type ProjectDevice = NonNullable<Awaited<ReturnType<ProjectDeviceRepository["findById"]>>>;
type LinkedRow = Awaited<ReturnType<CircuitDeviceRowRepository["listLinkedByProjectDevice"]>>[number];
export type ProjectDeviceSyncSource = {
id: string;
} & Pick<ProjectDeviceSyncRowSnapshot, ProjectDeviceSyncField>;
type SyncDependencies = {
projectDeviceRepository: Pick<ProjectDeviceRepository, "findById">;
deviceRowRepository: Pick<CircuitDeviceRowRepository, "listLinkedByProjectDevice">;
export type LinkedProjectDeviceRow = ProjectDeviceSyncRowSnapshot & {
id: string;
circuitId: string;
equipmentIdentifier: string;
circuitDisplayName: string | null;
circuitListId: string;
circuitListName: string;
distributionBoardId: string;
distributionBoardName: string;
};
function sourceValue(projectDevice: ProjectDevice, field: ProjectDeviceSyncField) {
export interface ProjectDeviceSyncSourceReader {
findById(
projectId: string,
projectDeviceId: string
): Promise<ProjectDeviceSyncSource | null>;
}
export interface LinkedProjectDeviceRowReader {
listLinkedByProjectDevice(
projectId: string,
projectDeviceId: string
): Promise<LinkedProjectDeviceRow[]>;
}
export interface ProjectDeviceSyncDependencies {
projectDeviceRepository: ProjectDeviceSyncSourceReader;
deviceRowRepository: LinkedProjectDeviceRowReader;
}
function sourceValue(
projectDevice: ProjectDeviceSyncSource,
field: ProjectDeviceSyncField
) {
return projectDevice[field];
}
@@ -36,7 +63,10 @@ function valuesEqual(left: unknown, right: unknown) {
return (left ?? null) === (right ?? null);
}
function buildDifferences(projectDevice: ProjectDevice, row: LinkedRow) {
function buildDifferences(
projectDevice: ProjectDeviceSyncSource,
row: LinkedProjectDeviceRow
) {
return projectDeviceSyncFields
.filter((field) => !valuesEqual(row[field], sourceValue(projectDevice, field)))
.map((field) => ({
@@ -48,12 +78,12 @@ function buildDifferences(projectDevice: ProjectDevice, row: LinkedRow) {
}
export class ProjectDeviceSyncService {
private readonly projectDeviceRepository: SyncDependencies["projectDeviceRepository"];
private readonly deviceRowRepository: SyncDependencies["deviceRowRepository"];
private readonly projectDeviceRepository: ProjectDeviceSyncSourceReader;
private readonly deviceRowRepository: LinkedProjectDeviceRowReader;
constructor(deps?: Partial<SyncDependencies>) {
this.projectDeviceRepository = deps?.projectDeviceRepository ?? new ProjectDeviceRepository();
this.deviceRowRepository = deps?.deviceRowRepository ?? new CircuitDeviceRowRepository();
constructor(deps: ProjectDeviceSyncDependencies) {
this.projectDeviceRepository = deps.projectDeviceRepository;
this.deviceRowRepository = deps.deviceRowRepository;
}
async getPreview(projectId: string, projectDeviceId: string) {
@@ -154,10 +184,15 @@ export class ProjectDeviceSyncService {
);
}
private resolveSelectedRows(linkedRows: LinkedRow[], rowIds: string[]) {
private resolveSelectedRows(
linkedRows: LinkedProjectDeviceRow[],
rowIds: string[]
) {
const uniqueRowIds = [...new Set(rowIds)];
const byId = new Map(linkedRows.map((row) => [row.id, row]));
const selectedRows = uniqueRowIds.map((rowId) => byId.get(rowId)).filter(Boolean) as LinkedRow[];
const selectedRows = uniqueRowIds
.map((rowId) => byId.get(rowId))
.filter(Boolean) as LinkedProjectDeviceRow[];
if (selectedRows.length !== uniqueRowIds.length) {
throw new Error("One or more rows are not linked to this project device.");
}
@@ -166,7 +201,9 @@ export class ProjectDeviceSyncService {
}
function toSyncSnapshot(row: LinkedRow): ProjectDeviceSyncRowSnapshot {
function toSyncSnapshot(
row: LinkedProjectDeviceRow
): ProjectDeviceSyncRowSnapshot {
return {
linkedProjectDeviceId: row.linkedProjectDeviceId,
name: row.name,