PostgreSQL Persistence with Prisma ORM
Date: 2026-03-16 Status: accepted
Context
The project had no persistent database — all services used in-memory storage as a placeholder (listed as a Known Gap). A user profile system was needed to store application-specific data beyond what Keycloak manages, establishing the database layer for future features.
Decision
-
PostgreSQL as the relational database — already running in docker-compose for Keycloak. We reuse the same PostgreSQL instance with a separate
luckyplansdatabase for application data (no second container). -
Prisma ORM in
packages/prisma— a dedicated workspace package that owns the schema, migrations, and generated client. This keeps ORM concerns (schema, migrations, query engine binaries) isolated frompackages/shared(types and utilities). -
Lazy profile creation — profiles are auto-created on first
mequery using session data (userId, email, name). This avoids coupling profile creation to the registration flow and ensures every authenticated user gets a profile transparently. -
service-coreowns all CRUD — per the functional decomposition pattern, profile operations live inservice-corealongside items. The gateway communicates via Redis message patterns as usual.
Consequences
Easier:
- Adding new entities with persistence — follow the Profile pattern with Prisma models
- Type safety across the stack — Prisma generates TypeScript types from the schema
- Database migrations are tracked and versioned in
packages/prisma/prisma/migrations/
Harder:
- Local setup requires
docker compose up -dto start thepostgresql-appcontainer (auto-creates theluckyplansdatabase) - Docker images for
service-coremust include Prisma query engine binaries for the Alpine target DATABASE_URLmust be configured as a secret in all environments