Skip to content

Why have procedure, service and query layers in such a small API?

1 min read · main part only; folded sections are optional

After this you can answer: Why have procedure, service and query layers in such a small API?

A booking procedure derives the user from the authenticated session, enforces its rate policy through a service, and calls the booking service. It does not accept a caller-supplied owner. The service distinguishes a valid retry from reuse of a key for different booking parameters, then invalidates event caches after a new commit.

The query performs the seat checks, decrement, booking insert and outbox insert in one Postgres transaction. Although the checks express business rules, moving them outside the locked transaction would introduce a race. The exception protects a stronger rule: the data checked must be the data changed.

The useful review question is who owns each decision and side effect. Adding a generic repository interface to every file would add indirection without improving this boundary.

What changes when HTTP is replaced?
The service can still be called by a script or another transport. Cookie handling and tRPC error mapping stay at the request boundary.
What failure would a misplaced check create?
Checking seats in a service, then updating later in a separate query, lets another transaction take the seats between those actions. A clean folder structure cannot repair that race.
Would you mock every layer?
No. Unit tests can isolate service decisions, while database-backed checks should exercise the transaction and constraints. A mock returning the expected row cannot prove locking or rollback.

Read Layers, then inspect the api-app piece →.

apps/api/src/queries/bookings.ts → createBookingTx

Read this book online: https://book.theversehub.in/