SaaS Foundation uses the repository pattern for database access. Route handlers
and services call repository functions instead of writing Prisma queries
directly.
Repository per entity
A repository usually represents one entity or closely related set of queries.
Examples include user, organization, member, invitation, and API key
repositories.
Repositories keep Prisma details in one layer. A route can focus on the request
and business rules without also owning filtering, sorting, pagination, or
database query construction.
Preserve portal boundaries
Admin and customer repositories remain separate even when two functions happen
to look similar.
- Admin repositories may perform platform-wide queries without implicit
organization scoping.
- Customer repositories query data within the authenticated organization.
- Auth repositories support authentication and invitation flows outside a
portal request.
This duplication is intentional. Combining an admin query and customer query
because they currently use similar Prisma code creates an easy path for
platform-wide access to leak into customer code later.
Scope customer queries
Customer repository functions that access organization-owned data receive the
authenticated organization ID. They include it in the database filter alongside
the requested record ID or other filters.
The withCustomer route wrapper reads the active organization from the session,
verifies the user’s membership, and passes the trusted organizationId into the
handler. The route then passes it to customer repositories.
Do not accept the tenant boundary from an untrusted request body. Use the
organization ID established by the authenticated customer context.
Keep responsibilities clear
Repositories perform data access. Route handlers remain responsible for request
validation, authorization, existence checks, and business rules unless a more
complex rule is deliberately extracted into a service or validator.