Appearance
System Architecture
SchemaStack is a hybrid Quarkus + Spring Boot platform. The split exists because Hibernate Reactive (used in Quarkus for non-blocking I/O) cannot do programmatic DDL generation — Spring Boot with classic Hibernate handles that part.
Deployment Model
The platform is designed for two deployment targets:
- AWS Lambda — stateless REST APIs (metadata-rest, organisation-rest). Cold start optimized via Quarkus native builds.
- AWS Fargate — long-running services (consumers, producers, SSE endpoints, Spring Boot processor). These maintain connections and state.
INFO
Infrastructure-as-code (CDK/SAM) is not in this repository. The codebase is Lambda-capable (quarkus-amazon-lambda-http dependency) but deployment automation lives separately.
Service Architecture
┌─────────────────────────────────────────────────────────┐
│ Clients │
│ (Browser / API consumers) │
└──────────┬─────────────────────────────┬────────────────┘
│ REST │ SSE
▼ ▼
┌──────────────────┐ ┌──────────────────┐
│ Quarkus REST │ │ Quarkus SSE │
│ (Lambda) │ │ (Fargate) │
│ - metadata-rest │ │ - metadata-sse │
│ - org-rest │ │ - org-sse │
└────────┬─────────┘ └────────▲─────────┘
│ │
│ RabbitMQ │ RabbitMQ broadcast
▼ │
┌──────────────────┐ ┌──────────────────┐
│ Quarkus │ │ Quarkus │
│ Consumers │──────────│ Producers │
│ (Fargate) │ │ (Fargate) │
└────────┬─────────┘ └──────────────────┘
│ RabbitMQ
▼
┌──────────────────────────────────────┐
│ Spring Boot Services (Fargate) │
│ - processor (DDL/Flyway) │
│ - workspace-api (dynamic CRUD) │
│ - session (session factory) │
└────────┬─────────────────────────────┘
│
▼
┌──────────────────┐
│ PostgreSQL │
│ - dynamicdb │
│ - workspace DBs │
└──────────────────┘Messaging
All inter-service communication uses RabbitMQ (not SQS/SNS).
Transactional Outbox Pattern
Messages are not sent directly to RabbitMQ from within business transactions. Instead, they're persisted to the outbox_message table as part of the same database transaction, then published asynchronously by a poller.
Business Transaction
│
├── 1. DB writes (entity changes)
├── 2. OutboxService.queueMessage() → inserts OutboxMessage (PENDING)
└── COMMIT
│
OutboxPoller (every 5s)
├── 3. Polls PENDING messages (batch of 100)
├── 4. Marks PROCESSING
├── 5. basicPublish() to RabbitMQ
└── 6. Marks SENT (or FAILED after 5 retries)OutboxMessage fields: messageId (UUID, dedup key), aggregateType + aggregateId, exchange, routingKey, payload (JSONB), status, retryCount.
OutboxPoller scheduled jobs:
- Every 5s: poll and publish (batch of 100, max 5 retries per message)
- Every 1m: reset stuck messages (PROCESSING > 5 minutes → back to PENDING)
- Every 1h: cleanup old SENT messages (> 7 days)
Currently used for sync operation completions and task completion notifications via OutboxService.queueSyncCompletionMessage() and queueTaskCompletionMessage().
MessageBus (Idempotency + Audit)
For direct RabbitMQ publishing (non-outbox), the MessageBus provides a middleware chain inspired by Symfony Messenger:
- Idempotency check — queries
message_audittable; skips if already COMPLETED or PROCESSING - Audit record — persists a
MessageAuditwith PENDING status, full JSON payload, and optional metadata (workspaceId, entityType, entityId, operation) - Send with retry — publishes via SmallRye emitter with up to 3 retries
- Audit update — marks COMPLETED on success, FAILED → DEAD_LETTER on exhausted retries
MessageAudit statuses: PENDING → PROCESSING → COMPLETED | FAILED | DEAD_LETTER.
Exchanges
Key exchanges:
| Exchange | Type | Flow |
|---|---|---|
metadata-updates | direct | Quarkus REST → Spring Boot processor |
metadata-updates-retry | direct | Processor retry with TTL |
metadata-updates-dlx | direct | Processor dead letter exchange |
task-completion | direct | Processor → Quarkus consumers |
task-completion-retry | direct | Consumer retry with TTL |
task-completion-dlx | direct | Consumer dead letter exchange |
workspace-events | direct | Workspace event broadcasts |
bulk-actions | direct | Bulk operation events |
sync-operation | direct | Schema sync operations |
email-exchange | direct | Email sending |
member-events-exchange | direct | Organisation member change events |
Consumers use single-active-consumer pattern with RabbitMQ broadcast mode for fan-out to SSE producers.
Infrastructure Dependencies
| Dependency | Usage |
|---|---|
| PostgreSQL | Primary database. dynamicdb for metadata/org data; per-workspace schemas created by processor |
| RabbitMQ | All async messaging. Durable queues, dead-letter exchanges, retry with TTL |
| S3 / S3-compatible | File storage (uploads, exports, mapped files). Per-workspace config with encrypted credentials. Required in production — local filesystem is dev-only |
Not used (despite some docs mentioning it)
Redis is not in the codebase.
Resilience
The Spring Boot processor has Resilience4j as a dependency with configuration for:
- Circuit breakers —
migrationService(50% failure threshold, 10-call window) andworkspaceConnection(50%, 5-call window) - Retry — max 3 attempts with exponential backoff for both services
- Rate limiter —
migrationServiceat 10 calls/second
Configuration in processor-service/src/main/resources/application.properties.
WARNING
The Resilience4j dependency and configuration exist, but @CircuitBreaker, @Retry, and @RateLimiter annotations are not yet applied to service methods. The protection is configured but not active.
SSE (Server-Sent Events)
Real-time updates use SSE endpoints in Quarkus:
metadata-sse— task completion events, workspace data changesorganisation-sse— org/member change events
SSE resources maintain a ConcurrentMap<String, Set<SseEventSink>> for active connections, filtered by workspace. Events flow from RabbitMQ consumer → broadcast → SSE endpoint → browser.
Email System
Email delivery is asynchronous via RabbitMQ. Three email types are supported:
| Type | Expiry | Trigger |
|---|---|---|
VERIFICATION | 24 hours | User registration |
PASSWORD_RESET | 1 hour | Forgot password |
INVITATION | 2 days | Member invitation |
Flow: Service → EmailEventProducer → RabbitMQ (email-exchange) → EmailEventConsumer → EmailService → AWS SES
The EmailService uses Quarkus ReactiveMailer backed by AWS SES SMTP in production. In dev mode (mailer.dev.enabled=true), all emails are redirected to a single address with a "[DEV REDIRECT]" subject prefix.
Email links point to the frontend: {app.frontend.url}/auth/verify-email?token={token}, {app.frontend.url}/auth/reset-password?token={token}, {app.frontend.url}/auth/accept-invitation?token={token}.
Subscription Tiers & Usage Tracking
Each organisation has a subscription tier that defines limits:
| Limit | Example |
|---|---|
maxWorkspaces | null = unlimited |
maxViewsPerWorkspace | — |
maxMembers | — |
monthlyRequestLimit | — |
monthlyRowOperationLimit | — |
storageLimitMb | — |
rateLimitPerMinute | Default 60 |
Usage is tracked per workspace per billing period via UsagePeriod (request count, row read/write counts, storage bytes). The UsageService aggregates usage across all workspaces for the current billing period and returns a UsageSummaryDTO.