LAB#05: Implementing Zone Access Control Logic

🎯 Learning Objectives

  • Understand different approaches to implementing complex business logic
  • Practice breaking down a decision tree into clean, maintainable code
  • Compare trade-offs between code styles (readability, testability, maintainability)
  • Learn how to separate business logic from UI in a React Native app

πŸ“‹ Scenario

You are building a wristband access control system for a large venue (festival, conference, stadium, etc.).

When a wristband is scanned at a checkpoint, the system must decide whether to GRANT or DENY access based on the flowchart provided.

Files:

πŸ”„ The Four Approaches

You will implement the same logic using four different styles:

  1. Nested If/Else (Simple but messy)
  2. Composable Pure Functions (Focused, testable functions.)
  3. Declarative Rules Array (Policy-based)
  4. State Machine (Conceptual / XState style)

πŸ“Œ Requirements

Create a function evaluateWristbandAccess(wristband, checkpoint) that returns:

type AccessResult = {
  granted: boolean;
  code: string;
  message: string;
  metadata?: Record<string, any>;
};

Core Rules (from flowchart):

  • If checkpoint.direction === 'OUT' β†’ Always GRANTED
  • If direction === 'IN':
    • Check if wristband.type is in checkpoint.allowedTypes
    • If not β†’ TYPE_NOT_ALLOWED
    • If yes β†’ Check Bypass Policy
      • If bypass active β†’ GRANTED
      • Else β†’ Check Zone Config
        • If no config β†’ ZONE_CONFIG_MISSING
        • If enterPermission === false β†’ NO_PERMISSION
        • Else β†’ Check Capacity
          • If full β†’ CAPACITY_FULL
          • Else β†’ GRANTED

πŸ› οΈ Exercise Tasks

Task 1: Nested If/Else Approach

Goal: Implement the logic using nested if/else statements.

Clues:

  • Start with the direction check (early exit)
  • Be careful with indentation
  • Try to avoid deep nesting where possible

Task 2: Composable Pure Functions

Goal: Break the logic into small, focused, testable functions.

Clues:

  • Create helper functions like:
    • isExit(checkpoint)
    • isTypeAllowed(wristband, allowedTypes)
    • hasActiveBypass(wristband)
    • getZoneConfig(zoneId) (async)
    • isCapacityAvailable(config)
  • Build a clean evaluateAccess orchestrator that calls them in sequence
  • Each function should do one thing

Task 3: Declarative Rules Array

Goal: Create a list of rules that are evaluated in order.

Clues:

const rules = [
  {
    condition: (w, c) => c.direction === 'OUT',
    result: () => ({ granted: true, code: 'GRANTED', message: 'Exit allowed' })
  },
  // ... more rules
];
  • Rules are evaluated from top to bottom (first match wins)
  • Good for non-developers to understand

Task 4: State Machine Style

Goal: Model the flowchart as a state machine (conceptual or using XState).

Clues:

  • States could be: scanned, checkingDirection, checkingType, checkingBypass, checkingCapacity, granted, denied
  • Transitions based on conditions
  • Excellent for complex flows with side effects

πŸ“ Deliverables

For each approach, create a file:

  • approach-1-nested.js
  • approach-2-composable.js
  • approach-3-declarative.js
  • approach-4-state-machine.js

Include:

  1. The implementation
  2. A short comment at the top explaining pros & cons
  3. At least 2 unit tests per approach (using Jest)

πŸ’‘ Bonus Challenges

  1. Add proper TypeScript types
  2. Make the logic fully async (simulate network calls for zone config and capacity)
  3. Add audit logging for every decision
  4. Create a React Native component that uses your favorite approach
  5. Implement error handling (network failure β†’ fail closed)

πŸ” Evaluation Criteria

  • Correctness (matches flowchart)
  • Code readability & organization
  • Separation of concerns
  • Test coverage
  • Comments & explanations

πŸš€ Starter Template

// approach-2-composable.js

function isExit(checkpoint) {
  // your code
}

function isTypeAllowed(wristband, allowedTypes) {
  // your code
}

// ... more helpers

async function evaluateWristbandAccess(wristband, checkpoint) {
  // orchestrator
}

Good luck!

Think about:

  • Which approach feels easiest to test and debug?
  • Which approach feels easiest to maintain and understand?
  • Which one would be easiest to scale and add new features?
  • Which one would be easiest for a non-developer to review?
  • Which one would you choose for a real production app? Why?
Back to top