Project case study

Event-Driven Workflow Scheduler

Dual-path event scheduler for real-time and future-dated triggers. Push path for immediate domain events, pull path via a managed scheduler for precise timestamps, with a dedicated failure handler for safe recovery.

Private implementation; details are shared at an architectural level.

Event-Driven Workflow Scheduler project

Problem

  • Existing scheduler only supported fixed intervals and a single daily polling pass
  • No way to trigger work on a real-time domain event
  • No precision on future-dated triggers
  • Critical time windows needed manual babysitting
  • Data pre-calculated before execution went stale by the time it ran

Constraints

  • Scheduling drives real user-facing operations, a missed run is a real miss
  • Has to support real-time and future-dated triggers in one coherent design
  • State changes between scheduling and firing, so pre-resolving entity lists is not safe
  • Failures must be visible and recoverable, not silent
  • Has to coexist with the existing downstream workers, not replace them

What We Tried First

  • First instinct was to pre-resolve the full entity list inside the daily pre-calculation pass
  • Simpler pipeline, easier to test, fewer moving parts
  • Did not survive first contact with real state changes between pre-calc and execution
  • The scheduler was firing on a stale snapshot of the world
  • That was the point where entity resolution had to move to execution time and the dual-path design stopped being optional

Architecture

  • Two deliberate paths, separated on purpose: push for real-time triggers, pull for future-dated
  • Push path wires domain events straight to the existing message broker
  • Pull path uses a managed scheduler with one schedule per task, not one per entity
  • Entities are resolved at execution time, then fanned out to the existing downstream workers unchanged
  • A dedicated failure handler watches the dead-letter queue, captures context, and allows controlled re-queuing

Tradeoffs

  • Execution-time entity resolution over pre-calculation. Simpler to pre-calc but stale data is worse than no data
  • One schedule per task over one per entity. Keeps quota pressure bounded and the next-run update atomic
  • Reuse the existing message broker instead of rewriting downstream workers. Coexistence beats big-bang migration
  • Minimal payloads at the scheduling boundary, entity fan-out after. Keeps scheduling cheap and entity logic centralised
  • Dedicated failure handler over best-effort logging. Silent failures in a scheduling system are the worst kind

Outcome

  • Scheduling moved from once-daily polling to exact-time delivery
  • Failures are now visible and recoverable instead of silent
  • Downstream workers still run unchanged
  • Same pattern now powers multiple scheduled workloads

Tools and technologies

AWS EventBridge · AWS SQS · RabbitMQ · PHP · PostgreSQL