Skip to Content
🎉 TestoQA 1.0 is released

Data Flow

1. Data Flow Principles

TestoQA is server-first. The database is the source of truth, and all tenant-scoped reads/writes occur on the server.

Core principles

  • Boundaries validate and contextualize: Input validation + RequestContext resolution occur before any work.
  • Authorization before data access: Permission checks must occur before reading or mutating tenant data.
  • Tenant scoping is mandatory: All tenant-scoped data access must include projectId.
  • DTOs cross boundaries: UI receives controlled shapes; persistence models are not treated as API contracts.
  • No client trust: Client state can request actions but cannot assert identity, tenant, or permissions.

System-wide invariants are defined in system-overview.mdx. This document focuses on the shape of read/write flows and the boundaries where data transforms.


2. Read Flow

A read flow is any operation that retrieves data to render UI or return a response.

Typical read flow (tenant-scoped)

UI (Client/Server Components) → Boundary (Server Component data fetch OR Server Action/Route Handler) → Resolve session (user identity) → Resolve RequestContext (userId + projectId) → Authorize (read permission) → Service (business interpretation / orchestration) → Repository (tenant-scoped query) → Prisma → PostgreSQL → Map result to DTO → Response (render or JSON)

Where reads happen

Reads may be initiated from:

  • Server Components (SSR data fetching patterns)
  • Route Handlers (API-like reads)
  • Server Actions (occasionally used for reads, but typically writes)

Regardless of entry point, the same invariant holds:

Tenant context + authorization must be enforced before tenant data is returned.

Read transformation boundaries

  • Repository layer: returns persistence objects (or internal shapes)
  • Service layer: maps to DTOs and applies business interpretation
  • Boundary/UI layer: consumes DTOs only

3. Write Flow

A write flow is any operation that mutates state or triggers a domain workflow.

Typical write flow (tenant-scoped)

UI (Client Component interaction) → Server Action / Route Handler (boundary) → Validate input (schema) → Resolve session → Resolve RequestContext (userId + projectId) → Authorize (create/update/delete/execute) → Service (workflow + rules) → Repository (tenant-scoped mutation) → Prisma → PostgreSQL (transaction if needed) → Optional side-effects (events/notifications) → Map to DTO → Response (success/failure)

Write guarantees

  • Writes must never occur without an explicit projectId.
  • Writes must not rely on client-provided tenant IDs unless verified via membership + context resolution.
  • Authorization must be checked before:
    • reading any resource used to determine the mutation
    • executing the mutation itself

Transactions and multi-step workflows

When a workflow requires multiple database operations:

  • Services may coordinate a transaction boundary (implementation detail)
  • The architectural requirement is that multi-step writes must be:
    • consistent
    • tenant-scoped
    • safe under concurrency

4. Validation and Transformation Boundaries

Validation and transformation prevent untrusted input or persistence details from leaking across layers.

Input validation (required at boundaries)

  • All boundary inputs must be validated against schemas.
  • Validated inputs should be treated as the only “trusted” request payload.
  • DTOs define what flows upward to the UI.
  • DTOs prevent accidental exposure of:
    • internal identifiers that shouldn’t be shown
    • cross-tenant fields
    • persistence-specific fields
    • security-sensitive metadata

Where mapping should occur

  • Prefer mapping in services (business-owned shaping)
  • Repositories should avoid returning raw persistence models directly to UI/boundary

5. File Upload and Binary Data Flow

Uploads are treated as untrusted inputs and require special handling.

Upload flow (high-level)

  1. Client initiates upload.
  2. Server validates:
    • authentication
    • tenant context (projectId)
    • authorization to upload within that tenant
    • file metadata constraints (size/type)
  3. Client uploads file content to storage via an upload gateway/provider.
  4. Server persists upload metadata scoped by projectId.
  5. Access/serving is controlled by tenant + authorization checks.

Access control rules

  • Private files must only be retrievable by authorized users within the same tenant.
  • Public exposure is explicit and reviewed.
  • File paths, names, and metadata must be sanitized.

Safety notes

  • Treat file content as hostile.
  • Do not log raw file content or sensitive metadata.
  • Virus scanning is not assumed unless explicitly implemented.

6. Realtime Event Flow

Realtime behavior (e.g., execution updates, run status changes) should be modeled as events emitted by server-side workflows.

Event flow (conceptual)

Domain Service completes a state change → Emits domain event (internal concept) → Integration publishes to realtime provider (e.g., Pusher) → Clients subscribed to project-scoped channels receive updates

Tenant-scoped channels

Realtime channels must be scoped by:

  • projectId (always)
  • and user scope when needed for sensitive updates

Rule: A client must not be able to subscribe to events for a tenant they do not belong to.

Authorization for subscription or channel naming is part of the realtime integration boundary.


7. Caching and Revalidation

Caching in a multi-tenant system is dangerous if not explicitly scoped.

Default posture

  • Prefer no caching (no-store) for authorization-dependent and tenant-scoped reads unless a safe strategy is proven.

If caching is used

Cache keys must include:

  • tenant scope (projectId)
  • user scope if results vary by permissions
  • any other condition that changes the output

Revalidation rules

  • Mutations should invalidate or revalidate relevant reads.
  • Revalidation must not cause cross-tenant pollution (e.g., revalidating a shared path that serves different tenant content without scoping).

8. Data Flow Failure Modes

Data flow failures must fail safely without leaking cross-tenant information.

Validation failures

  • Return a controlled validation response
  • Do not proceed to service/repository calls

Authentication failures

  • Treat as unauthenticated (401/redirect semantics)
  • No tenant data must be accessed or revealed

Authorization failures

  • Return a forbidden response (403 or equivalent)
  • Prefer not to reveal whether a resource exists in a different tenant

Tenant resolution failures

  • Fail fast before any data access
  • Treat as mis-scoped or invalid request

External integration failures

  • Handle explicitly (timeouts/retries where safe)
  • Avoid logging sensitive payloads
  • Fail in a way that does not corrupt tenant state

9. Review Checklist (Data Flow)

  • Are inputs validated at the boundary?
  • Is RequestContext resolved (userId + projectId) before work?
  • Is authorization enforced before tenant data access?
  • Do repository queries/mutations enforce tenant scoping?
  • Are DTOs used to shape outputs to UI?
  • Are uploads treated as untrusted and access-controlled?
  • Are realtime channels/events tenant-scoped?
  • Is caching either disabled or explicitly scoped by tenant/user?
Last updated on