Skip to content

About

Production cloud architecture for an event-driven workflow automation platform. System design documentation and patterns.

Topics

Resources

Stars

0 stars

Watchers

0 watching

Forks

Latest commit

 

History

1 Commit

Folders and files

NameName
Last commit message
Last commit date
 
 

Repository files navigation

Automotive Shop Management Platform - Cloud Architecture

A production-grade, event-driven cloud platform designed to automate and streamline automotive repair shop operations. This system integrates with third-party shop management software, processes multimedia content, and delivers real-time insights to technicians and service advisors.

System Overview

This platform demonstrates my approach to building scalable, resilient cloud architectures that handle complex business workflows across distributed systems. The architecture processes thousands of daily transactions while maintaining data integrity with external APIs that have strict rate limits.

Core Capabilities

  • Multi-Stage Data Processing Pipeline - Orchestrated workflow that transforms raw input data through validation, enrichment, and synchronization stages
  • Real-Time Media Processing - Video transcription, compression, and intelligent content pairing
  • Third-Party API Integration - Resilient integration layer with OAuth token management and adaptive rate limiting
  • WebSocket-Based Live Updates - Push notifications to mobile and web clients for status changes
  • AI-Powered Content Generation - LLM integration for expanding technical descriptions and generating estimates

Architecture Philosophy

Why Event-Driven?

I chose an event-driven architecture over a monolithic approach for several reasons:

  1. Decoupled Evolution - Each processing stage can be modified, scaled, or replaced independently without affecting others
  2. Natural Retry Boundaries - Failed operations are isolated to specific stages, making partial retries possible without reprocessing entire workflows
  3. Visibility - EventBridge events create an auditable trail of system state transitions
  4. Cost Efficiency - Pay only for actual compute time rather than idle server capacity

Why Serverless-First?

The workload is inherently bursty - shops are busiest during business hours with minimal overnight activity. Serverless computing aligns costs with actual usage patterns, while eliminating operational overhead of managing server fleets.


High-Level Architecture

┌─────────────────────────────────────────────────────────────────────────────┐
│                              CLIENT LAYER                                    │
│  ┌──────────────┐  ┌──────────────┐  ┌──────────────┐  ┌──────────────┐     │
│  │  Mobile App  │  │   Web App    │  │Chrome Extend.│  │  Edge Device │     │
│  │  (iOS/And.)  │  │   (React)    │  │              │  │  (Raspberry) │     │
│  └──────┬───────┘  └──────┬───────┘  └──────┬───────┘  └──────┬───────┘     │
└─────────┼─────────────────┼─────────────────┼─────────────────┼─────────────┘
          │                 │                 │                 │
          ▼                 ▼                 ▼                 ▼
┌─────────────────────────────────────────────────────────────────────────────┐
│                              API LAYER                                       │
│  ┌──────────────────────────────────────────────────────────────────────┐   │
│  │                    API Gateway (REST + WebSocket)                     │   │
│  │  ┌─────────────┐  ┌─────────────┐  ┌─────────────┐  ┌─────────────┐  │   │
│  │  │ Upload API  │  │ Vehicle API │  │ Estimate API│  │ Auth/Token  │  │   │
│  │  └─────────────┘  └─────────────┘  └─────────────┘  └─────────────┘  │   │
│  └──────────────────────────────────────────────────────────────────────┘   │
│                    ▲                                                         │
│                    │ JWT/Cognito Validation                                  │
│  ┌──────────────────────────────────────────────────────────────────────┐   │
│  │              Custom Lambda Authorizer (Multi-Provider)                │   │
│  │              Supports: Cognito + Auth0 + Custom JWT                   │   │
│  └──────────────────────────────────────────────────────────────────────┘   │
└─────────────────────────────────────────────────────────────────────────────┘
          │
          ▼
