Approval workflow app architecture
Approval workflow app architecture: the decisions that matter
The architecture of an approval workflow app comes down to 3 choices: approval chains come from tenant policy rows, separation of duties enforced in the database and escalation by a claimed job, not timers. Each is explained below with the alternatives that were rejected and what you accept by choosing it.
Last checked
Approval chains come from tenant policy rows
Situation: Every customer has different thresholds and approver roles.
Choice: Store amount bands and ordered steps as rows and select the chain with a pure function.
- Rejected: Hard-coded rules per customer: a deploy per change.
- Rejected: A general rules engine: too much surface for phase 1.
- Consequence: Admins change policy without a deploy.
- Consequence: Band overlap must be validated on write.
- Consequence: In-flight requests keep the chain they started with.
Separation of duties enforced in the database
Situation: An application-only check can be bypassed by a bug or an admin script.
Choice: A trigger refuses a decision by the requester, in addition to the service rule.
- Rejected: Service check only: bypassable.
- Rejected: Manual audit review: detects, does not prevent.
- Consequence: Self-approval is impossible by construction.
- Consequence: Tests must attack the database path directly.
- Consequence: Delegation rules must be modelled explicitly.
Escalation by a claimed job, not timers
Situation: Millions of open steps cannot each hold a timer.
Choice: Each step stores due_at; a cron job claims overdue pending steps with SKIP LOCKED.
- Rejected: Per-step timers: lost on restart.
- Rejected: External scheduler: new infrastructure.
- Consequence: Accuracy is the polling interval.
- Consequence: Idempotent by status predicate.
- Consequence: Cost is bounded by batch size.
How the data is indexed
Indexes are chosen from the questions the app asks most often. Each line below names a table and the question its main index answers.
- tenants: unique (slug) — tenant lookup by slug.
- memberships: unique (tenant_id, user_id) — one membership per user per tenant.
- invitations: unique (token_hash) — invite link lookup.
- requests: (tenant_id, status, created_at DESC) — lists by status, keyset paginated.
- approval_policies: (tenant_id, min_amount_minor) — pick the band for an amount.
- approval_steps: unique (request_id, step_no) — one row per step.
- approval_decisions: (request_id, decided_at) — decision history of a request.
- audit_log: (tenant_id, created_at DESC) — audit viewer, keyset paginated.
What Forge64 provides underneath
Forge64 matches your description to one of 25 reference architectures, writes the plan and data model into the project, generates the app, and runs a quality gate over the code before you see it. Each app gets its own Postgres database and sign-in with no per-user fee. Hosting on Forge Cloud and Forge Payments are rolling out.
This is a reference design for the category, not a promise that every item is generated today. Built-ins such as background jobs, file uploads and search are rolling out; email sent from your app is coming soon, so anything here that needs it is something you add yourself for now.
FAQ
- What is the most important architecture decision for an approval workflow app?
- Approval chains come from tenant policy rows. Store amount bands and ordered steps as rows and select the chain with a pure function.
- Do I have to make these decisions myself?
- No. Forge AI plans them from your description and writes the decisions into the project as architecture records you can read and change.
- What do the alternatives cost me?
- For example, for "Separation of duties enforced in the database": Service check only: bypassable. Manual audit review: detects, does not prevent.
Build your app with Forge64
One prompt to a running app with auth, database, hosting and payments. Start free, no credit card required.
Start building for free