Skip to main content

Infrastructure

The backend depends on several platform services, but it still runs as one Spring Boot application. This page covers the backend-facing runtime contracts that shape code behavior. It intentionally stays out of cluster operations and day-2 runbooks.

This page is a reference document.

Source Paths

PathRole
app/src/main/resources/application.yamlMain runtime config: data sources, Flyway, Redis, RabbitMQ, MinIO, worker URLs, CORS, rate limits, OpenAPI, actuator
app/src/main/resources/application-dev.ymlDev-only overrides such as cookie SameSite
app/src/main/java/com/lumie/app/config/RoutingDataSourceConfig.javaPrimary and readonly Hikari pools plus Flyway migrator data source
app/src/main/java/com/lumie/app/config/RuntimeDbRoleGuard.javaStartup guard against RLS-bypassing DB roles
app/src/main/java/com/lumie/app/config/ShedLockConfig.javaDistributed scheduler locking
app/src/main/java/com/lumie/app/config/CorsConfig.javaCORS contract
app/src/main/java/com/lumie/app/config/ratelimit/RateLimitFilter.javaIn-process rate limiting
modules/exam/src/main/java/com/lumie/exam/adapter/out/config/*RabbitMQ, RestClient, and MinIO infrastructure owned by the exam module
modules/auth/src/main/java/com/lumie/auth/adapter/out/persistence/RedisTokenRepository.javaAuth's Redis persistence contract
modules/file/src/main/java/com/lumie/file/adapter/out/storage/MinioStorageAdapter.javaFile-service object storage contract
modules/billing/src/main/java/com/lumie/billing/adapter/out/external/*Billing's external provider adapters

Runtime Topology

Dependency Contracts

DependencyConfig surfaceMain backend ownersContract notes
PostgreSQLapp.datasource.*, spring.flyway.*all modulesShared-schema RLS, Modulith outbox, ShedLock, primary plus readonly pools
RabbitMQspring.rabbitmq.*mainly examQueue-backed grading and report workflows
Redisspring.data.redis.*mainly authRefresh tokens, blacklist, and session index storage
MinIOminio.*exam, file, tenantOMR objects, presigned uploads, file metadata, tenant logos
grading-svclumie.services.grading.urlexamDirect HTTP grading plus queue callback ecosystem
report-svclumie.services.report.urlexamDirect HTTP report generation plus queue callback ecosystem
chatbot-svclumie.services.chatbot.urlaiHTTP streaming proxy out, HMAC callback surface back in
Toss Paymentsbilling config plus provider secretsbillingImplemented HTTP integration
Popbill tax invoice pathbilling config plus provider secretsbillingCurrent code is still a stubbed adapter, not a full live integration

Database And Migration Infrastructure

Read/write split

RoutingDataSourceConfig declares:

  • app.datasource.primary.*
  • app.datasource.readonly.*
  • a RoutingDataSource wrapped in LazyConnectionDataSourceProxy

Routing rule:

  • @Transactional(readOnly = true) -> readonly pool
  • write transaction or non-transactional access -> primary pool

Flyway runs with separate credentials

Flyway does not use the runtime application pool credentials. The backend creates a dedicated @FlywayDataSource from the primary URL plus spring.flyway.user/password, so DDL can run with table-owning privileges while runtime traffic stays on the restricted lumie_app role.

Runtime DB role guard

RuntimeDbRoleGuard fails startup if the active runtime role is:

  • SUPERUSER
  • BYPASSRLS

That is a backend safety invariant, not just an operations preference.

Async And Scheduler Infrastructure

  • Spring Modulith stores outbox rows in public.event_publication
  • spring.modulith.events.completion-mode=delete
  • spring.modulith.events.republish-outstanding-events-on-restart=true
  • ShedLock uses public.shedlock
  • @EnableAsync is on the application, and Spring-managed async execution gets the tenant-aware task decorator

Important boundary detail:

  • Spring-managed async work gets context propagation automatically
  • custom executors, such as the AI module's dedicated chat executor, still have to re-establish context manually

Object Storage And External HTTP

MinIO

  • exam uses two MinIO clients: an internal endpoint client and a presign client using minio.external-endpoint
  • file uses MinIO for uploads, downloads, and presigned URLs
  • tenant uses MinIO for logo objects
  • FileServiceAdapter deletes MinIO objects only after database commit

Worker and provider HTTP

  • exam uses RestClient with external-call logging and explicit timeouts
  • OmrServiceClient overrides the read timeout to 90s
  • ai pins chatbot-svc traffic to HTTP/1.1 for uvicorn compatibility
  • billing provider calls are intentionally kept outside long transactions

HTTP Surface And Backend Guards

From application.yaml and app config:

  • OpenAPI: /v3/api-docs
  • Swagger UI: /swagger-ui.html
  • CORS origins: production, dev, and local http://localhost:3000
  • multipart limit: 5MB per file, 25MB per request
  • graceful shutdown timeout: 30s
  • actuator exposure: health, info, prometheus, metrics
  • per-IP in-process rate limiting on selected POST routes

RequestContextFilter also provides:

  • generated or forwarded X-Request-Id
  • MDC population for request ID, tenant, user, and optional trace ID

Current Drift And Caveats

  • The billing module contains a real Toss integration, but PopbillTaxInvoiceClient is still a stub that returns a synthetic success response and logs a warning.
  • RabbitMQ topology is not declared in backend Java config; backend code assumes the queues and policies exist and only wires the application-layer beans.

Verification Commands

cd /Users/bluemayne/Projects/Lumie/lumie-backend
./gradlew :app:test
./gradlew :modules:exam:test
./gradlew :modules:auth:test
./gradlew integrationTest

Most relevant tests:

  • app/src/test/java/com/lumie/app/config/RoutingDataSourceIntegrationTest.java
  • app/src/test/java/com/lumie/app/config/RuntimeDbRoleGuardTest.java
  • app/src/test/java/com/lumie/app/migration/MigrationsRlsIntegrationTest.java