Appearance
View Operations
End-to-end flows for view CREATE, UPDATE, DELETE, and schema sync/reset. Views map to database tables in the customer database — creating or deleting a view means creating or dropping an actual table.
View CREATE
Creates a new view (table) — always asynchronous because it requires CREATE TABLE on the customer database.
Response: 202 Accepted
Frontend (Spread) Quarkus REST RabbitMQ Processor (Spring Boot)
───────────────── ──────────── ──────── ───────────────────────
User clicks "Add View"
│
▼
NgRx action: createView
│
▼
ViewCrudEffects
│ POST /api/metadata/views
▼
ViewResource.createView()
│
▼
ViewService
.createByWorkspaceUuid()
│
├── Generates EntityMetadataDTO
│ (no View entity yet —
│ processor creates it)
│
└── Queues outbox message
│
MetadataUpdateProducer
.onEntityChange(CREATE)
│
▼
metadata-updates
exchange
│
▼
EntityMetadataConsumer
.processEntityMetadataInternal()
│
▼
CREATE TABLE IF NOT EXISTS
(via FlywayMigrationService)
│
▼
TaskCompletionProducer
.sendSuccess()
│
▼
task-completion
exchange
┌─────────────────────────────────────────────────────────────────────┘
▼
Quarkus TaskCompletionConsumer
│
▼
ViewOperationHandler
.handleCreate()
│
├── Creates View entity
├── Creates all ViewColumns
└── Creates ColumnMetadata records
│
▼
SSE: metadata.view.created
│
▼
Frontend receives SSE
│
▼
NgRx action: viewCreatedFromSSE
│
▼
Navigate to new viewKey Detail: Deferred View Entity Creation
Unlike columns (where metadata is created before the processor runs), views are only created in the metadata DB after the processor succeeds. The flow:
- REST endpoint generates an
EntityMetadataDTOdescribing the table structure - This DTO is sent to the processor via RabbitMQ (no View entity exists yet)
- Processor creates the physical table
- On success,
ViewOperationHandler.handleCreate()creates theView,ViewColumn, andColumnMetadatarecords
This means if the processor fails, no orphan View record exists in the metadata DB.
Frontend Optimistic Create
- A temporary view is added to the sidebar immediately with a generated UUID
- The view shows a loading state until SSE confirms creation
- A timeout mechanism removes the temp view if no SSE confirmation arrives within a configured window
- On SSE
metadata.view.created: the temp view is replaced with the real view and the user is navigated to it
View DELETE
Deletes a view (drops the table) — asynchronous with optimistic archival.
Response: 202 Accepted
Frontend (Spread) Quarkus REST RabbitMQ Processor (Spring Boot)
───────────────── ──────────── ──────── ───────────────────────
User deletes view
(confirm dialog)
│
▼
NgRx action: deleteView
│
▼
ViewCrudEffects
│ DELETE /api/metadata/
│ views/{uuid}
▼
ViewResource.deleteView()
│
▼
ViewService
.deleteByUuid()
│
├── Archives the view
│ (soft delete)
│
└── Queues outbox message
│
MetadataUpdateProducer
.onEntityChange(DELETE)
│
▼
metadata-updates
exchange
│
▼
EntityMetadataConsumer
.processEntityMetadataInternal()
│
▼
DROP TABLE IF EXISTS
(via FlywayMigrationService)
│
▼
TaskCompletionProducer
.sendSuccess()
│
▼
task-completion
exchange
┌─────────────────────────────────────────────────────────────────────┘
▼
Quarkus TaskCompletionConsumer
│
▼
ViewOperationHandler
.handleDelete()
│
├── Cascade delete ViewColumns
├── Cascade delete ColumnMetadata
└── Delete View entity
│
▼
SSE: metadata.view.deleted
│
▼
Frontend: viewDeletedFromSSE
│
▼
Remove view from sidebar,
navigate to next viewArchive-First Strategy
The view is archived (soft-deleted) before the processor runs. This allows rollback:
- On success:
ViewOperationHandler.handleDelete()performs the hard delete of all related records - On failure: the view is unarchived (restored) and an error notification is shown to the user
Cascade Delete Order
ViewOperationHandler.handleDelete() cleans up in this order:
ViewColumnPathrecordsViewColumnrecordsColumnMetadatarecordsViewentity itself
View UPDATE
Updates view properties (name, slug, position) — synchronous, no processor involvement.
Response: 200 OK
Frontend (Spread) Quarkus REST
───────────────── ────────────
User renames view
or changes position
│
▼
NgRx action: updateView
│
▼
ViewCrudEffects
│ PUT /api/metadata/views/{uuid}
▼
ViewResource.updateView()
│
▼
ViewService.updateByUuid()
│
├── Updates View entity
│ (name, slug, position)
│
└── Returns updated view
│
▼
SSE: metadata.view.updated
│
▼
Other clients: viewUpdatedFromSSEView updates are simple because they only affect metadata — no table structure changes, no processor involvement.
Schema Sync / Reset
Re-synchronizes the metadata DB with the actual state of the customer database. Used when a customer modifies their database directly (outside SchemaStack) and needs the UI to reflect the changes.
Frontend (Admin) Quarkus REST
───────────────── ────────────
User clicks "Sync Schema"
│
▼
POST /api/workspace/
{uuid}/schema/sync
│
▼
SchemaResetService
.resetSingleView()
│
├── 1. Connect to customer DB
│ (decrypt credentials,
│ JDBC connection)
│
├── 2. Introspect actual schema
│ (DatabaseMetaData.getTables()
│ + getColumns())
│
├── 3. Compare with metadata
│ (detect added/removed/
│ changed columns)
│
├── 4. Create new ViewColumns
│ for added columns
│
├── 5. Update changed columns
│ (type mismatches, etc.)
│
└── 6. Remove ViewColumns for
dropped columns
│
▼
SSE: workspace notification
│
▼
Frontend: reload viewSchemaSyncOrchestrator
The full workspace-level sync uses the SchemaSyncOrchestrator which manages a state machine with checkpoint/resume:
INITIATED → SCHEMA_EXTRACTED → ENTITIES_MERGED → RELATIONSHIPS_MERGED
→ VIEW_COLUMNS_GENERATED → HASH_UPDATED → COMPLETEDEach step is checkpointed so that a failed sync can be resumed from the last successful step rather than starting over.
Cross-reference
See the Multi-Tenancy page for full details on the SchemaSyncOrchestrator state machine, progress tracking, and the schema import flow.
What Sync Does NOT Do
- Does not create or drop tables — only syncs column-level changes within existing views
- Does not go through the processor — reads the customer DB directly via JDBC
- Does not modify the customer database — it only updates SchemaStack's metadata to match reality
Response Code Summary
| Operation | Sync/Async | Success Code | Notes |
|---|---|---|---|
| View CREATE | Async | 202 Accepted | Requires CREATE TABLE |
| View DELETE | Async | 202 Accepted | Requires DROP TABLE |
| View UPDATE | Sync | 200 OK | Metadata only |
| Schema Sync | Sync | 200 OK | Direct DB introspection |
Cross-References
- Processor — DDL generation for CREATE/DROP TABLE
- Multi-Tenancy — SchemaSyncOrchestrator and schema import
- System Overview — messaging architecture
- Column Operations — column-level operations within views