Document future project history architecture

This commit is contained in:
2026-07-23 18:11:09 +02:00
parent c7e5f7a80d
commit 6696bba72a
4 changed files with 227 additions and 0 deletions
+6
View File
@@ -105,3 +105,9 @@ Primary circuit-first editor route:
- `/projects/:projectId/circuit-lists/:circuitListId/tree-edit`
Legacy route remains separate (read-only/preview/migration transition path) and is intentionally not merged into the editable tree editor.
## Future Persistence Direction
Persistent undo/redo, project revisions, logical snapshots, external-model exchange and PostgreSQL readiness are specified in [Project History and External Model Architecture](./project-history-and-external-model-architecture.md).
Near-term refactoring must move command and transaction ownership toward server-side, project-scoped change sets. Database backups remain separate from user-visible project snapshots.
@@ -2,6 +2,9 @@
- Undo/redo history is session-local only.
- Undo/redo history is not persisted across reloads or between users.
- No project revision or logical snapshot model is implemented yet.
- No Revit/CSV/IFCGUID import and export workflow is implemented yet.
- Persistence currently targets local SQLite; PostgreSQL is an architectural option, not an implemented runtime.
- No global cross-project device library workflow is implemented yet.
- Final electrical sizing logic is not implemented yet.
- No full norm-compliant voltage-drop and protection-dimensioning calculation flow yet.
@@ -0,0 +1,149 @@
# Project History and External Model Architecture
## Status
This document records architectural direction for future work. It is not a final database schema and does not authorize implementing Revit exchange or migrating to PostgreSQL during unrelated phases.
## Goals
- persist undo/redo across page reloads and application restarts
- provide a project-wide version history with named restore points
- preserve an auditable sequence of user, import, migration and restore operations
- support future Revit/CSV round-trips using stable IFCGUID-based links
- keep the persistence boundary replaceable so PostgreSQL can be adopted later
## Separate Concerns
The following mechanisms solve different problems and must not be conflated:
- database backups protect against database loss or corruption
- project snapshots provide user-visible restore points
- change history records logical application operations
- undo/redo navigates or inverses eligible logical operations
- external import batches describe data exchanged with another model
Database backups remain necessary even after project version history exists.
## Revision and Change-Set Direction
History is project-wide. Every accepted mutating command should eventually execute through one server-side transaction that:
1. validates the expected current project revision
2. applies all domain writes
3. records one immutable change set with sufficient before/after data
4. increments the project revision
The conceptual entities are:
- `ProjectRevision`
- monotonically increasing revision number within one project
- timestamp, actor and source (`user`, `undo`, `redo`, `import`, `restore`, `migration`)
- optional label or description
- `ProjectChangeSet`
- one logical user or system command
- contains one or more domain operations committed atomically
- stores enough before/after state to validate and apply an inverse
- `ProjectSnapshot`
- a logical, database-independent representation of one project revision
- may be created periodically or explicitly named by a user
- accelerates restore without replaying the entire history
Full event sourcing is not required. Current normalized tables remain the primary read model. Immutable change sets plus periodic snapshots provide the required history without forcing every query to rebuild state from events.
## Undo, Redo and Restore Semantics
- Undo applies an inverse as a new recorded revision; it does not delete history.
- Redo reapplies an eligible undone operation as a new recorded revision.
- A new edit after undo may hide the prior redo branch in the normal UI, while the historical revisions remain auditable.
- Restoring a snapshot creates a new revision whose state matches the selected historical revision.
- The server, not React component state, is the source of truth for available undo/redo operations.
- After a page reload, the client requests the current revision and eligible history actions.
- Commands include an expected project revision. A stale command is rejected instead of silently overwriting a newer project state.
The current session-local frontend command stack is transitional. Editor modularization must separate command descriptions from React callbacks so commands can later be sent to and reconstructed by the server.
## Snapshot Storage
Project history must use logical project data, not copies of the SQLite database file. This keeps snapshots:
- project-scoped
- portable across SQLite and PostgreSQL
- independent from unrelated projects
- suitable for validation, comparison and selective restore tooling
Large snapshots may later be compressed or stored in object storage, with metadata and checksums retained in the database.
## Revit / CSV / IFCGUID Round-Trip Direction
External model exchange should use explicit staging and link entities instead of writing imported rows directly into circuits.
Conceptual entities:
- `ExternalModelSource`
- identifies a Revit/IFC model or another external source within a project
- `ExternalImportBatch`
- file name, checksum, import time, format/version and processing status
- `ExternalModelObject`
- stable IFCGUID and optional Revit element identifiers
- source attributes and last-seen import batch
- `ExternalObjectLink`
- explicit relationship to a project device, circuit device row or another future domain entity
- `ExternalParameterMapping`
- maps imported/exported columns to application fields
- `ExternalExportBatch`
- records which project revision and import source produced an exported file
Required behavior:
- IFCGUID uniqueness is scoped to project and external model source.
- Parsing writes to staging first.
- Users preview additions, changes, missing objects and conflicts before applying them.
- Applying an import creates one project revision and one auditable change set.
- Imported source values and locally assigned planning values remain distinguishable.
- Export retains IFCGUID and source identifiers so Revit parameters can be matched deterministically.
- Missing objects and duplicate/conflicting identifiers are reported, never silently reassigned.
## SQLite and PostgreSQL Direction
SQLite remains appropriate for the current local, single-process development workflow and can handle initial external-object volumes. Data volume alone is not a reason to migrate immediately.
PostgreSQL becomes appropriate when one or more of these conditions apply:
- multiple users edit projects concurrently
- the API runs as a shared network service
- background import/export jobs need independent workers
- revision/history queries or external-object datasets need server-side concurrency and operational tooling
- centralized backup, access control and monitoring become required
To preserve that option:
- domain services must not depend directly on `better-sqlite3`
- new multi-write workflows use an injectable transaction/unit-of-work boundary
- database-generated behavior is kept behind repositories
- IDs remain stable UUIDs and revisions use explicit project-scoped sequence numbers
- history snapshots remain logical and database-independent
- SQLite-specific synchronous transaction callbacks are treated as an adapter detail
- real persistence tests are separated so PostgreSQL parity tests can be added later
Drizzle schemas and migrations are dialect-specific and will require a deliberate PostgreSQL schema/migration path. The goal is portable domain behavior, not pretending the current database schema files can be switched without work.
## Near-Term Refactoring Constraints
Before implementing the future features:
- add real SQLite rollback tests around the persistence boundary
- stop adding direct imports of the global SQLite client to domain services
- extract transaction ownership from oversized repositories incrementally
- modularize editor history commands independently from UI rendering
- preserve stable domain IDs during legacy cleanup
- do not model IFCGUID as an overloaded circuit equipment identifier
- keep database backup/restore checks separate from project history tests
## Deferred Decisions
- exact snapshot frequency and retention policy
- whether large snapshot payloads remain in PostgreSQL or move to object storage
- branch visualization after undo followed by new edits
- user/role model and actor attribution
- supported Revit CSV dialects and parameter mapping UI
- PostgreSQL deployment topology and migration date
@@ -300,3 +300,72 @@ Acceptance criteria:
- undo/redo does not disrupt the current reading position
- the editor remains usable without overflowing the browser layout
- active filters are easy to understand, change and clear
## Phase 11: Persistence and Transaction Foundation
Goal:
Prepare the current SQLite implementation for persistent history and a later PostgreSQL adapter without changing user-facing behavior.
Tasks:
- add real SQLite integration tests for transaction commit and rollback
- introduce an injectable transaction / unit-of-work boundary
- remove direct database-client access from domain services
- treat synchronous SQLite transactions as an adapter detail
- remove dead persistence methods after coverage exists
- verify database backup and restore independently from project history
Acceptance criteria:
- critical multi-write commands roll back completely in a real database test
- domain command behavior is not coupled to `better-sqlite3`
- adding a PostgreSQL persistence adapter does not require rewriting editor domain rules
## Phase 12: Project Revisions and Persistent Undo / Redo
Goal:
Persist project-wide change history and restore points across reloads and application restarts.
Tasks:
- add project-scoped monotonic revisions
- record immutable server-side change sets with before/after state
- add optimistic revision checks for stale commands
- persist undo/redo eligibility on the server
- add named and periodic logical project snapshots
- restore a historical snapshot as a new revision
- keep database backups separate from logical history
Acceptance criteria:
- undo/redo remains available after a page reload
- every logical command and inverse is atomic and auditable
- restoring an older state does not delete intervening history
- concurrent stale commands cannot silently overwrite newer project state
## Phase 13: External Model Round-Trip
Goal:
Exchange model objects and planning parameters with Revit or comparable tools through CSV or another agreed format.
Tasks:
- model external sources, import batches and stable IFCGUID objects
- stage and validate imports before applying them
- show additions, changes, missing objects and conflicts
- link external objects explicitly to project-domain entities
- apply an import as one auditable project revision
- configure inbound and outbound parameter mappings
- export against a known project revision while preserving IFCGUID
Acceptance criteria:
- repeated imports match objects deterministically by source and IFCGUID
- imports never silently overwrite local planning values
- users approve the diff before it changes the project
- exported data can be assigned back to the correct Revit objects
PostgreSQL should be introduced when shared multi-user operation, background jobs or operational scale justify it. External-object count alone does not require an immediate migration.