Skip to content

Cloud-Native Application Patterns — 12-Factor App, Microservices, Serverless & Event-Driven

DodaTech Updated 2026-06-22 6 min read

In this tutorial, you'll learn about Cloud. We cover key concepts, practical examples, and best practices to help you understand and apply this topic effectively.

Cloud-native application patterns are architectural approaches designed specifically for cloud environments, emphasizing scalability, resilience, and rapid deployment through Microservices, serverless, and event-driven designs.

What You'll Learn

You'll learn the 12-factor app methodology, Microservices Architecture principles, Serverless Computing patterns, event-driven design with Message Queues, and how these patterns combine in production systems.

Why It Matters

Cloud-native applications scale efficiently, recover from failures automatically, and deploy 200+ times per day without downtime. Software Architecture decisions determine long-term operational costs. DodaZIP uses event-driven patterns where file compression requests trigger Lambda functions through SQS queues.

Real-World Use

A streaming service uses 12-factor principles (build one release, config from environment), decomposes its monolith into 30 Microservices, processes video transcoding via serverless functions, and handles user events through Kinesis streams.

The 12-Factor App Methodology

Factor Principle Cloud Implementation
1 Codebase One repo per service, tracked in Git
2 Dependencies Explicit dependency declaration (package.json, requirements.txt)
3 Config Environment variables, never in code
4 Backing services Treat databases, queues as attached resources
5 Build, release, run Strict separation of build and run stages
6 Processes Stateless processes, no local persistence
7 Port binding Self-contained, export services via port
8 Concurrency Scale out via process model
9 Disposability Fast startup, graceful shutdown
10 Dev/prod parity Keep environments as similar as possible
11 Logs Treat logs as event streams
12 Admin processes Run admin tasks as one-off processes
# Factor 3: Config from environment variables (12-factor)
import os
import psycopg2

db_config = {
    "host": os.environ.get("DATABASE_HOST", "localhost"),
    "port": int(os.environ.get("DATABASE_PORT", 5432)),
    "database": os.environ["DATABASE_NAME"],
    "user": os.environ["DATABASE_USER"],
    "password": os.environ["DATABASE_PASSWORD"],
}

# Never hardcode connection strings!
conn = psycopg2.connect(**db_config)
print(f"Connected to {db_config['database']} on {db_config['host']}")

Expected behavior: The application reads configuration from environment variables. The same container image runs in dev, staging, and production with different environment values.

Microservices Architecture

Microservices decompose applications into small, independently deployable services that communicate over the network.

flowchart LR
  A[API Gateway] --> B[User Service]
  A --> C[Order Service]
  A --> D[Payment Service]
  A --> E[Notification Service]
  B --> F[(User DB)]
  C --> G[(Order DB)]
  D --> H[(Payment DB)]
  B -->|events| I[Message Queue]
  C -->|events| I
  E --> I
  style A fill:#48f,color:#fff
  style I fill:#f80,color:#fff
// Microservice: Order Service API endpoint
const express = require('express');
const app = express();

app.get('/api/orders/:userId', async (req, res) => {
  const { userId } = req.params;

  // Service only queries its own database
  const orders = await db.query(
    'SELECT * FROM orders WHERE user_id = $1',
    [userId]
  );

  // Returns minimal data — other details come from User Service
  res.json({
    userId,
    orders: orders.map(o => ({
      id: o.id,
      total: o.total,
      status: o.status,
      createdAt: o.created_at
    }))
  });
});

app.listen(3000);

Expected behavior: Each service runs independently. If the Notification Service fails, Order and Payment continue processing. Failures are isolated.

Serverless Patterns

Serverless Computing runs code without provisioning servers. Functions scale from zero to thousands instantly.

# AWS Lambda: event-driven image resizing
import boto3

s3 = boto3.client("s3")

def lambda_handler(event, context):
    for record in event["Records"]:
        bucket = record["s3"]["bucket"]["name"]
        key = record["s3"]["object"]["key"]

        # Resize image when uploaded to S3
        print(f"Processing {key} from {bucket}")

        # Thumbnail path
        thumbnail_key = f"thumbnails/{key.split('/')[-1]}"

        # Get image, resize, upload thumbnail
        response = s3.get_object(Bucket=bucket, Key=key)
        image_data = response["Body"].read()
        thumbnail = resize_image(image_data, width=200)

        s3.put_object(
            Bucket=bucket,
            Key=thumbnail_key,
            Body=thumbnail,
            ContentType="image/webp"
        )

    return {"statusCode": 200, "body": "Processed"}

Expected behavior: Every new image uploaded to the S3 bucket triggers the Lambda function, which creates a 200px-wide WebP thumbnail automatically.

Event-Driven Architecture

Event-driven systems use asynchronous message passing between services. Producers emit events without knowing which consumers will process them.

# Send an event to SQS
aws sqs send-message \
  --queue-url https://sqs.us-east-1.amazonaws.com/123456789/dodatech-jobs \
  --message-body '{"job_type": "compress", "file": "backup.zip"}'

# Receive events from SQS
aws sqs receive-message \
  --queue-url https://sqs.us-east-1.amazonaws.com/123456789/dodatech-jobs \
  --max-number-of-messages 10 \
  --wait-time-seconds 20

Expected behavior: The producer sends a job event to SQS. Workers poll the queue and process jobs concurrently. Failed jobs return to the queue after the visibility timeout expires.

Common Errors

  1. Monolith decomposition too aggressive: Splitting into too many Microservices before the team is ready causes coordination overhead. Start with 3-5 services.
  2. Shared database across services: Microservices must own their data. A shared database creates coupling that defeats the purpose of independent deployment.
  3. Neglecting observability: Distributed Systems fail in complex ways. Without centralized logging, tracing, and metrics, debugging becomes impossible.
  4. Synchronous calls between services: Synchronous chains create cascading failures. Use async messaging and event-driven patterns for resilience.
  5. Cold starts in serverless: Functions that idle for long periods incur cold start latency. Use provisioned concurrency or keep functions warm with scheduled pings.
  6. Ignoring eventual consistency: In Distributed Systems, strong consistency is expensive. Design for eventual consistency where possible.

Practice Questions

  1. What is the difference between the 12-factor Config and Codebase factors? Codebase mandates one repo per deployable. Config requires separating configuration from code using environment variables.
  2. When should you use serverless instead of containers? Serverless is ideal for event-driven, bursty, or short-duration workloads. Containers suit long-running, stateful, or GPU-dependent services.
  3. What is eventual consistency in Event-Driven Architecture? Changes propagate asynchronously across services. A user might not see their update immediately, but the system converges over time.
  4. How do you handle idempotency in event processing? Assign a unique event ID to each message. Consumers check if the event ID has been processed before applying the change.
  5. Challenge: Design a cloud-native architecture for a document processing system. Users upload PDFs, the system extracts text, runs OCR, translates to multiple languages, and notifies users. Use 12-factor principles, Microservices, serverless, and event-driven patterns.

Mini Project

Build a cloud-native document processing pipeline:

  • User uploads a PDF to an S3 bucket
  • An S3 event triggers a Lambda function that extracts metadata
  • The function sends a message to an SQS queue for text extraction
  • A containerized worker (ECS or EKS) polls the queue and extracts text using OCR
  • Results are stored in a database and a notification event is published
  • A notification service sends an email to the user
  • Implement the 12-factor config pattern (environment variables for S3 bucket names, database URLs, queue URLs)
  • Deploy each component independently

Built by the developers of Doda Browser, DodaZIP, and Durga Antivirus Pro.

Built by the developers of DodaTech

Doda Browser, DodaZIP & Durga Antivirus Pro