┌─────────────────────────────────────────────────────────────────────────────┐
│                         PROCESSING LAYER                                     │
│                                                                              │
│  ┌──────────────────────────────────────────────────────────────────────┐   │
│  │                    EventBridge (Event Router)                         │   │
│  │                                                                        │   │
│  │   Stage Transitions    Failure Alerts    S3 Triggers    Schedules    │   │
│  └──────────────────────────────────────────────────────────────────────┘   │
│          │                    │                   │                          │
│          ▼                    ▼                   ▼                          │
│  ┌──────────────┐     ┌──────────────┐    ┌──────────────┐                  │
│  │   Stage 1    │────▶│   Stage 2    │───▶│   Stage 3    │                  │
│  │  (Ingest)    │     │  (Process)   │    │  (Finalize)  │                  │
│  └──────────────┘     └──────────────┘    └──────────────┘                  │
│                                                                              │
│  ┌──────────────────────────────────────────────────────────────────────┐   │
│  │                    Step Functions Orchestrator                        │   │
│  │                                                                        │   │
│  │  ┌─────────┐   ┌─────────┐   ┌─────────┐   ┌─────────┐   ┌─────────┐ │   │
│  │  │ Assign  │──▶│ Verify  │──▶│ Upload  │──▶│ Confirm │──▶│Complete │ │   │
│  │  │  Slots  │   │Pre-Send │   │ w/Rate  │   │Post-Send│   │         │ │   │
│  │  └─────────┘   └─────────┘   │  Limit  │   └─────────┘   └─────────┘ │   │
│  │                              └─────────┘                              │   │
│  └──────────────────────────────────────────────────────────────────────┘   │
│                                                                              │
│  ┌──────────────────────────────────────────────────────────────────────┐   │
│  │                    SQS Queues (Work Distribution)                     │   │
│  │                                                                        │   │
│  │  ┌───────────────┐  ┌───────────────┐  ┌───────────────┐             │   │
│  │  │ Upload Queue  │  │ Estimate Queue│  │ Media Queue   │             │   │
│  │  │    (FIFO)     │  │    (FIFO)     │  │  (Standard)   │             │   │
│  │  └───────┬───────┘  └───────┬───────┘  └───────┬───────┘             │   │
│  │          │                  │                   │                     │   │
│  │          ▼                  ▼                   ▼                     │   │
│  │  ┌───────────────┐  ┌───────────────┐  ┌───────────────┐             │   │
│  │  │  Dead Letter  │  │  Dead Letter  │  │  Dead Letter  │             │   │
│  │  │    Queue      │  │    Queue      │  │    Queue      │             │   │
│  │  └───────────────┘  └───────────────┘  └───────────────┘             │   │
│  └──────────────────────────────────────────────────────────────────────┘   │
└─────────────────────────────────────────────────────────────────────────────┘
          │
          ▼
┌─────────────────────────────────────────────────────────────────────────────┐
│                         MEDIA PROCESSING LAYER                               │
│                                                                              │
│  ┌────────────────────────────────────────────────────────────────────┐     │
│  │                    Multi-Stage Media Pipeline                       │     │
│  │                                                                      │     │
│  │  ┌─────────┐   ┌─────────┐   ┌─────────┐   ┌─────────┐   ┌───────┐ │     │
│  │  │ Ingest  │──▶│Transcode│──▶│Transcribe──▶│  Match  │──▶│ Concat│ │     │
│  │  │         │   │Compress │   │  (AI)   │   │ Visuals │   │       │ │     │
│  │  └─────────┘   └─────────┘   └─────────┘   └─────────┘   └───────┘ │     │
│  └────────────────────────────────────────────────────────────────────┘     │
│                              │                                               │
│                              ▼                                               │
│                    ┌──────────────────┐                                      │
│                    │  MediaConvert    │                                      │
│                    │  (AWS Service)   │                                      │
│                    └──────────────────┘                                      │
└─────────────────────────────────────────────────────────────────────────────┘
          │
          ▼
┌─────────────────────────────────────────────────────────────────────────────┐
│                           DATA LAYER                                         │
│                                                                              │
│  ┌─────────────────────────────────────────────────────────────────────┐    │
│  │                         DynamoDB Tables                              │    │
│  │                                                                       │    │
│  │  ┌─────────────┐  ┌─────────────┐  ┌─────────────┐  ┌─────────────┐ │    │
│  │  │   Queue     │  │  Processing │  │   Entity    │  │   Config    │ │    │
│  │  │   Tables    │  │   Status    │  │   Tables    │  │   Tables    │ │    │
│  │  │             │  │             │  │             │  │             │ │    │
│  │  │ - Findings  │  │ - RO Status │  │ - Vehicles  │  │ - Auth Tkns │ │    │
│  │  │ - Estimates │  │ - Uploads   │  │ - Customers │  │ - Rate Lmts │ │    │
│  │  │ - Media     │  │ - Locks     │  │ - Orders    │  │ - Employees │ │    │
│  │  └─────────────┘  └─────────────┘  └─────────────┘  └─────────────┘ │    │
│  └─────────────────────────────────────────────────────────────────────┘    │
│                                                                              │
│  ┌─────────────────────────────────────────────────────────────────────┐    │
│  │                              S3                                      │    │
│  │  ┌─────────────┐  ┌─────────────┐  ┌─────────────┐  ┌─────────────┐ │    │
│  │  │   Media     │  │   Static    │  │  Processed  │  │   Lambda    │ │    │
│  │  │   Uploads   │  │   Assets    │  │   Outputs   │  │   Deploys   │ │    │
│  │  └─────────────┘  └─────────────┘  └─────────────┘  └─────────────┘ │    │
│  └─────────────────────────────────────────────────────────────────────┘    │
└─────────────────────────────────────────────────────────────────────────────┘
          │
          ▼
