Skip to content

Database Per Service Strategy ​

Phase: 1 Architecture Plan Status: pre-implementation contract

Principle ​

The target platform uses database-per-service. The legacy Prisma database is the source for audit, migration, and parity checks, but it is not a shared runtime database for new Go services.

Database Naming ​

ServiceDatabaseInitial Migration Mode
iam-servicehoctapaz_iam_dbBackfill users' auth material and refresh tokens after contract lock
profile-servicehoctapaz_profile_dbBackfill profiles and KYC after auth identity mapping
school-servicehoctapaz_school_dbBackfill organizations and IAM-synchronized membership read models
classroom-servicehoctapaz_classroom_dbBackfill after school IDs are mapped
course-servicehoctapaz_course_dbBackfill after document/question references are stable
document-servicehoctapaz_document_dbEarly backfill for media assets and teaching documents
docx-import-servicehoctapaz_import_dbEarly native job schema; legacy import rows read-only for parity
ai-classifier-servicehoctapaz_ai_classifier_dbBackfill jobs/provider settings after question IDs are mapped
question-bank-servicehoctapaz_question_bank_dbBackfill question types, taxonomy, questions, versions, options
exam-servicehoctapaz_exam_dbBackfill exams/templates/snapshots after questions are mapped
attempt-servicehoctapaz_attempt_dbBackfill only after exam snapshot contract is frozen
notification-servicehoctapaz_notification_dbRebuild from canonical events where possible
analytics-servicehoctapaz_analytics_dbRebuild read models from event/backfill jobs
admin-servicehoctapaz_admin_dbBackfill feature maintenance, audit logs, support tickets
payment-servicehoctapaz_payment_dbBackfill/reconcile provider payment orders and idempotency records
wallet-servicehoctapaz_wallet_dbBackfill AZ Credit ledger only after billing/payment event policy is locked
billing-servicehoctapaz_billing_dbBackfill plans, subscriptions, invoices, and entitlement snapshots
usage-servicehoctapaz_usage_dbRebuild quotas, feature gates, and usage counters from billing/import events
search-servicehoctapaz_search_dbRebuild copied indexes from owner-service projections
audit-servicehoctapaz_audit_dbBackfill append-only security/compliance event history

Shared Reference Data ​

Taxonomy tables such as Subject, Chapter, Topic, EducationLevel, Grade, Curriculum, ExamTrack, QuestionSource, and DifficultyLevel are shared in legacy. Initial owner is question-bank-service unless later product analysis moves school-specific grade/curriculum references into school-service.

Services that need taxonomy use:

  • local replicated read model, or
  • query API from question-bank-service.

They must not join the question-bank database directly.

ID Strategy ​

  • Preserve legacy IDs in migrated records as legacy_id.
  • Native primary keys may remain UUID/string-compatible if existing frontend contracts expose IDs.
  • Every migrated table needs a unique index on legacy_id when legacy data is backfilled.
  • Cross-service references use the target service's public ID plus optional legacy_id for validation reports.

Migrations ​

Each service owns:

  • migrations/ SQL consumed by tools/migrator with schema-up --service=<service>.
  • queries/ using sqlc.
  • internal/store or equivalent storage adapter.

Migration rules:

  • One service migration cannot create tables in another service database.
  • No migration reads the legacy database directly.
  • Backfill jobs live outside schema migration commands.
  • Applied schema files are tracked in each service database with schema_migrations(service, filename, checksum, applied_at).
  • Schema migrations must be forward-only in CI; rollback scripts may exist for local and emergency recovery.
  • make test-compose-migrations must stay green after runtime changes; it verifies every active service has a local Compose runtime entry and that DB-backed services use service-owned runtime and migration DSNs.
  • Compose initialization revokes PostgreSQL CONNECT and TEMPORARY from PUBLIC for every service database before granting the owner role access. make test-postgres-database-isolation starts an independently named, portless PostgreSQL container, proves all 20 owner roles can connect to their own database, checks all 380 cross-service CONNECT pairs are denied, and verifies an actual denied connection for every role. It removes only that test container when complete.

Transactions ​

  • Transactions stay inside one service database.
  • Cross-service workflow uses events and idempotency keys.
  • Gateway adapters must not wrap multiple services in a fake distributed transaction.
  • Import approval must be designed as a saga:
    1. lock import job/draft
    2. create questions or exam draft through owning services
    3. write approval result
    4. publish events
    5. compensate only with explicit domain actions, not hidden deletes

JSON Snapshot Fields ​

Keep JSON snapshot fields where they preserve product behavior:

  • question rich content
  • question scoring rules
  • exam question snapshots
  • attempt question snapshots
  • import parse results and warnings
  • provider/raw OCR metadata where needed for troubleshooting

Do not normalize snapshots away in Phase 2. That would change exam/attempt behavior.

Legacy Read Adapters ​

Allowed:

  • offline inventory scripts
  • parity tests
  • backfill jobs
  • audit reports

Not allowed:

  • native service write path depending on legacy DB joins
  • silent runtime fallback to legacy DB after a route has been declared migrated

Go-platform documentation is generated from repository Markdown.