Skip to content

Backend Overview

The SchemaStack backend lives in the ../schemastack-be repository. It's a hybrid Quarkus + Spring Boot application built with Maven.

Why Two Frameworks?

Quarkus uses Hibernate Reactive for non-blocking database access, which is great for REST APIs and SSE endpoints. However, Hibernate Reactive cannot do programmatic DDL generation — it can't dynamically create tables, columns, and constraints at runtime.

Spring Boot with classic Hibernate handles this part: reading customer database schemas, generating DDL, and applying migrations with Flyway.

The two frameworks communicate exclusively through RabbitMQ — they never call each other directly.

Tech Stack

LayerTechnology
REST APIQuarkus (JAX-RS, Hibernate Reactive, Panache)
Schema processingSpring Boot (Hibernate, Flyway, Resilience4j)
Dynamic CRUDSpring Boot (workspace-api)
MessagingRabbitMQ (SmallRye Reactive Messaging / Spring AMQP)
DatabasePostgreSQL
Real-timeSSE (Server-Sent Events)
AuthJWT tokens (access + refresh)
BuildMaven multi-module

Key Domains

Metadata

Manages workspaces, views, columns, constraints, presets, relationships, and schema sync. This is the core domain — most CRUD operations go through metadata services.

Organisation

Manages organisations, memberships, invitations, authentication (including 2FA), user profiles, and session management.

Processor (Spring Boot)

Receives schema change messages from Quarkus via RabbitMQ. Generates DDL using Hibernate and applies it to customer databases using Flyway. Includes circuit breakers and retry logic via Resilience4j.

Workspace API (Spring Boot)

Dynamically serves REST CRUD endpoints for workspace data. Uses schema metadata to query customer databases without hardcoded entity classes.

Key Backend Systems

Value Transformers

The ValueTransformerRegistry maps each widget type to a transformer that converts raw database values to display values (and validates input on writes). 18 transformers are auto-discovered via CDI:

STRING, TEXT, INTEGER, NUMBER, DECIMAL, BOOLEAN, DATE, DATETIME, EMAIL, URL, PHONE, SELECT, MULTI_SELECT, IMAGE, FILE

Examples:

  • BooleanValueTransformer normalizes "true", "1", "t", "yes"true
  • ImageValueTransformer resolves FileReference JSON to download URLs (/api/files/{viewUuid}/download?key={key})
  • DateTimeValueTransformer passes through ISO format strings

Adding a new widget type: implement WidgetValueTransformer interface with getWidgetType() and transform(), annotate with @ApplicationScoped.

Bulk Action Jobs

Async bulk operations (delete, update, export) are tracked as BulkActionJob entities:

REST API → creates BulkActionJob (PENDING)
    → BulkActionProducer → RabbitMQ (bulk-actions exchange)
    → BulkActionHandler (processor) → processes rows
    → SSE notification on completion

Selection modes: SELECTED_IDS (explicit row IDs) or FILTERED (filter criteria, bulk applies to all matching).

Progress tracking: totalRows, processedRows, successCount, errorCount (max 100 error details stored).

Export files: stored via the file storage system at exports/{jobId}/filename.csv.

File Storage

Pluggable storage with a factory pattern (FileStorageFactory). Two providers:

ProviderStorageUse Case
LocalFileStorageProvider{basePath}/{workspaceUuid}/{key}Dev only — not available in production
S3FileStorageProviderAWS S3 / S3-compatible (MinIO)Required in production and staging

S3 provider features:

  • AWS SDK v2 (S3Client, S3Presigner)
  • Workspace-isolated keys ({workspaceUuid}/{key} prefix)
  • Presigned URL mode (configurable, default expiration 3600s)
  • Custom endpoint support (for MinIO, DigitalOcean Spaces, etc.)
  • pathStyleAccess option for S3-compatible stores
  • Connection testing via HeadBucketRequest

Provider caching: ConcurrentHashMap<UUID, FileStorageProvider> per workspace. S3 clients are expensive to create, so they are cached and evicted when config changes.

Security: Both providers validate paths — LocalFileStorageProvider normalizes and checks prefix, S3FileStorageProvider sanitizes segments (removes .., null bytes, leading slashes).

Storage Configuration API

WorkspaceStorageConfigResource — 4 endpoints for managing per-workspace S3 configuration:

MethodPathPurpose
GET/api/workspaces/{uuid}/storage-configGet config (credentials masked)
PUT/api/workspaces/{uuid}/storage-configCreate or update config
DELETE/api/workspaces/{uuid}/storage-configRemove config (dev only — blocked in production/staging with 400)
POST/api/workspaces/{uuid}/storage-config/testTest S3 connection

Credentials (accessKeyId, secretAccessKey) are encrypted at rest using EncryptionUtil (AES-256-GCM). Stored as a single encrypted JSON blob in workspace_storage_config.config_encrypted. Credentials are @JsonProperty(access = WRITE_ONLY) — never returned in API responses.

Smart File Mapping (Mapped Mode)

For file/image columns, S3 paths can be resolved dynamically from row data using templates:

Template resolution (S3PathTemplateResolver):

  • ${value} — current cell's raw value
  • ${row.column_name} — another column in the same row
  • ${row.column_name:fallback} — column value with default
  • ${workspace.uuid} — workspace UUID

Two-level override:

  1. Column-level: ViewColumn.filePathTemplate (per-column, highest priority)
  2. Entity-level: EntityMetadata.filePathTemplate (default for all file columns in the view)
  3. Workspace-level: S3Config.filePathTemplate (workspace default)
  4. No template: managed mode (SchemaStack stores files directly)

Resolution order is in TransformContext.getEffectiveTemplate().

Query pipeline: Templates flow from ViewColumn entity → MetadataQueryBuilderQueryColumnTransformContextFileValueTransformer / ImageValueTransformer.

Download endpoint: The FileResource detects mapped files by the mapped: prefix in the storage key query parameter, then calls S3FileStorageProvider.retrieveExternal() which skips the workspace UUID prefix to access customer-managed S3 keys directly.

Used for: bulk exports, image/file uploads, mapped file access to customer S3 buckets.

Feedback

Simple in-app feedback submission (FeedbackService / FeedbackResource). Users submit feedback from the platform, stored in the metadata database.

See Modules for the full module breakdown and System Architecture for deployment and messaging details.

SchemaStack Internal Developer Documentation