┌─────────────────────────────────────────────────────────────────────────────┐
│                     CONTAINERIZED SERVICES (EC2)                             │
│                                                                              │
│  ┌─────────────────────────────────────────────────────────────────────┐    │
│  │                    Docker Compose Stack                              │    │
│  │                                                                       │    │
│  │  ┌─────────────┐  ┌─────────────┐  ┌─────────────┐                  │    │
│  │  │ Long-Running│  │  Scheduled  │  │  Stateful   │                  │    │
│  │  │   Workers   │  │    Jobs     │  │  Services   │                  │    │
│  │  └─────────────┘  └─────────────┘  └─────────────┘                  │    │
│  └─────────────────────────────────────────────────────────────────────┘    │
└─────────────────────────────────────────────────────────────────────────────┘

Design Patterns & Decisions

1. Multi-Stage Pipeline with EventBridge

The Problem: Processing a single unit of work requires multiple sequential operations (fetch, enrich, validate, sync), each with different failure modes and retry characteristics.

My Approach: I decomposed the workflow into discrete stages, using EventBridge as the choreographer. Each stage:

  • Emits a "Stage N Complete" event upon success
  • Emits a "Stage N Failed" event with error details on failure
  • Is triggered by the previous stage's completion event

Why This Works:

  • Failures are isolated - a transcription failure doesn't block data sync
  • Each stage can have custom retry policies and timeouts
  • Operations can be replayed from any stage without starting over
  • New stages can be inserted without modifying existing code

2. DynamoDB Streams for Reactive Processing

The Problem: When new work items arrive, processing must begin immediately without polling.

My Approach: DynamoDB Streams trigger Lambda functions whenever items are inserted into queue tables. The stream captures:

  • The full item content (NewImage)
  • Whether it's an insert, modify, or delete
  • The sequence of changes for ordering guarantees

Why This Works:

  • Zero-latency trigger from data write to processing start
  • Built-in ordering within partition keys
  • Automatic batching for efficiency
  • No separate polling infrastructure to manage

3. FIFO Queues for Ordered, Exactly-Once Processing

The Problem: Certain operations (like uploading to third-party APIs) must happen in order and exactly once to maintain data integrity.

My Approach: FIFO SQS queues with message group IDs based on the entity being processed:

  • Content-based deduplication prevents duplicate processing
  • Message groups ensure per-entity ordering
  • Dead letter queues capture persistent failures for manual review

Why This Works:

  • Guarantees each item is processed exactly once
  • Maintains ordering when it matters (same entity)
  • Parallelizes when safe (different entities)
  • Failed messages don't block the entire queue

4. Step Functions for Complex Orchestration

The Problem: Uploading to the third-party API requires multiple coordinated steps with verification at each stage.

My Approach: A state machine that orchestrates:

  1. Slot Assignment - Match local data to available slots in the external system
  2. Pre-Upload Verification - Validate the mapping before any API calls
  3. Rate-Limited Upload - Sequential API calls with enforced delays (500ms between each)
  4. Post-Upload Verification - Fetch back from API to confirm success

Why This Works:

  • Visual debugging through the Step Functions console
  • Built-in retry with exponential backoff
  • Timeouts at each stage prevent runaway executions
  • Graceful handling of partial success ("CompletedWithErrors" state)

5. Adaptive Rate Limiting

The Problem: Third-party APIs have strict rate limits. Exceeding them results in temporary bans.

My Approach: A multi-layer rate limiting strategy:

  1. Token Bucket in DynamoDB - Track API calls per time window
  2. Sequential Processing - Step Functions enforce 500ms delays between calls
  3. Circuit Breaker - Exponential backoff when rate limit errors occur
  4. Per-Shop Counters - Distribute load fairly across tenants

Why This Works:

  • Respects API limits while maximizing throughput
  • Automatically adapts when limits change
  • Prevents one tenant from consuming all capacity
  • Graceful degradation under load

6. Multi-Provider Authentication

The Problem: Different clients use different identity providers - web app uses Cognito, mobile uses Auth0, and internal tools use custom tokens.

My Approach: A unified Lambda authorizer that:

  • Inspects the token format to determine provider
  • Validates against the appropriate provider
  • Returns a normalized principal for downstream use
  • Caches validation results to reduce latency

Why This Works:

  • Single authorization logic for all APIs
  • Clients don't need to change - only the auth layer
  • Easy to add new providers
  • Token caching reduces auth latency

7. WebSocket Connection Management

