Workflow Architecture
Technical reference for NIVAA's shared workflow pattern. This page explains the contract used by active workflow domains, the file layout each module follows, and how routes, projections, notifications, and state transitions are expected to interact.
What counts as a workflow module
A workflow module owns a domain aggregate plus the rules that decide what may happen next. In NIVAA that means the module is responsible for aggregate loading, pure rule guards, explicit commands, read projections, and effect packaging. Route handlers should become thin adapters around these modules.
Shared Contract
| Layer | Canonical location | Responsibility | Must not do |
|---|---|---|---|
| Contract types | workers_nivaa/src/lib/workflows/core/contracts.ts | Defines actor/context/result/event/notification shapes shared across domains. | Must not contain domain-specific branching. |
| Aggregate + repository | <module>/aggregate.ts + <module>/repository.ts | Loads the full domain state needed for one workflow decision. | Must not mutate data during reads. |
| Guards | <module>/guards.ts | Pure rule checks, state normalization, and precondition evaluation. | Must not execute writes or notifications. |
| Commands | <module>/commands.ts | Explicit state transitions such as create/apply/approve/cancel/convert. | Must not hide transitions behind route conditionals. |
| Projections | <module>/projections.ts | Read-model shaping for UI/API consumers. | Must not perform hidden writes or expiry side effects. |
| Effects | <module>/effects.ts | Builds workflow metadata, audit payloads, and notification event specs. | Must not re-implement domain policy already owned by commands. |
| Scheduler | session-workflow/scheduler.ts only where needed | Owns timed sweeps such as expiry/reminder processing. | Must not be replaced with write-on-read helpers. |
Active Workflow Domains
| Workflow | Code location | Primary purpose | Primary impacted routes / surfaces | Primary data touched |
|---|---|---|---|---|
| Session workflow | workers_nivaa/src/lib/workflows/session-workflow/ | Own appointment lifecycle, reschedule negotiation, check-in/out, parent approval/dispute, and expiry/reminders. | /appointments/*, /reschedule/*, tutor + parent session surfaces, check-in services, scheduler sweeps | appointments, reschedule_requests, discrepancies, package ledgers, user notifications` |
| Tutor onboarding | workers_nivaa/src/lib/workflows/tutor-onboarding/ | Normalize tutor onboarding state and capability projection for workspace/postings access. | /intake/tutor, /admin/tutors/:id/onboarding, tutor layout, register/setup, Telegram tutor gating | users, tutor_profiles |
| Family onboarding | workers_nivaa/src/lib/workflows/family-onboarding/ | Determine whether a parent/child set is ready for posting and what the next onboarding step is. | /profile/parent/me, /children/overview, /postings, parent/admin family surfaces | users, child_profiles, student_profiles |
| Posting workflow | workers_nivaa/src/lib/workflows/posting-workflow/ | Own posting creation, tutor applications, coordinator push, parent acceptance, and trial-match handoff. | /postings, coordinator posting views, parent posting views | postings, posting_applications, student_profiles, matches |
| Trial workflow | workers_nivaa/src/lib/workflows/trial-workflow/ | Own trial date negotiation, invoice/payment progression, post-trial feedback, and conversion to regular sessions. | /trial/*, parent posting detail, tutor student flows, coordinator trial operations | matches, trial_date_proposals, trial_invoices, trial_feedback, appointments |
| Notification dispatch | workers_nivaa/src/lib/notifications/ | Centralize inbox reads and typed notification event delivery from domain workflows. | /notifications, workspace shell, notification center, workflow effect dispatch | user_notifications plus telegram delivery side effects` |
Execution Model
- 1 1. Route parses input: The HTTP route authenticates the request and parses payload/query inputs.
- 2 2. Aggregate loads once: The workflow repository loads the current domain state required for the operation.
- 3 3. Guards evaluate preconditions: The command checks access and lifecycle preconditions using pure guard functions.
- 4 4. Command performs the mutation: The command writes the authoritative state transition and returns a workflow result.
- 5 5. Effects package fanout: Audit metadata and notification event payloads are created from the workflow result.
- 6 6. Projections feed the UI: Read-only projections shape aggregate state into button/next-step/status payloads for web and Telegram consumers.
Non-Negotiable Rules
- Reads must stay read-only. The session workflow already moved expiry/reminders out of projection helpers and into the scheduler.
- One domain should have one place that decides whether a transition is valid. Routes should not duplicate domain policy.
- Impersonation, role adaptation, and transport quirks belong at the route/auth layer, not inside workflow guards.
- Notification fanout should emit from workflow effects or shared notification helpers, not from route-local ad hoc writes.
- If a domain is still route-owned, document it as route-owned rather than pretending it is fully workflowized.