Skip to main content

Approval Module

The approval module provides a complete workflow engine for building approval-based business processes. It supports visual flow design (React Flow compatible), multi-level approval chains, conditional branching, parallel approval, delegation, rollbacks, and transactional event publishing.

This category splits the module across five pages: this overview (enabling, wiring, configuration), RPC Resources, Flow Design, Instance Runtime, and Events & Integration.

Enabling the Module

Approval is an optional feature module. It is intentionally absent from the default vef.Run(...) boot graph, so applications that do not need workflow support do not register its API resources, CQRS handlers, engine, binding listener, or timeout scanners.

Enable it explicitly:

vef.Run(
vef.ApprovalModule,
app.Module,
)

Event Routing Prerequisite

Approval publishes approval.* events with event.WithTx, so every event type except approval.instance.binding_failed must resolve to a transactional transport. The module asserts this at boot via event.RouteInspector — a misconfigured route fails the application instead of degrading silently:

[vef.event]
default_transport = "memory"

[[vef.event.routing]]
pattern = "approval.*"
transports = ["outbox", "redis_stream"]

Since v0.38 the business projection no longer consumes lifecycle events (it converges from the durable apv_business_projection table — see Business-State Projection), so the module itself no longer requires a subscribable sink in the route: a bare ["outbox"] route satisfies the startup check. Add the configured outbox sink transport (memory single-node, redis_stream cross-node) alongside outbox — as in the example above — whenever the host subscribes to approval events via approval.SubscribeInstance or approval.BindCommand, because the outbox transport is publish-only and subscribers attach to the sink. See Event Bus for transports, routing semantics, and the outbox relay.

InstanceBindingFailedEvent is the exception to the transactional-route startup check: it is published by the eventual projection worker after the approval transaction has already committed.

Architecture Overview

Flow Category → Flow → Flow Version → Nodes + Edges

Instance → Tasks → Action Logs
ConceptTableDescription
Flow Categoryapv_flow_categoryHierarchical grouping of flows
Flowapv_flowA workflow definition (e.g., "Leave Request")
Flow Versionapv_flow_versionVersioned snapshot with nodes, edges, and form schema
Flow Nodeapv_flow_nodeA step in the workflow (approval, handle, condition, CC)
Flow Edgeapv_flow_edgeDirected connection between nodes
Instanceapv_instanceA running instance of a flow
Taskapv_taskAn individual approval/handle task assigned to a user
Action Logapv_action_logAudit trail of all actions
Business Projectionapv_business_projectionDurable desired-state convergence for business-bound flows (v0.38)

Configuration

[vef.approval]
auto_migrate = true
timeout_scan_interval = "1m"
pre_warning_scan_interval = "5m"
cleanup_scan_interval = "24h"
delegation_max_depth = 10
form_snapshot_retention = "2160h" # 90 days
urge_record_retention = "720h" # 30 days
cc_record_retention = "2160h" # 90 days

[vef.approval.business_binding]
consistency = "synchronous" # or "eventual"
scan_interval = "10s" # eventual worker cadence
batch_size = 100 # projections per scan

auto_migrate is a plain boolean switch and is not set by ApprovalConfig.ApplyDefaults(): enable it explicitly when the app should run approval DDL on startup. cc_record_retention only prunes CC records that have already been read.

business_binding (v0.38) controls how approval state is projected onto bound business tables: synchronous (default) writes the business row inside the approval transaction, eventual commits the desired state and lets a background worker converge the row (see Business-State Projection). An out-of-enum consistency or a negative worker setting fails config validation at startup (config.ErrInvalidApprovalBindingConsistency / ErrInvalidApprovalBusinessBindingWorkerConfig).

The outbox-related fields that previously lived under [vef.approval] (outbox_relay_interval, outbox_max_retries, outbox_batch_size) moved to [vef.event.transports.outbox] in v0.21. The approval module now publishes through the framework-wide outbox transport — see Event Bus.

See Configuration Reference for details.

Binding Modes

ModeConstantWire valueDescription
StandaloneBindingStandalonestandaloneForm data stored in the approval module's own tables
BusinessBindingBusinessbusinessLinks to an existing business data table

Business binding connects the approval flow to your domain tables via a single Flow.BusinessBinding document (approval.BusinessBindingConfig): tableName, composite keyColumns, statusColumn, the mandatory instanceIdColumn CAS fence, optional startedAtColumn / finishedAtColumn, and an optional statusMapping (see Business-State Projection). The binding is snapshotted onto each deployed flow version, and its state converges through the durable apv_business_projection table.


Next: RPC Resources for the API surface, or Flow Design for node types and the designer wire shapes.