The Problem: Clients need real-time updates without polling.

My Approach: API Gateway WebSocket endpoints with:

  • Connection IDs stored in DynamoDB
  • Topic-based subscriptions (e.g., "subscribe to order 123")
  • Fan-out on state changes to relevant connections
  • Connection cleanup on disconnect

Why This Works:

  • True push notifications without client polling
  • Scales to thousands of concurrent connections
  • Cost-effective (pay per message, not per connection)
  • Works through firewalls and proxies

8. Media Processing Pipeline

The Problem: Video content needs transcription, compression, matching with visual templates, and concatenation.

My Approach: A staged media pipeline:

  1. Ingestion - S3 event triggers processing
  2. Transcription - Audio extraction and speech-to-text
  3. Compression - MediaConvert for format normalization
  4. Visual Matching - AI-powered matching with template library
  5. Concatenation - Final assembly of clips

Why This Works:

  • Each stage can scale independently
  • Expensive operations (transcription, compression) are isolated
  • Failures in one stage don't require restarting the pipeline
  • Can prioritize stages differently (e.g., rush transcription, defer compression)

Resilience Patterns

Dead Letter Queues Everywhere

Every SQS queue has a paired DLQ. After a configured number of failures, messages move to the DLQ for:

  • Manual inspection
  • Automated alerting
  • Replay after fixes

Idempotency Keys

Critical operations (especially API calls) use idempotency keys stored in DynamoDB:

  • Prevents duplicate processing on retry
  • Keys expire after processing window
  • Enables safe retry without side effects

Circuit Breaker Pattern

External API calls use a circuit breaker:

  • Closed (normal) - Requests flow through
  • Open (tripped) - Requests fail fast, no API calls
  • Half-Open (testing) - Limited requests to test recovery

Distributed Locks

For operations that must be singleton (one at a time per entity):

  • DynamoDB conditional writes for lock acquisition
  • TTL-based lock expiration to prevent deadlocks
  • Lock holder recorded for debugging

Monitoring & Observability

Event Patterns for Alerting

EventBridge rules capture failure events and route to:

  • SNS topics for email/SMS alerts
  • Slack webhooks for team notifications
  • CloudWatch for metrics and dashboards

Structured Logging

All Lambda functions emit structured JSON logs with:

  • Correlation IDs for request tracing
  • Stage identifiers for pipeline position
  • Entity IDs for debugging specific items

Cost Optimization Decisions

Why Serverless Over Always-On

For this workload profile (business hours heavy, overnight light):

  • Lambda costs ~40% less than equivalent always-on EC2
  • No capacity planning required
  • Automatic scaling to zero overnight

Why DynamoDB Over RDS

For this access pattern (key-value lookups, no complex joins):

  • On-demand pricing aligns with bursty traffic
  • No connection pool management
  • Global tables for future multi-region if needed

When EC2 Makes Sense

Some workloads run on EC2 with Docker:

  • Long-running processes (>15 min Lambda limit)
  • Stateful services requiring local storage
  • Third-party tools not designed for Lambda

Lessons Learned

  1. Start with events, add orchestration - Not everything needs Step Functions. EventBridge handles most workflows; reserve Step Functions for complex branching logic.

  2. Design for partial failure - Third-party APIs fail randomly. Every integration point needs retry logic, timeout handling, and graceful degradation.

  3. Rate limits are features, not bugs - Designing around rate limits forced better queue management and fairer resource distribution.

  4. DynamoDB Streams are powerful - They're not just for replication. Using them as triggers eliminates entire categories of polling infrastructure.

  5. FIFO isn't always necessary - Standard queues with idempotency keys often suffice. Reserve FIFO for true ordering requirements.


Technology Stack Summary

Layer Technologies
Compute AWS Lambda (Python, Node.js), EC2 with Docker Compose
API API Gateway (REST, WebSocket), Lambda Authorizers
Events EventBridge, SQS (Standard + FIFO), SNS, DynamoDB Streams
Orchestration Step Functions
Data DynamoDB, S3
Media AWS MediaConvert, AI Transcription Services
Auth Cognito, Auth0, Custom JWT
Monitoring CloudWatch, Custom EventBridge Alerting

Architecture Characteristics

Attribute Implementation
Scalability Auto-scaling Lambda, on-demand DynamoDB
Resilience Multi-stage retry, DLQs, circuit breakers
Observability Structured logging, event-based alerting
Cost Efficiency Pay-per-use compute, right-sized resources
Maintainability Decoupled services, event-driven integration

This architecture documentation describes the system design patterns and decisions. It does not include implementation details, credentials, or proprietary business logic.

About

Production cloud architecture for an event-driven workflow automation platform. System design documentation and patterns.

Topics

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors