Skip to content

Work / DwarSeva / Wiki / Decisions

2026-10-23-infra-may-touch-prisma

Decisioncanonicalverified 2026-10-23

DECISION.2026-10-23.INFRA-MAY-TOUCH-PRISMA

Decision

Two infrastructure files may use PrismaService directly; nothing else outside a repository may. They are src/api/health/health.service.ts (the SELECT 1 connection ping) and src/shared/services/dataloader.service.ts (the GraphQL batch loaders). The only-repositories-touch-prisma rule in apps/server/.dependency-cruiser.cjs names exactly these two files.

Why

layered-architecture says no layer calls the database except a repository. That rule protects business logic: it keeps queries, soft-delete filtering and type conversion in one layer. Neither file holds business logic.

  • The health ping checks the connection itself, not any data. A repository for it would wrap one raw query in a class with no model.
  • The dataloader batches lookups across many models to stop N+1 queries in GraphQL. It is data access already; splitting it across every model's repository would rebuild it, not move it.

Options weighed:

  • Move both into repositories — rejected, for the reasons above.
  • Exempt src/shared/ as a whole — rejected: too wide, a future shared helper could query freely.
  • Name the two files — chosen. Any new exception needs its own decision.

The other two files the rule caught on 2026-10-23 held domain logic and were fixed instead: the push token pruner now goes through its repository, and the subscription scheduler — a second copy of SubscriptionPlanJobsService running at the same hour — was removed, its per-admin notices moved into the jobs service.

Impact

  • zeno check fails if any other non-repository file imports PrismaService.
  • Adding a third exception means editing this page and the rule together.

Status

Active

Sources

  • apps/server/.dependency-cruiser.cjs — rule only-repositories-touch-prisma
  • apps/server/src/api/health/health.service.ts
  • apps/server/src/shared/services/dataloader.service.ts