#Cascade Screening Cookbook

Version: 0.43.0 | Updated: 2026-03-28 | Applies to: ranvier-runtime 0.36+ | Category: Cookbook


#Overview

Cascade screening is a sequential filter pattern where a subject passes through multiple independent screens. Each screen can reject (short-circuit) or pass through. This pattern is ideal when:

  • Each check is independently valid
  • Early rejection saves downstream computation
  • The order of checks matters (cheapest/fastest first)
  • Each screen is independently testable and replaceable

#1. Pattern Anatomy

Input โ†’ Screen A โ†’ Screen B โ†’ Screen C โ†’ Screen D โ†’ Approved
           โ†“           โ†“           โ†“           โ†“
        Reject A    Reject B    Reject C    Reject D

Key characteristics:

  • Fail-fast: First rejection short-circuits the entire pipeline
  • Independence: Each screen knows nothing about other screens
  • Ordering: Place cheapest/fastest checks first for efficiency
  • Auditability: Each screen logs its decision

#2. Applied Domain: AML/KYC Compliance

Four-stage compliance screening for new customers.

let screening = Axon::typed::<CustomerApplication, String>("kyc-screening")
    .then(sanctions_check)         // Screen 1: O(1) list lookup
    .then(pep_check)               // Screen 2: O(1) flag check
    .then(risk_scoring)            // Screen 3: O(n) algorithm
    .then(document_verification);  // Screen 4: expensive external API

// Screen order matters: sanctions check is instant, document
// verification calls an external API. Ordering cheapest-first
// avoids unnecessary API calls for sanctioned applicants.

#Each screen as a Transition

#[transition]
async fn sanctions_check(
    applicant: CustomerApplication,
) -> Outcome<CustomerApplication, String> {
    let sanctioned_countries = ["NK", "SY", "IR"];
    if sanctioned_countries.contains(&applicant.country.as_str()) {
        return Outcome::Fault(format!(
            "Screen 1 REJECTED: {} from sanctioned country {}",
            applicant.name, applicant.country
        ));
    }
    Outcome::Next(applicant)
}

#3. Applied Domain: Content Moderation

Three-stage content moderation pipeline.

let moderation = Axon::typed::<ContentSubmission, String>("content-moderation")
    .then(spam_filter)          // Fast regex/heuristic check
    .then(toxicity_classifier)  // ML model inference
    .then(policy_enforcer);     // Business rule evaluation

#Pattern insight

Spam filter (microseconds) runs before toxicity classifier (milliseconds). If content is obvious spam, the expensive ML model is never invoked.


#4. Applied Domain: Customs Declaration Screening

Multi-layer customs screening for import declarations.

let customs = Axon::typed::<ImportDeclaration, String>("customs-screening")
    .then(tariff_classification)    // Classify goods by HS code
    .then(restricted_goods_check)   // Check against restricted list
    .then(valuation_check)          // Verify declared value
    .then(origin_verification);     // Country of origin rules

#5. Combining with Outcome::Emit for Audit Trail

Each screen can emit an audit event while continuing the pipeline:

#[transition]
async fn pep_check(
    mut applicant: CustomerApplication,
    bus: &mut Bus,
) -> Outcome<CustomerApplication, String> {
    let is_pep = check_pep_database(&applicant.name).await;

    // Log the screen result to Bus for audit trail
    bus.insert(ScreenResult {
        screen: "pep",
        passed: !is_pep,
        timestamp: chrono::Utc::now(),
    });

    if is_pep {
        Outcome::Fault("PEP check: flagged as politically exposed".into())
    } else {
        Outcome::Next(applicant)
    }
}

#6. Difference from Rule Chain

Aspect Cascade Screening Rule Chain
Focus Filter/reject Evaluate/score
Rejection Domain-specific logic Generic threshold
Output Pass/reject decision Eligibility result
Ordering Performance-driven Logic-driven

Both use the same Axon mechanism (Outcome::Next / Outcome::Fault), but the intent is different. Screening emphasizes fail-fast filtering; rule chains emphasize comprehensive evaluation.


#Quick Reference

Start Template:  ranvier new my-app --template screening
Example:         cargo run -p cascade-screening
Pattern Tag:     screening

#See Also

  • cascade-screening example โ€” Pure Axon screening demo
  • compliance-demo example โ€” HTTP screening with Guards
  • cookbook_saga_compensation.md โ€” When screens trigger compensable actions