Product Provisioning Fan-Out

Phase 5 of the main workflow — multiple product child workflows running concurrently, all joined by Promise.allOf().


Table of Contents

  1. 11.3 Product Provisioning Orchestrator
  2. How the Fan-Out Works
  3. Product Gating by Segment

11.3 Product Provisioning Orchestrator

%%{init: {'theme': 'base'}}%%
flowchart TD
    classDef startEnd   fill:#2d6a4f,stroke:#1b4332,color:#ffffff,font-weight:bold
    classDef childWf    fill:#5c4a8a,stroke:#c77dff,color:#ffffff
    classDef childWfHuman fill:#e76f51,stroke:#bc4749,color:#ffffff,font-weight:bold
    classDef gateway    fill:#457b9d,stroke:#1d3557,color:#ffffff
    classDef more       fill:#6c757d,stroke:#495057,color:#ffffff

    FanOut(["▶  ProductProvisioningOrchestrator"]):::startEnd

    WireCheck{"Wire\nselected?"}:::gateway
    WireWF["WireDomesticWorkflow\n⏳ Human task — Operations review\n⏱ Durable activation timer"]:::childWfHuman

    ACHCheck{"ACH\nselected?"}:::gateway
    ACHWF["ACHOriginationWorkflow\nTreasury Platform entitlement"]:::childWf

    ARPCheck{"Positive Pay\nselected?"}:::gateway
    ARPWF["PositivePayWorkflow\n⏳ Human task — specialist review"]:::childWfHuman

    RTPCheck{"RTP — Instant Payments\nselected?\nEnterprise only"}:::gateway
    RTPWF["RTPProvisioningWorkflow\nInstant Payments Network\nregistration via adapter"]:::childWf

    MoreDots["⋯ Additional product\nchild workflows\nall run concurrently"]:::more

    Join(["■  Promise.allOf() — all complete"]):::startEnd

    FanOut --> WireCheck & ACHCheck & ARPCheck & RTPCheck & MoreDots
    WireCheck -- "Yes" --> WireWF --> Join
    WireCheck -- "No" --> Join
    ACHCheck -- "Yes" --> ACHWF --> Join
    ACHCheck -- "No" --> Join
    ARPCheck -- "Yes" --> ARPWF --> Join
    ARPCheck -- "No" --> Join
    RTPCheck -- "Yes" --> RTPWF --> Join
    RTPCheck -- "No" --> Join
    MoreDots --> Join
Node key     🟪 Automated child workflow  ·  🟧 Child workflow with embedded human task  ·  🔷 Product selection gate  ·  ⬜ Remaining product workflows

How the Fan-Out Works

The ProductProvisioningOrchestrator evaluates the case’s product selections against the segment’s available products. For each selected product:

  1. A check confirms the product is present in the case’s product list
  2. If yes, a child workflow is started asynchronously
  3. All child workflows run concurrently — they do not wait for each other
  4. Promise.allOf(childPromises) blocks the orchestrator until every started child workflow completes
// Simplified orchestrator logic
List<Promise<Void>> promises = new ArrayList<>();

if (config.availableProductCodes().contains("WIRE_DOMESTIC")
        && case.hasProduct("WIRE_DOMESTIC")) {
    promises.add(Async.procedure(
        wireDomesticWorkflow::execute, caseId, productSpec
    ));
}
// ... repeat for each product in the catalog ...

Promise.allOf(promises.toArray(new Promise[0])).get();
// All products provisioned — return to parent workflow

Product Gating by Segment

The orchestrator only starts child workflows for products available to the case’s segment. The WorkflowConfig.availableProductCodes() set controls this — it is computed once at workflow start from the clientSegment flag:

Segment Products Available Workflows That Can Start
SMALL 10 products Up to 10 child workflows
MEDIUM 14 products Up to 14 child workflows
ENTERPRISE Full catalog Up to the full product catalog in parallel

If a case has clientSegment = SMALL and the intake form mistakenly included an Enterprise-only product, the orchestrator’s product gate prevents that child workflow from starting — the segment config is the authoritative source of truth, not the intake form.

See Product Catalog for the full product catalog.


↑ Back to top

Anchita Platform — Fictional Reference Architecture for Cloud-Native Institutional Banking