Skip to main content

πŸ“š Advanced Microservices Architecture - Practical Assignments

Here is the full English translation of the uploaded document E-Commerce_Microservices_Platform_Assignment_v1.0_en.md.

All Vietnamese instructions, scenarios, and explanations have been translated into professional technical English while maintaining the original Markdown structure, code blocks, and diagrams.


πŸ“š Advanced Microservices Architecture - Practical Assignments

E-Commerce Platform Migration Project​

Course: Advanced Microservices Architecture Version: 1.0.0 Last Updated: 2025-12-24 Prerequisite: Completed mono-src (Building High-Performance APIs with FastAPI)


🎯 Project Overview​

Migration Journey​

You will migrate mono-src (monolithic Product Catalog Service) to a complete Microservices architecture:

β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”          β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚ β”‚ β”‚ MICROSERVICES PLATFORM β”‚
β”‚ mono-src β”‚ β”‚ β”‚
β”‚ (Monolithic) β”‚ ──────► β”‚ β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β” β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β” β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β” β”‚
β”‚ β”‚ β”‚ β”‚ User β”‚ β”‚ Product β”‚ β”‚ Order β”‚ β”‚
β”‚ β€’ User Auth β”‚ β”‚ β”‚ Service β”‚ β”‚ Service β”‚ β”‚ Service β”‚ β”‚
β”‚ β€’ Product CRUD β”‚ β”‚ β””β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”˜ β””β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”˜ β””β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”˜ β”‚
β”‚ β€’ Single DB β”‚ β”‚ β”‚ β”‚ β”‚ β”‚
β”‚ β”‚ β”‚ β”Œβ”€β”€β”€β”€β–Όβ”€β”€β”€β”€β” β”Œβ”€β”€β”€β”€β–Όβ”€β”€β”€β”€β” β”Œβ”€β”€β”€β”€β–Όβ”€β”€β”€β”€β” β”‚
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ β”‚ β”‚ DB β”‚ β”‚ DB β”‚ β”‚ DB β”‚ β”‚
β”‚ β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ β”‚
β”‚ β”‚
β”‚ + API Gateway (Kong) β”‚
β”‚ + Message Broker (RabbitMQ) β”‚
β”‚ + Cache (Redis) β”‚
β”‚ + Observability Stack β”‚
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜

Technology Stack​

ComponentTechnologyPort
User ServiceFastAPI + PostgreSQL8001
Product ServiceFastAPI + PostgreSQL + Redis8002
Order ServiceFastAPI + PostgreSQL + RabbitMQ8003
Notification ServiceFastAPI + RabbitMQ8004
API GatewayKong8000
CacheRedis6379
Message BrokerRabbitMQ5672, 15672
TracingJaeger16686
MetricsPrometheus9090
LoggingLoki + Promtail3100
VisualizationGrafana3000

Target Architecture​

                          β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚ Svelte SPA β”‚
β”‚ (Port 5173) β”‚
β””β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”˜
β”‚
β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β–Όβ”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚ Kong Gateway β”‚
β”‚ (Port 8000) β”‚
β””β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”˜
β”‚
β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚ β”‚ β”‚
β”Œβ”€β”€β”€β”€β”€β”€β”€β–Όβ”€β”€β”€β”€β”€β”€β”€β” β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β–Όβ”€β”€β”€β”€β”€β”€β”€β”€β” β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β–Όβ”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚ User Service │◄────────│ Product Service β”‚ β”‚ Order Service β”‚
β”‚ (Port 8001) β”‚ Token β”‚ (Port 8002) β”‚ β”‚ (Port 8003) β”‚
β”‚ β”‚ Verify β”‚ β”‚ β”‚ β”‚
β”‚ β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β” β”‚ β”‚ β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β” β”‚ β”‚ β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β” β”‚
β”‚ β”‚PostgreSQLβ”‚ β”‚ β”‚ β”‚PostgreSQLβ”‚ β”‚ β”‚ β”‚PostgreSQLβ”‚ β”‚
β”‚ β”‚ user_db β”‚ β”‚ β”‚ β”‚product_dbβ”‚ β”‚ β”‚ β”‚ order_db β”‚ β”‚
β”‚ β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ β”‚ β”‚ β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ β”‚ β”‚ β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ β”‚
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ β”‚ β”‚ β”‚ β”‚
β”‚ β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β” β”‚ β”‚ β”‚ β”‚
β”‚ β”‚ Redis β”‚ β”‚ β”‚ β”‚ β”‚
β”‚ β”‚ Cache β”‚ β”‚ β”‚ β–Ό β”‚
β”‚ β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ β”‚ β”‚ β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” β”‚
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ β”‚ β”‚ RabbitMQ β”‚ β”‚
β”‚ β”‚ Publisherβ”‚ β”‚
β”‚ β””β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”˜ β”‚
β””β”€β”€β”€β”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜
β”‚
β”Œβ”€β”€β”€β”€β”€β”€β”€β–Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚ Notification β”‚
β”‚ Service β”‚
β”‚ (Port 8004) β”‚
β”‚ β”‚
β”‚ β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” β”‚
β”‚ β”‚ RabbitMQ β”‚ β”‚
β”‚ β”‚ Consumer β”‚ β”‚
β”‚ β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ β”‚
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜


Assignment 01: User Service Extraction​

Unit 1 - Microservices Fundamentals & Refactoring​

🎯 Learning Objectives​

  • Understand Bounded Context and Database per Service concepts.
  • Apply the Strangler Fig Pattern to migrate services.
  • Set up an independent microservice with FastAPI.

πŸ“– Business Requirements​

Scenario​

The company has decided to transition from a monolithic to a microservices architecture to:

  • Scale components independently.
  • Deploy features without affecting the entire system.
  • Allow teams to work in parallel.

User Service is selected first because:

  • It has a clear Bounded Context (Authentication domain).
  • It has few dependencies on other domains.
  • It is a critical component - needs stability and independent scaling.

βœ… Required Tasks​

Task 1.1: Project Structure Setup (15 points)​

Requirements:

Create the directory structure for User Service according to the pattern:

micro-src/
└── server/
└── user-service/
β”œβ”€β”€ app/
β”‚ β”œβ”€β”€ __init__.py
β”‚ β”œβ”€β”€ main.py # FastAPI app entry
β”‚ β”œβ”€β”€ api/
β”‚ β”‚ β”œβ”€β”€ __init__.py
β”‚ β”‚ β”œβ”€β”€ auth.py # Auth endpoints
β”‚ β”‚ └── deps.py # Dependencies
β”‚ β”œβ”€β”€ config/
β”‚ β”‚ β”œβ”€β”€ __init__.py
β”‚ β”‚ └── settings.py # Pydantic settings
β”‚ β”œβ”€β”€ database/
β”‚ β”‚ β”œβ”€β”€ __init__.py
β”‚ β”‚ └── database.py # SQLAlchemy setup
β”‚ β”œβ”€β”€ models/
β”‚ β”‚ β”œβ”€β”€ __init__.py
β”‚ β”‚ └── user.py # User model
β”‚ β”œβ”€β”€ repositories/
β”‚ β”‚ β”œβ”€β”€ __init__.py
β”‚ β”‚ β”œβ”€β”€ base.py
β”‚ β”‚ └── user_repository.py
β”‚ β”œβ”€β”€ schemas/
β”‚ β”‚ β”œβ”€β”€ __init__.py
β”‚ β”‚ └── user.py # Pydantic schemas
β”‚ β”œβ”€β”€ services/
β”‚ β”‚ β”œβ”€β”€ __init__.py
β”‚ β”‚ └── auth_service.py # Business logic
β”‚ └── utils/
β”‚ β”œβ”€β”€ __init__.py
β”‚ └── security.py # JWT, password utils
β”œβ”€β”€ alembic/
β”œβ”€β”€ tests/
β”œβ”€β”€ Dockerfile
β”œβ”€β”€ requirements.txt
β”œβ”€β”€ alembic.ini
└── start.sh

Acceptance Criteria:

  • Correct directory structure.
  • All __init__.py files exist.
  • Code from mono-src migrated and refactored.

Task 1.2: Database per Service (20 points)​

Business Logic:

User Service requires a separate, completely isolated database:

  • Do not share schema with other services.
  • Independent DB scaling.
  • Independent migration.

Technical Requirements:

SettingValue
DatabasePostgreSQL 15
DB Nameuser_service_db
Port (external)5433
Port (internal)5432

Docker Compose Configuration:

user-db:
image: postgres:15-alpine
environment:
POSTGRES_USER: user
POSTGRES_PASSWORD: password
POSTGRES_DB: user_service_db
ports:
- '5433:5432'
volumes:
- user-db-data:/var/lib/postgresql/data
healthcheck:
test: ['CMD-SHELL', 'pg_isready -U user -d user_service_db']

Acceptance Criteria:

  • Database container runs successfully.
  • Connection from service to DB works.
  • Healthcheck passes.

Task 1.3: User Model & Migrations (15 points)​

Business Logic:

Migrate the User model from mono-src, ensuring:

  • Preserve schema structure.
  • Alembic migrations work.
  • Auto-migration when the container starts.

Model Definition:

ColumnTypeConstraints
idIntegerPK, auto-increment
usernameString(50)Unique, Not Null, Indexed
hashed_passwordString(255)Not Null
is_activeBooleanDefault True
created_atDateTimeServer default now()
updated_atDateTimeAuto-update

Acceptance Criteria:

  • Alembic initialized.
  • Initial migration generated.
  • start.sh runs migrations automatically.

Task 1.4: Authentication Endpoints (25 points)​

Business Logic:

Migrate and enhance authentication endpoints from mono-src:

EndpointMethodDescription
/registerPOSTUser registration
/loginPOSTOAuth2 Password Flow
/validate-tokenPOSTNEW - Token validation for other services
/healthGETHealth check

Token Validation Endpoint (Important):

This is a NEW endpoint, not present in mono-src. Other services (Product, Order) will call this endpoint to validate JWT tokens instead of decoding the token directly.

POST /validate-token
Content-Type: application/json

Request:
{
"token": "eyJhbGciOiJIUzI1NiIs..."
}

Response (Valid):
{
"valid": true,
"user_id": 1,
"username": "john_doe",
"message": "Token is valid"
}

Response (Invalid):
{
"valid": false,
"user_id": null,
"username": null,
"message": "Token is invalid or expired"
}

Why not share the JWT secret?

  • Security: Each service does not need to know the secret key.
  • Centralized auth: User Service is the Single Source of Truth.
  • Rotation: Keys can be rotated without updating other services.

Acceptance Criteria:

  • POST /register creates user successfully.
  • POST /login returns JWT token.
  • POST /validate-token validates and returns user info.
  • GET /health returns status.

Task 1.5: Dockerization (15 points)​

Business Logic:

The Service needs to be containerized for easy deployment:

  • Multi-stage build (optional) to reduce image size.
  • Non-root user for security.
  • Auto-migration on startup.

Dockerfile Requirements:

FROM python:3.11-slim

WORKDIR /app

# Install dependencies
COPY requirements.txt .
RUN pip install --no-cache-dir -r requirements.txt

# Copy application
COPY . .

# Make start script executable
RUN chmod +x start.sh

# Create non-root user
RUN useradd -m appuser && chown -R appuser:appuser /app
USER appuser

EXPOSE 8001

HEALTHCHECK --interval=30s --timeout=3s --start-period=40s \
CMD curl -f http://localhost:8001/health || exit 1

CMD ["./start.sh"]

start.sh:

#!/bin/bash
set -e

echo "Waiting for database..."
sleep 5

echo "Running database migrations..."
alembic upgrade head

echo "Starting User Service..."
uvicorn app.main:app --host 0.0.0.0 --port 8001

Acceptance Criteria:

  • Docker build successful.
  • Container starts and is healthy.
  • Migrations run automatically.
  • API accessible at port 8001.

🌟 Bonus Tasks​

Bonus 1.1: User Events Publishing (15 points)​

Guideline: When a user successfully registers, publish a user.created event to RabbitMQ. The event contains user_id, username, created_at. Prepare for Notification Service to consume.

Bonus 1.2: Password Policy (10 points)​

Guideline: Implement password policy validation: min 8 chars, 1 uppercase, 1 lowercase, 1 digit, 1 special char. Return specific error messages for each failed rule.

Bonus 1.3: Rate Limiting (10 points)​

Guideline: Implement rate limiting for the login endpoint: max 5 attempts per minute per IP. Use in-memory storage or Redis.


πŸ“Š Rubric - Assignment 01​

CriteriaExcellent (100%)Good (80%)Satisfactory (60%)Needs Improvement (40%)
Project Structure (15pts)Perfect structure, all filesMinor issuesBasic structureIncorrect structure
Database Setup (20pts)Isolated DB, healthcheck, volumesWorks, minor config issuesBasic setupCannot connect
Model & Migrations (15pts)Alembic works, auto-migrateWorks manuallyPartialNot working
Auth Endpoints (25pts)All 4 endpoints, validate-token worksMissing 1 endpoint2 endpoints workMost broken
Dockerization (15pts)Multi-stage, non-root, healthcheckBasic Dockerfile worksContainer runsBuild fails
Code Quality (10pts)Clean, documented, type hintsGood codeAcceptablePoor quality

Total: 100 points Bonus: 35 points


πŸ“ Assignment 02: Product Service & API Gateway​

Unit 2 - API Gateway (Kong)​

🎯 Learning Objectives​

  • Extract Product Service into an independent microservice.
  • Implement inter-service authentication.
  • Setup and configure Kong API Gateway.
  • Understand routing and rate limiting concepts.

πŸ“– Business Requirements​

Scenario​

After the User Service is stable, the team continues to migrate the Product Service. At the same time, an API Gateway is needed to:

  • Provide a single entry point for all API calls.
  • Centralize routing and security.
  • Implement rate limiting to protect services.
  • Handle logging and monitoring.

βœ… Required Tasks​

Task 2.1: Product Service Extraction (25 points)​

Business Logic:

Migrate the Product domain from mono-src into an independent service. Product Service:

  • Has a separate database (product_service_db).
  • Does NOT decode JWT tokens directly.
  • Calls User Service to validate tokens.

Project Structure:

product-service/
β”œβ”€β”€ app/
β”‚ β”œβ”€β”€ main.py
β”‚ β”œβ”€β”€ api/
β”‚ β”‚ β”œβ”€β”€ products.py # CRUD endpoints
β”‚ β”‚ └── deps.py # Auth dependency
β”‚ β”œβ”€β”€ config/
β”‚ β”œβ”€β”€ database/
β”‚ β”œβ”€β”€ models/
β”‚ β”‚ └── product.py
β”‚ β”œβ”€β”€ repositories/
β”‚ β”‚ └── product_repository.py
β”‚ β”œβ”€β”€ schemas/
β”‚ β”‚ └── product.py
β”‚ β”œβ”€β”€ services/
β”‚ β”‚ └── product_service.py
β”‚ └── utils/
β”‚ └── auth_client.py # HTTP client to User Service
β”œβ”€β”€ alembic/
β”œβ”€β”€ Dockerfile
└── requirements.txt

Acceptance Criteria:

  • Service runs on port 8002.
  • Separate database (product_service_db, port 5434).
  • CRUD endpoints functional.

Task 2.2: Inter-Service Authentication (25 points)​

Business Logic:

Product Service needs to authenticate the user but does NOT have access to the JWT secret. Flow:

β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”    Token     β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”    Validate    β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚ Client β”‚ ──────────► β”‚ Product Service β”‚ ─────────────► β”‚ User Service β”‚
β””β”€β”€β”€β”€β”€β”€β”€β”€β”˜ β”‚ β”‚ β”‚ β”‚
β”‚ Authorization: β”‚ POST β”‚ /validate- β”‚
β”‚ Bearer <token> β”‚ ◄───────────── β”‚ token β”‚
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ {valid: true}β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜

Auth Client Implementation:

Create auth_client.py to call User Service:

# app/utils/auth_client.py

class AuthClient:
def __init__(self, user_service_url: str):
self.user_service_url = user_service_url

async def validate_token(self, token: str) -> Optional[dict]:
"""
Validate JWT token via User Service

Args:
token: JWT token from Authorization header

Returns:
User info dict if valid, None if invalid
"""
# Implement HTTP call to User Service /validate-token
pass

Dependency for Protected Routes:

# app/api/deps.py

async def get_current_user(
authorization: str = Header(None)
) -> dict:
"""
Dependency to get current user via User Service

Raises:
HTTPException 401 if no token or invalid
"""
# Extract token from "Bearer <token>"
# Call auth_client.validate_token(token)
# Return user info or raise 401
pass

Acceptance Criteria:

  • Auth client calls User Service successfully.
  • Protected endpoints require valid token.
  • 401 returned when token is invalid/missing.
  • User info available in route handlers.

Task 2.3: Kong API Gateway Setup (30 points)​

Business Logic:

Kong Gateway provides:

  • Routing: Direct requests to correct service.
  • Rate Limiting: Protect services from abuse.
  • Logging: Centralized access logs.
  • Authentication (optional): Can add JWT plugin.

Docker Compose for Kong:

kong-db:
image: postgres:15-alpine
environment:
POSTGRES_USER: kong
POSTGRES_PASSWORD: kong
POSTGRES_DB: kong
volumes:
- kong-db-data:/var/lib/postgresql/data

kong-migration:
image: kong:3.4
command: kong migrations bootstrap
environment:
KONG_DATABASE: postgres
KONG_PG_HOST: kong-db
KONG_PG_USER: kong
KONG_PG_PASSWORD: kong
depends_on:
kong-db:
condition: service_healthy

kong:
image: kong:3.4
environment:
KONG_DATABASE: postgres
KONG_PG_HOST: kong-db
KONG_PG_USER: kong
KONG_PG_PASSWORD: kong
KONG_PROXY_ACCESS_LOG: /dev/stdout
KONG_ADMIN_ACCESS_LOG: /dev/stdout
KONG_PROXY_ERROR_LOG: /dev/stderr
KONG_ADMIN_ERROR_LOG: /dev/stderr
KONG_ADMIN_LISTEN: 0.0.0.0:8001
ports:
- '8000:8000' # Proxy
- '8443:8443' # Proxy SSL
- '8001:8001' # Admin API
depends_on:
kong-migration:
condition: service_completed_successfully

Kong Configuration via Admin API:

  1. Register User Service:
curl -X POST http://localhost:8001/services \
--data "name=user-service" \
--data "url=http://user-service:8001"

curl -X POST http://localhost:8001/services/user-service/routes \
--data "paths[]=/api/users" \
--data "strip_path=true"

  1. Register Product Service:
curl -X POST http://localhost:8001/services \
--data "name=product-service" \
--data "url=http://product-service:8002"

curl -X POST http://localhost:8001/services/product-service/routes \
--data "paths[]=/api/products" \
--data "strip_path=true"

Routing Table:

Client RequestKong RouteUpstream
POST /api/users/register/api/users/*user-service:8001/register
POST /api/users/login/api/users/*user-service:8001/login
GET /api/products/api/products/*product-service:8002/products
POST /api/products/api/products/*product-service:8002/products

Acceptance Criteria:

  • Kong container running healthy.
  • User Service accessible via Kong.
  • Product Service accessible via Kong.
  • Direct service access still works (for internal calls).

Task 2.4: Rate Limiting Plugin (10 points)​

Business Logic:

Protect API from abuse with rate limiting:

  • Login endpoint: 5 requests/minute (prevent brute force).
  • Product listing: 100 requests/minute.
  • Product creation: 20 requests/minute.

Kong Rate Limiting Configuration:

# Global rate limit
curl -X POST http://localhost:8001/plugins \
--data "name=rate-limiting" \
--data "config.minute=100" \
--data "config.policy=local"

# Specific for login route
curl -X POST http://localhost:8001/services/user-service/plugins \
--data "name=rate-limiting" \
--data "config.minute=10" \
--data "config.policy=local"

Acceptance Criteria:

  • Rate limiting plugin enabled.
  • Exceeded requests return 429 Too Many Requests.
  • Headers show rate limit info (X-RateLimit-*).

🌟 Bonus Tasks​

Bonus 2.1: Kong Declarative Config (15 points)​

Guideline: Instead of Admin API calls, create a kong.yml declarative config file. Mount it to the container with KONG_DECLARATIVE_CONFIG. Easier for version control and reproduction.

Bonus 2.2: JWT Plugin (15 points)​

Guideline: Configure Kong JWT plugin to validate tokens at the gateway level. When enabled, Kong verifies JWT before forwarding to the service. Requires registering consumer and credentials.

Bonus 2.3: Request/Response Transformation (10 points)​

Guideline: Add a transformation plugin to add custom headers (X-Request-ID, X-Service-Name) into responses. Useful for debugging and tracing.


πŸ“Š Rubric - Assignment 02​

CriteriaExcellent (100%)Good (80%)Satisfactory (60%)Needs Improvement (40%)
Product Service (25pts)Complete CRUD, isolated DBMissing 1 featureBasic CRUD worksService broken
Inter-Service Auth (25pts)Auth client works, proper errorsWorks, error handling weakBasic validationNot implemented
Kong Setup (30pts)Full routing, both servicesOne service worksKong runs, partial routingKong not working
Rate Limiting (10pts)Multiple limits configuredBasic limit worksPlugin enabledNot configured
Code Quality (10pts)Clean, async, documentedGood codeAcceptablePoor quality

Total: 100 points Bonus: 40 points


πŸ“ Assignment 03: Async Communication (RabbitMQ)​

Unit 3 - Async Communication​

🎯 Learning Objectives​

  • Understand the difference between Sync vs Async communication.
  • Setup RabbitMQ with Exchange, Queue, Binding.
  • Implement Producer/Consumer pattern with pika/aio-pika.
  • Handle message acknowledgment and error cases.

πŸ“– Business Requirements​

Scenario​

When a user registers, the system needs to send a welcome email. If the email API is called synchronously:

  • User must wait for email sending to complete (slow response).
  • If the email service is down β†’ registration fails (not acceptable).

Solution: Async communication via message queue.

β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”  1.Register   β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚ Client β”‚ ────────────► β”‚ User Service β”‚
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ β”‚ β”‚
β–² β”‚ 2. Create β”‚
β”‚ β”‚ User β”‚
β”‚ 3. Return β”‚ β”‚
β”‚ 201 Created β”‚ 4. Publish β”‚
β”‚ (immediate) β”‚ Event β”‚
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”΄β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”˜
β”‚
β”Œβ”€β”€β”€β”€β”€β”€β–Όβ”€β”€β”€β”€β”€β”€β”€β”
β”‚ RabbitMQ β”‚
β”‚ user.createdβ”‚
β””β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”˜
β”‚
β”Œβ”€β”€β”€β”€β”€β”€β–Όβ”€β”€β”€β”€β”€β”€β”€β”
β”‚ Notification β”‚
β”‚ Service β”‚
β”‚ β”‚
β”‚ 5. Send Emailβ”‚
β”‚ (async) β”‚
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜


βœ… Required Tasks​

Task 3.1: RabbitMQ Setup (15 points)​

Docker Compose:

rabbitmq:
image: rabbitmq:3-management-alpine
container_name: rabbitmq-server
environment:
RABBITMQ_DEFAULT_USER: guest
RABBITMQ_DEFAULT_PASS: guest
ports:
- '5672:5672' # AMQP
- '15672:15672' # Management UI
volumes:
- rabbitmq-data:/var/lib/rabbitmq
healthcheck:
test: ['CMD', 'rabbitmq-diagnostics', 'ping']
interval: 10s
timeout: 5s
retries: 5

Concepts to Understand:

ConceptDescription
ExchangeRouter that receives messages and routes them to queues
QueueBuffer that stores messages waiting to be consumed
BindingRule connecting exchange to queue
Routing KeyPattern to route message to correct queue

Exchange Types:

TypeBehavior
directRoute by exact routing key match
topicRoute by pattern matching (*.created, order.#)
fanoutBroadcast to all bound queues
headersRoute based on headers

Acceptance Criteria:

  • RabbitMQ container healthy.
  • Management UI accessible at :15672.
  • Can create exchange/queue via UI.

Task 3.2: Event Publisher - User Service (25 points)​

Business Logic:

After user registers successfully, publish event to RabbitMQ:

Event Structure:

{
"event": "user.created",
"timestamp": "2025-12-24T10:00:00Z",
"data": {
"user_id": 1,
"username": "john_doe",
"email": "john@example.com",
"created_at": "2025-12-24T10:00:00Z"
}
}

Exchange Configuration:

  • Name: user_events
  • Type: topic
  • Durable: true

Publisher Implementation Skeleton:

# user-service/app/utils/rabbitmq.py

class RabbitMQPublisher:
"""Publisher class for sending events to RabbitMQ"""

async def connect(self):
"""Connect to RabbitMQ and declare exchange 'user_events' (topic, durable)"""
# TODO: Use aio_pika.connect_robust()
pass

async def publish_user_created(self, user_data: dict):
"""
Publish user.created event with:
- routing_key: "user.created"
- delivery_mode: PERSISTENT
- body: {"event": "user.created", "data": user_data}
"""
pass

async def close(self):
"""Close connection gracefully"""
pass

Integration Point (AuthService):

# After create user successfully, publish event:
await rabbitmq_publisher.publish_user_created({
"user_id": user.id,
"username": user.username,
"created_at": str(user.created_at),
})

Acceptance Criteria:

  • Exchange user_events created (durable).
  • Event published when user registers.
  • Message visible in RabbitMQ Management UI.
  • Registration is still fast (async publish).

Task 3.3: Event Consumer - Notification Service (30 points)​

Business Logic:

Notification Service consumes events and sends notifications:

  • Welcome email when user registers.
  • Order confirmation when order created (future).

Project Structure:

notification-service/
β”œβ”€β”€ app/
β”‚ β”œβ”€β”€ main.py
β”‚ β”œβ”€β”€ config/
β”‚ β”‚ └── settings.py
β”‚ └── utils/
β”‚ β”œβ”€β”€ rabbitmq.py # Consumer
β”‚ └── notifications.py # Email/SMS handlers
β”œβ”€β”€ Dockerfile
└── requirements.txt

Consumer Implementation Skeleton:

# notification-service/app/utils/rabbitmq.py

class RabbitMQConsumer:
"""Consumer class for processing messages from RabbitMQ"""

async def start_consuming(self):
"""
Setup and start consuming:
1. Connect to RabbitMQ
2. Set QoS (prefetch_count=10)
3. Declare exchange 'user_events' (topic, durable)
4. Declare queue 'user_notifications' (durable)
5. Bind queue to exchange with routing_key="user.created"
6. Start async iterator over queue
"""
pass

async def handle_message(self, message):
"""
Process message:
- Parse JSON body
- Route to appropriate handler based on event type
- Acknowledge on success, nack on failure
"""
pass

async def send_welcome_email(self, user_data: dict):
"""Send welcome email to new user"""
# TODO: Implement email sending (SMTP, SendGrid, etc.)
pass

Startup Integration:

# main.py - Start consumer on application startup
@app.on_event("startup")
async def startup_event():
asyncio.create_task(rabbitmq_consumer.start_consuming())

Acceptance Criteria:

  • Queue user_notifications created.
  • Queue bound to exchange with routing key.
  • Consumer receives messages.
  • Message acknowledged after processing.
  • Failed messages handled (DLQ or retry).

Task 3.4: Error Handling & Reliability (20 points)​

Business Logic:

Message queue needs to be reliable:

  • Messages must not be lost if consumer crashes.
  • Failed messages need to be retried or moved to DLQ.
  • Consumer crash should not block other messages.

Reliability Patterns:

  1. Persistent Messages:
message = aio_pika.Message(
body=...,
delivery_mode=aio_pika.DeliveryMode.PERSISTENT, # Save to disk
)

  1. Manual Acknowledgment:
async with message.process():  # Auto-ack on success
try:
await handle_message(message)
except Exception:
await message.nack(requeue=True) # Requeue on failure

  1. Dead Letter Queue (DLQ):
# Declare main queue with DLQ settings
queue = await channel.declare_queue(
"user_notifications",
durable=True,
arguments={
"x-dead-letter-exchange": "dlx",
"x-dead-letter-routing-key": "failed",
"x-message-ttl": 300000, # 5 minutes
}
)

Acceptance Criteria:

  • Messages persistent (survive broker restart).
  • Failed messages requeued or sent to DLQ.
  • Consumer reconnects after connection loss.
  • No message loss in failure scenarios.

🌟 Bonus Tasks​

Bonus 3.1: Retry with Exponential Backoff (15 points)​

Guideline: Implement retry mechanism with exponential backoff: 1s β†’ 2s β†’ 4s β†’ 8s β†’ DLQ. Track retry count in message headers. Use separate retry exchange.

Bonus 3.2: Multiple Consumers (10 points)​

Guideline: Scale consumer horizontally by running multiple instances. Messages are distributed (competing consumers pattern). Test with 3 consumer instances.

Bonus 3.3: Event Schema Registry (10 points)​

Guideline: Create shared event schemas package. Define JSON Schema for each event type. Validate messages against schema before publish/consume.


πŸ“Š Rubric - Assignment 03​

CriteriaExcellent (100%)Good (80%)Satisfactory (60%)Needs Improvement (40%)
RabbitMQ Setup (15pts)Healthy, UI works, concepts clearWorks, minor issuesBasic setupNot running
Publisher (25pts)Events published, persistent, asyncWorks, missing persistenceBasic publishNot implemented
Consumer (30pts)Consumes, processes, acknowledgesWorks, ack issuesReceives messagesNot working
Error Handling (20pts)DLQ, retry, reconnectionPartial error handlingBasic try/catchNo error handling
Code Quality (10pts)Clean async code, documentedGood codeAcceptablePoor quality

Total: 100 points Bonus: 35 points


πŸ“ Assignment 04: SAGA Pattern - Order Service​

Unit 4 & 5 - SAGA Pattern (Happy Path & Failure Path)​

🎯 Learning Objectives​

  • Understand Distributed Transactions issues (ACID vs BASE).
  • Implement SAGA Pattern with Choreography.
  • Handle Compensating Transactions on failure.
  • Build Order Service with multi-service coordination.

πŸ“– Business Requirements​

Scenario​

Order creation involves multiple services:

  1. User Service: Verify user exists and authenticated.
  2. Product Service: Check stock availability, deduct stock.
  3. Order Service: Create order record.
  4. Notification Service: Send order confirmation.

Problem: Distributed Transaction

In a monolith, database transactions are used:

BEGIN;
UPDATE products SET quantity = quantity - 1;
INSERT INTO orders ...;
COMMIT; -- All or nothing

In microservices, each service has its own DB β†’ Cannot use single transaction.

Solution: SAGA Pattern

SAGA is a sequence of local transactions, where each transaction updates a service and publishes an event to trigger the next transaction.

Order Created Flow (Happy Path):
β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚ Order β”‚ β”‚ Product β”‚ β”‚ Order β”‚ β”‚Notificationβ”‚
β”‚ Service β”‚ β”‚ Service β”‚ β”‚ Service β”‚ β”‚ Service β”‚
β””β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”˜ β””β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”˜ β””β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”˜ β””β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”˜
β”‚ β”‚ β”‚ β”‚
β”‚ 1. Create β”‚ β”‚ β”‚
β”‚ Order β”‚ β”‚ β”‚
β”‚ (PENDING) β”‚ β”‚ β”‚
β”‚ β”‚ β”‚ β”‚
β”‚ 2. Publish ─────►│ β”‚ β”‚
β”‚ order.created β”‚ β”‚ β”‚
β”‚ β”‚ β”‚ β”‚
β”‚ β”‚ 3. Reserve β”‚ β”‚
β”‚ β”‚ Stock β”‚ β”‚
β”‚ β”‚ β”‚ β”‚
β”‚ β”‚ 4. Publish ─────►│ β”‚
β”‚ β”‚ stock.reserved β”‚ β”‚
β”‚ β”‚ β”‚ β”‚
β”‚ β”‚ β”‚ 5. Update β”‚
β”‚ β”‚ β”‚ Order β”‚
β”‚ β”‚ β”‚ (CONFIRMED) β”‚
β”‚ β”‚ β”‚ β”‚
β”‚ β”‚ β”‚ 6. Publish ─────►│
β”‚ β”‚ β”‚ order.confirmed β”‚
β”‚ β”‚ β”‚ β”‚
β”‚ β”‚ β”‚ β”‚ 7. Send
β”‚ β”‚ β”‚ β”‚ Email
β–Ό β–Ό β–Ό β–Ό

Failure Path (Compensating Transaction):

β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”     β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”     β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚ Order β”‚ β”‚ Product β”‚ β”‚ Order β”‚
β”‚ Service β”‚ β”‚ Service β”‚ β”‚ Service β”‚
β””β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”˜ β””β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”˜ β””β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”˜
β”‚ β”‚ β”‚
β”‚ 1. Order β”‚ β”‚
β”‚ (PENDING) β”‚ β”‚
β”‚ β”‚ β”‚
β”‚ 2. order.created─► β”‚
β”‚ β”‚ β”‚
β”‚ β”‚ 3. Check Stock β”‚
β”‚ β”‚ INSUFFICIENT! β”‚
β”‚ β”‚ β”‚
β”‚ β”‚ 4. Publish ─────►│
β”‚ β”‚ stock.failed β”‚
β”‚ β”‚ β”‚
β”‚ β”‚ β”‚ 5. COMPENSATE
β”‚ β”‚ β”‚ Update Order
β”‚ β”‚ β”‚ (CANCELLED)
β”‚ β”‚ β”‚
│◄───────────────────────────────────│
β”‚ order.cancelled β”‚
β–Ό β–Ό β–Ό


βœ… Required Tasks​

Task 4.1: Order Service Setup (20 points)​

Project Structure:

order-service/
β”œβ”€β”€ app/
β”‚ β”œβ”€β”€ main.py
β”‚ β”œβ”€β”€ api/
β”‚ β”‚ β”œβ”€β”€ orders.py
β”‚ β”‚ └── deps.py
β”‚ β”œβ”€β”€ config/
β”‚ β”œβ”€β”€ database/
β”‚ β”œβ”€β”€ models/
β”‚ β”‚ β”œβ”€β”€ order.py
β”‚ β”‚ └── order_item.py
β”‚ β”œβ”€β”€ repositories/
β”‚ β”‚ └── order_repository.py
β”‚ β”œβ”€β”€ schemas/
β”‚ β”‚ └── order.py
β”‚ β”œβ”€β”€ services/
β”‚ β”‚ └── order_service.py
β”‚ └── utils/
β”‚ β”œβ”€β”€ auth_client.py # Call User Service
β”‚ β”œβ”€β”€ product_client.py # Call Product Service
β”‚ └── rabbitmq.py # Event publisher
β”œβ”€β”€ alembic/
β”œβ”€β”€ Dockerfile
└── requirements.txt

Order Model:

ColumnTypeDescription
idIntegerPK
user_idIntegerUser who placed order
statusEnumPENDING, CONFIRMED, CANCELLED, SHIPPED, DELIVERED
total_amountDecimalTotal order value
created_atDateTimeOrder creation time
updated_atDateTimeLast update time

OrderItem Model:

ColumnTypeDescription
idIntegerPK
order_idIntegerFK to Order
product_idIntegerProduct ID
product_nameStringSnapshot of product name
quantityIntegerQuantity ordered
unit_priceDecimalPrice at order time

Why snapshot product data?

  • Product name/price can change.
  • Order needs to preserve information at the time of ordering.
  • Historical accuracy.

Acceptance Criteria:

  • Order Service runs on port 8003.
  • Order and OrderItem models created.
  • Migrations work.

Task 4.2: Order Creation API (25 points)​

Business Logic:

Create order endpoint needs to:

  1. Validate user (via User Service).
  2. Validate products exist (via Product Service).
  3. Create order with status PENDING.
  4. Publish order.created event.
  5. Return order (do not wait for stock reservation).

API Endpoint:

POST /orders
Authorization: Bearer <token>
Content-Type: application/json

Request:
{
"items": [
{
"product_id": 1,
"quantity": 2
},
{
"product_id": 3,
"quantity": 1
}
]
}

Response 201:
{
"id": 1,
"user_id": 5,
"status": "PENDING",
"total_amount": 299.97,
"items": [
{
"product_id": 1,
"product_name": "Product A",
"quantity": 2,
"unit_price": 99.99
},
{
"product_id": 3,
"product_name": "Product C",
"quantity": 1,
"unit_price": 99.99
}
],
"created_at": "2025-12-24T10:00:00Z"
}

Service Implementation Steps:

async def create_order(self, user_id: int, items: List[OrderItemCreate]) -> Order:
"""
Create order with SAGA pattern (step 1)

Steps:
1. Fetch product info from Product Service (validate products exist)
2. Snapshot product data (name, price at order time)
3. Calculate total amount
4. Create order with status PENDING
5. Publish 'order.created' event to RabbitMQ
6. Return order immediately (don't wait for stock)
"""
pass

Acceptance Criteria:

  • Order created with status PENDING.
  • Products fetched from Product Service.
  • Total calculated correctly.
  • Event published to RabbitMQ.
  • Response immediate (not waiting for stock).

Task 4.3: Stock Reservation - Product Service (25 points)​

Business Logic:

Product Service consumes order.created event and reserves stock:

Consumer Implementation Skeleton:

# product-service/app/utils/rabbitmq.py

async def handle_order_created(self, event_data: dict):
"""
Handle order.created event - Reserve stock

Steps:
1. Extract order_id and items from event
2. Check stock availability for ALL items first
3. If all available: deduct stock, publish 'stock.reserved'
4. If any insufficient: publish 'stock.failed' with reason
5. Invalidate product cache after stock changes
"""
pass

Events:

EventRouting KeyWhen
stock.reservedstock.reservedAll items reserved successfully
stock.failedstock.failedInsufficient stock

Acceptance Criteria:

  • Product Service consumes order.created.
  • Stock deducted when sufficient.
  • stock.reserved event published on success.
  • stock.failed event published on failure.
  • Cache invalidated after stock change.

Task 4.4: Order Status Update & Compensation (20 points)​

Business Logic:

Order Service consumes stock events to update order status.

Event Handlers Skeleton:

async def handle_stock_reserved(self, event_data: dict):
"""
Happy path: Update order to CONFIRMED
Publish 'order.confirmed' for notification
"""
pass

async def handle_stock_failed(self, event_data: dict):
"""
Compensation: Update order to CANCELLED
Record cancellation reason
Publish 'order.cancelled'
"""
pass

Order Status Flow:

PENDING ──────► CONFIRMED ──────► SHIPPED ──────► DELIVERED
β”‚
└──────────► CANCELLED (compensation)

Acceptance Criteria:

  • Order updated to CONFIRMED on success.
  • Order updated to CANCELLED on failure.
  • Cancellation reason recorded.
  • Appropriate events published.

🌟 Bonus Tasks​

Bonus 4.1: Orchestration Pattern (20 points)​

Guideline: Implement SAGA Orchestrator service instead of Choreography. Orchestrator coordinates steps, tracks SAGA state, handles rollbacks centrally. Create new saga_orchestrator service.

Bonus 4.2: Partial Failure Handling (15 points)​

Guideline: Handle case: Order has 3 items, 2 items have stock, 1 item out of stock. Options: (1) Cancel entire order, (2) Partial fulfillment. Implement configurable behavior.

Bonus 4.3: Order Status WebSocket (10 points)​

Guideline: Implement WebSocket endpoint /orders/{id}/status for real-time status updates. Client subscribes and receives updates when status changes.


πŸ“Š Rubric - Assignment 04​

CriteriaExcellent (100%)Good (80%)Satisfactory (60%)Needs Improvement (40%)
Order Service Setup (20pts)Complete with items modelBasic order modelPartialNot working
Order Creation (25pts)Full validation, event publishedWorks, minor issuesBasic creationBroken
Stock Reservation (25pts)Reserve + events for both pathsSuccess path onlyBasic consumerNot implemented
Compensation (20pts)Full compensation, status flowBasic cancellationPartialNo compensation
Code Quality (10pts)Clean, documented, proper asyncGoodAcceptablePoor

Total: 100 points Bonus: 45 points


πŸ“ Assignment 05: Performance - Redis Caching​

Unit 6 - Performance Patterns (Redis)​

🎯 Learning Objectives​

  • Understand caching strategies (Cache-aside, Read-through, Write-through).
  • Implement Cache-aside pattern with Redis.
  • Handle Cache Invalidation correctly.
  • Measure performance improvement.

πŸ“– Business Requirements​

Scenario​

Product Service receives many read requests for product details:

  • 80% requests are GET operations.
  • Same products are queried multiple times.
  • Database queries take ~50-100ms.

Problem: Database bottleneck with high traffic.

Solution: Redis caching to:

  • Reduce DB load.
  • Improve response time (< 10ms from cache).
  • Handle traffic spikes.

βœ… Required Tasks​

Task 5.1: Redis Setup (15 points)​

Docker Compose:

redis:
image: redis:7-alpine
container_name: redis-cache
ports:
- '6379:6379'
volumes:
- redis-data:/data
command: redis-server --appendonly yes --maxmemory 256mb --maxmemory-policy allkeys-lru
healthcheck:
test: ['CMD', 'redis-cli', 'ping']
interval: 10s
timeout: 5s
retries: 5

Configuration Options:

OptionValuePurpose
appendonly yesEnable AOFData persistence
maxmemory 256mbMemory limitPrevent OOM
maxmemory-policy allkeys-lruEviction policyLRU when memory full

Acceptance Criteria:

  • Redis container healthy.
  • redis-cli ping responds PONG.
  • Persistence enabled.

Task 5.2: Cache Manager Implementation (25 points)​

Business Logic:

Create CacheManager class to handle all cache operations:

Cache Key Strategy:

product:{product_id}        # Single product
products:list:{skip}:{limit} # Product list (paginated)
products:search:{query} # Search results

Implementation Skeleton:

# product-service/app/utils/cache.py

class CacheManager:
"""Redis cache manager with error handling"""

def __init__(self):
# TODO: Initialize Redis client with settings
pass

def get(self, key: str) -> Optional[Any]:
"""Get value from cache, return None on miss or error"""
pass

def set(self, key: str, value: Any, ttl: int = None) -> bool:
"""Set value with TTL, return success status"""
pass

def delete(self, key: str) -> bool:
"""Delete single key"""
pass

def delete_pattern(self, pattern: str) -> int:
"""Delete all keys matching pattern (e.g., 'products:list:*')"""
pass

def healthcheck(self) -> bool:
"""Return True if Redis is available"""
pass

Key Requirements:

  • Error handling: Cache failures should not crash the application.
  • Serialization: Use JSON for complex objects.
  • TTL: Default from settings, can be overridden per call.

Acceptance Criteria:

  • CacheManager with get/set/delete methods.
  • Error handling does not crash application.
  • TTL configurable.
  • Pattern-based deletion.

Task 5.3: Cache-Aside Pattern for Get Product (30 points)​

Business Logic:

Cache-Aside (Lazy Loading) Pattern:

  1. Check cache first.
  2. If HIT β†’ return cached data.
  3. If MISS β†’ query DB β†’ store in cache β†’ return.
β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”     1. GET /products/1    β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚ Client β”‚ ─────────────────────────►│ Product Service β”‚
β””β”€β”€β”€β”€β”€β”€β”€β”€β”˜ β””β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”˜
β”‚
2. Check β”‚ Cache
β–Ό
β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚ Redis β”‚
β”‚ product:1 β”‚
β””β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”˜
β”‚
β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”΄β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚ β”‚
3a. CACHE HIT 3b. CACHE MISS
β”‚ β”‚
β–Ό β–Ό
Return cached β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
data immediately β”‚ PostgreSQL β”‚
β””β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”˜
β”‚
4. Query DB
β”‚
5. Store in cache
β”‚
6. Return data

Service Implementation:

# product-service/app/services/product_service.py

class ProductService:
def get_product(self, product_id: int) -> Optional[Product]:
"""
Get product with cache-aside pattern
"""
cache_key = f"product:{product_id}"

# 1. Try cache first
cached = self.cache_manager.get(cache_key)
if cached:
logger.info(f"Cache HIT for product {product_id}")
return Product(**cached) # Deserialize

# 2. Cache miss - query DB
logger.info(f"Cache MISS for product {product_id}")
product = self.product_repository.get_by_id(product_id)

if product:
# 3. Store in cache
self.cache_manager.set(
cache_key,
product.to_dict(), # Serialize
ttl=settings.CACHE_TTL # 300 seconds default
)

return product

Acceptance Criteria:

  • First request queries DB and caches data.
  • Subsequent requests hit cache.
  • Response time < 10ms for cache hits.
  • Cache expires after TTL.

Task 5.4: Cache Invalidation (20 points)​

Business Logic:

Cache must be invalidated when data changes:

  • Product updated β†’ delete product:{id}
  • Product deleted β†’ delete product:{id}
  • Stock changed β†’ delete product:{id}

Invalidation Strategies:

StrategyWhenImplementation
Write-invalidateUpdate/DeleteDelete cache key
TTL expirationAutoRedis handles
Pattern invalidationBulk updatesDelete matching keys

Implementation in ProductService:

def update_product(self, product_id: int, data: ProductUpdate) -> Product:
"""Update product and invalidate cache"""
# 1. Update in DB
product = self.product_repository.update(product_id, data.dict())

# 2. Invalidate cache
self.cache_manager.delete(f"product:{product_id}")

# 3. Also invalidate list caches (they're now stale)
self.cache_manager.delete_pattern("products:list:*")

return product

def delete_product(self, product_id: int) -> bool:
"""Delete product and invalidate cache"""
# 1. Delete from DB
result = self.product_repository.delete(product_id)

# 2. Invalidate caches
self.cache_manager.delete(f"product:{product_id}")
self.cache_manager.delete_pattern("products:list:*")

return result

When Stock Changes (from Order Service event):

async def handle_stock_reserved(self, event_data: dict):
# ... deduct stock logic ...

# Invalidate cache
for item in items:
self.cache_manager.delete(f"product:{item['product_id']}")

Acceptance Criteria:

  • Cache invalidated on update.
  • Cache invalidated on delete.
  • Cache invalidated on stock change.
  • List caches also invalidated.

🌟 Bonus Tasks​

Bonus 5.1: User Profile Caching (15 points)​

Guideline: Apply caching for User Service's "Get User Profile" API. Implement cache-aside, invalidate on profile update. Track cache hit rate metrics.

Bonus 5.2: Cache Warming (10 points)​

Guideline: On service startup, pre-populate cache with frequently accessed products (top 100 by views). Query analytics or maintain a "hot products" list.

Bonus 5.3: Distributed Locking (15 points)​

Guideline: Implement distributed lock with Redis to prevent "thundering herd" - multiple requests querying DB simultaneously on cache miss. Use Redis SETNX for locking.


πŸ“Š Rubric - Assignment 05​

CriteriaExcellent (100%)Good (80%)Satisfactory (60%)Needs Improvement (40%)
Redis Setup (15pts)Full config, persistence, healthcheckWorks, basic configContainer runsNot running
Cache Manager (25pts)All methods, error handling, patternsBasic get/set/deletePartial implementationNot working
Cache-Aside (30pts)Full pattern, logging, metricsWorks correctlyBasic cachingBroken
Invalidation (20pts)All cases handled, patternsMost casesBasic invalidationMissing
Performance (10pts)Measured improvement, < 10msImproved responseSome improvementNo change

Total: 100 points Bonus: 40 points


πŸ“ Assignment 06: Observability - Tracing (Jaeger/OTel)​

Unit 6 - Observability (Tracing)​

🎯 Learning Objectives​

  • Understand concepts: Trace, Span, Context Propagation.
  • Setup OpenTelemetry instrumentation.
  • Integrate Jaeger for distributed tracing.
  • Debug cross-service requests.

πŸ“– Business Requirements​

Scenario​

When order creation fails, we need to trace the request across services:

  • How long does it take in each service?
  • Which service failed?
  • Which database query was slow?

Without tracing: Check logs of each service, difficult to correlate.

With tracing: Single view of the entire request journey.


βœ… Required Tasks​

Task 6.1: Jaeger Setup (15 points)​

Docker Compose:

jaeger:
image: jaegertracing/all-in-one:1.51
container_name: jaeger
ports:
- '5775:5775/udp' # Compact thrift (deprecated)
- '6831:6831/udp' # Compact thrift
- '6832:6832/udp' # Binary thrift
- '5778:5778' # Serve configs
- '16686:16686' # Jaeger UI
- '14268:14268' # Jaeger collector HTTP
- '14250:14250' # gRPC
- '9411:9411' # Zipkin compatible
- '4317:4317' # OTLP gRPC
- '4318:4318' # OTLP HTTP
environment:
- COLLECTOR_OTLP_ENABLED=true
- SPAN_STORAGE_TYPE=memory

Key Concepts:

ConceptDescription
TraceEnd-to-end journey of a request
SpanSingle operation in a trace (e.g., DB query, HTTP call)
ContextMetadata propagated across services (trace ID, span ID)
InstrumentationCode that creates and manages spans

Acceptance Criteria:

  • Jaeger UI accessible at :16686.
  • OTLP endpoint available at :4317.

Task 6.2: OpenTelemetry Setup (25 points)​

Dependencies:

opentelemetry-api
opentelemetry-sdk
opentelemetry-instrumentation-fastapi
opentelemetry-instrumentation-sqlalchemy
opentelemetry-instrumentation-httpx
opentelemetry-instrumentation-redis
opentelemetry-exporter-otlp

Tracing Setup Skeleton:

# app/utils/tracing.py

def setup_tracing(app, service_name: str, service_version: str = "1.0.0"):
"""
Configure OpenTelemetry tracing

Steps:
1. Create Resource with service identity (name, version, environment)
2. Create TracerProvider with resource
3. Create OTLPSpanExporter pointing to Jaeger (port 4317)
4. Add BatchSpanProcessor for performance
5. Set global tracer provider
6. Auto-instrument: FastAPI, SQLAlchemy, HTTPX, Redis
"""
pass

# Usage in main.py
setup_tracing(app, service_name="user-service", service_version="1.0.0")

Required Instrumentations per Service:

ServiceFastAPISQLAlchemyHTTPXRedisRabbitMQ
Userβœ“βœ“--Manual
Productβœ“βœ“βœ“βœ“-
Orderβœ“βœ“βœ“-Manual
Notificationβœ“---Manual

Acceptance Criteria:

  • Tracing configured in all services.
  • Auto-instrumentation for FastAPI, SQLAlchemy, HTTPX, Redis.
  • Spans exported to Jaeger.

Task 6.3: Instrument All Services (30 points)​

Apply tracing to:

ServiceInstrumentation
User ServiceFastAPI, SQLAlchemy
Product ServiceFastAPI, SQLAlchemy, Redis, HTTPX
Order ServiceFastAPI, SQLAlchemy, HTTPX, RabbitMQ (manual)
Notification ServiceFastAPI, RabbitMQ (manual)

Manual Span for RabbitMQ:

# For async message handling, create manual spans

from opentelemetry import trace

tracer = trace.get_tracer(__name__)

async def handle_order_created(self, message):
# Extract trace context from message headers (if available)

with tracer.start_as_current_span(
"handle_order_created",
kind=trace.SpanKind.CONSUMER,
attributes={
"messaging.system": "rabbitmq",
"messaging.operation": "process",
}
) as span:
data = json.loads(message.body)
span.set_attribute("order_id", data["order_id"])

try:
await self.process_order(data)
except Exception as e:
span.record_exception(e)
span.set_status(trace.StatusCode.ERROR)
raise

Acceptance Criteria:

  • User Service traced.
  • Product Service traced (including Redis).
  • Order Service traced (including HTTPX calls).
  • Manual spans for RabbitMQ handlers.

Task 6.4: Trace Analysis (20 points)​

Business Logic:

Test and analyze traces in Jaeger UI:

Test Scenarios:

  1. Happy Path: Create order successful
  • Trace should show: Order Service β†’ Product Service (validate) β†’ DB insert β†’ RabbitMQ publish
  1. Failure Path: Insufficient stock
  • Trace should show: Order Service β†’ Product Service β†’ Error span
  1. Cache Hit vs Miss:
  • Cache HIT: Short span, no DB call
  • Cache MISS: DB span visible

Jaeger UI Features:

FeatureUsage
Service dropdownFilter by service
Operation dropdownFilter by endpoint
Tags searchFind by order_id, user_id
Compare tracesCompare slow vs fast
DependenciesService dependency graph

Acceptance Criteria:

  • Can find traces by service.
  • Can filter by operation.
  • Can see full trace across services.
  • Can identify slow spans.

🌟 Bonus Tasks​

Bonus 6.1: Custom Spans & Attributes (10 points)​

Guideline: Add custom spans for business-critical operations (e.g., stock validation, payment processing). Add custom attributes (order_id, product_count, total_amount).

Bonus 6.2: Trace Context in Messages (15 points)​

Guideline: Propagate trace context via RabbitMQ messages. Include traceparent header in message properties. Consumer extracts and continues trace.

Bonus 6.3: Sampling Strategy (10 points)​

Guideline: Configure trace sampling (not 100% in production). Implement probabilistic sampling (10%) or rate limiting sampling (100 traces/sec).


πŸ“Š Rubric - Assignment 06​

CriteriaExcellent (100%)Good (80%)Satisfactory (60%)Needs Improvement (40%)
Jaeger Setup (15pts)UI works, OTLP enabledBasic setupContainer runsNot running
OTel Setup (25pts)All instrumentationsMissing 1-2Basic FastAPI onlyNot working
All Services (30pts)All 4 services traced3 services2 services1 or none
Trace Analysis (20pts)Full analysis, can debugBasic viewingCan find tracesCannot use UI
Code Quality (10pts)Clean, configurableGoodAcceptablePoor

Total: 100 points Bonus: 35 points


πŸ“ Assignment 07: Observability - Logging (Loki)​

Unit 7 - Observability (Logging)​

🎯 Learning Objectives​

  • Understand centralized logging concepts.
  • Setup Loki + Promtail for log aggregation.
  • Implement structured logging.
  • Query and analyze logs effectively.

πŸ“– Business Requirements​

Scenario​

With multiple services, logs are scattered:

  • User Service logs β†’ container A
  • Product Service logs β†’ container B
  • Order Service logs β†’ container C

Problem: Difficult to correlate logs when debugging.

Solution: Centralized logging with Loki.

  • All logs β†’ Loki
  • Query from Grafana
  • Correlate with trace_id

βœ… Required Tasks​

Task 7.1: Loki + Promtail Setup (20 points)​

Loki Configuration:

# deployment/observability/loki/loki-config.yml
auth_enabled: false

server:
http_listen_port: 3100

common:
path_prefix: /loki
storage:
filesystem:
chunks_directory: /loki/chunks
rules_directory: /loki/rules
replication_factor: 1
ring:
kvstore:
store: inmemory

schema_config:
configs:
- from: 2020-10-24
store: boltdb-shipper
object_store: filesystem
schema: v11
index:
prefix: index_
period: 24h

Promtail Configuration:

# deployment/observability/promtail/promtail-config.yml
server:
http_listen_port: 9080

positions:
filename: /tmp/positions.yaml

clients:
- url: http://loki:3100/loki/api/v1/push

scrape_configs:
- job_name: docker
docker_sd_configs:
- host: unix:///var/run/docker.sock
refresh_interval: 5s
relabel_configs:
- source_labels: ['__meta_docker_container_name']
regex: '/(.*)'
target_label: 'container'
- source_labels: ['__meta_docker_container_label_service']
target_label: 'service'

Docker Compose:

loki:
image: grafana/loki:2.9.0
ports:
- '3100:3100'
volumes:
- ./deployment/observability/loki/loki-config.yml:/etc/loki/local-config.yaml
- loki-data:/loki
command: -config.file=/etc/loki/local-config.yaml

promtail:
image: grafana/promtail:2.9.0
volumes:
- ./deployment/observability/promtail/promtail-config.yml:/etc/promtail/config.yml
- /var/run/docker.sock:/var/run/docker.sock
- /var/lib/docker/containers:/var/lib/docker/containers:ro
command: -config.file=/etc/promtail/config.yml

Acceptance Criteria:

  • Loki running at :3100.
  • Promtail collecting Docker logs.
  • Logs visible in Grafana.

Task 7.2: Structured Logging (25 points)​

Business Logic:

JSON structured logs for easy parsing and querying:

Log Format:

{
"timestamp": "2025-12-24T10:00:00.000Z",
"level": "INFO",
"service": "order-service",
"trace_id": "abc123...",
"span_id": "def456...",
"message": "Order created successfully",
"order_id": 1,
"user_id": 5,
"total_amount": 299.97
}

Python Logging Configuration Skeleton:

# app/config/logging.py

class JSONFormatter(logging.Formatter):
"""
JSON formatter with trace context

Output format:
{
"timestamp": "ISO8601",
"level": "INFO",
"service": "service-name",
"trace_id": "from OpenTelemetry",
"message": "log message",
...extra fields
}
"""

def format(self, record: logging.LogRecord) -> str:
# TODO: Get trace context from OpenTelemetry
# TODO: Build log_data dict
# TODO: Include extra fields from record
pass

def setup_logging(service_name: str):
"""Configure JSON logging to stdout"""
# TODO: Create handler with JSONFormatter
# TODO: Set root logger
# TODO: Reduce noise from uvicorn, httpx
pass

Usage Example:

logger = logging.getLogger(__name__)
logger.info("Order created", extra={"order_id": 1, "user_id": 5})

Acceptance Criteria:

  • Logs in JSON format.
  • trace_id included (for correlation).
  • Custom fields supported.
  • Log levels appropriate.

Task 7.3: Log Aggregation Verification (25 points)​

Business Logic:

Verify logs from all services are collected and queryable.

Test Scenarios:

  1. All Services Logging:
  • Start all services.
  • Make API requests.
  • Verify logs appear in Loki.
  1. Cross-Service Correlation:
  • Create order (hits multiple services).
  • Find logs by trace_id.
  • Should see logs from Order, Product, User services.
  1. Error Logging:
  • Trigger error (invalid product ID).
  • Search for ERROR level logs.
  • Verify stack trace present.

Grafana Loki Queries (LogQL):

# All logs from order-service
{container="order-service"}

# Error logs only
{container=~".*-service"} |= "ERROR"

# Filter by trace_id
{container=~".*-service"} | json | trace_id="abc123..."

# Search for specific order
{container="order-service"} | json | order_id=1

# Rate of errors in last hour
rate({container="order-service"} |= "ERROR" [1h])

Acceptance Criteria:

  • Logs from all services visible.
  • Can filter by service.
  • Can search by trace_id.
  • Can search by custom fields.

Task 7.4: Log-based Alerting (20 points)​

Business Logic:

Alert when error rate exceeds threshold.

Grafana Alert Rule:

# Alert when >10 errors in 5 minutes
name: High Error Rate
condition: count > 10
query: |
count_over_time(
{container=~".*-service"} |= "ERROR" [5m]
)
for: 1m
annotations:
summary: 'High error rate detected'
description: 'More than 10 errors in the last 5 minutes'

Acceptance Criteria:

  • Alert rule created in Grafana.
  • Alert fires on error spike (test).
  • Alert resolves when errors decrease.

🌟 Bonus Tasks​

Bonus 7.1: ELK Stack Alternative (20 points)​

Guideline: Setup ELK Stack (Elasticsearch, Logstash, Kibana) instead of Loki. Configure Filebeat to ship logs. Create Kibana dashboard.

Bonus 7.2: Log Retention Policy (10 points)​

Guideline: Configure log retention: keep 7 days detailed, 30 days aggregated. Implement log rotation. Calculate storage requirements.

Bonus 7.3: Audit Logging (10 points)​

Guideline: Separate audit logs for security events (login, permission changes). Store in separate index/stream. Never delete.


πŸ“Š Rubric - Assignment 07​

CriteriaExcellent (100%)Good (80%)Satisfactory (60%)Needs Improvement (40%)
Loki Setup (20pts)Full config, Promtail worksBasic setupContainer runsNot running
Structured Logging (25pts)JSON, trace_id, custom fieldsJSON, missing trace_idBasic JSONUnstructured
Aggregation (25pts)All services, can queryMost servicesSome logs visibleCannot query
Alerting (20pts)Alert works, testedAlert configuredPartialNo alerting
Code Quality (10pts)Clean, reusableGoodAcceptablePoor

Total: 100 points Bonus: 40 points


πŸ“ Assignment 08: Observability - Metrics (Prometheus/Grafana)​

Unit 9 - Observability (Metrics)​

🎯 Learning Objectives​

  • Understand metrics types: Counter, Gauge, Histogram, Summary.
  • Setup Prometheus for metrics collection.
  • Build Grafana dashboards.
  • Implement custom application metrics.
  • Setup alerting based on metrics.

πŸ“– Business Requirements​

Scenario​

The Operations team needs to monitor the system:

  • Request rate (requests per second).
  • Error rate (% of failed requests).
  • Response latency (p50, p90, p99).
  • Resource usage (CPU, memory).
  • Business metrics (orders/hour, revenue).

Goal: Real-time visibility into system health.


βœ… Required Tasks​

Task 8.1: Prometheus Setup (20 points)​

Prometheus Configuration:

# deployment/observability/prometheus/prometheus.yml
global:
scrape_interval: 15s
evaluation_interval: 15s

alerting:
alertmanagers:
- static_configs:
- targets: []

rule_files:
- /etc/prometheus/rules/*.yml

scrape_configs:
# Prometheus self-monitoring
- job_name: 'prometheus'
static_configs:
- targets: ['localhost:9090']

# User Service
- job_name: 'user-service'
static_configs:
- targets: ['user-service:8001']
metrics_path: /metrics

# Product Service
- job_name: 'product-service'
static_configs:
- targets: ['product-service:8002']
metrics_path: /metrics

# Order Service
- job_name: 'order-service'
static_configs:
- targets: ['order-service:8003']
metrics_path: /metrics

# Redis
- job_name: 'redis'
static_configs:
- targets: ['redis-exporter:9121']

# PostgreSQL
- job_name: 'postgres'
static_configs:
- targets: ['postgres-exporter:9187']

Docker Compose:

prometheus:
image: prom/prometheus:v2.47.0
container_name: prometheus
ports:
- '9090:9090'
volumes:
- ./deployment/observability/prometheus/prometheus.yml:/etc/prometheus/prometheus.yml
- ./deployment/observability/prometheus/rules:/etc/prometheus/rules
- prometheus-data:/prometheus
command:
- '--config.file=/etc/prometheus/prometheus.yml'
- '--storage.tsdb.path=/prometheus'
- '--web.enable-lifecycle'
healthcheck:
test: ['CMD', 'wget', '--spider', '-q', 'localhost:9090/-/healthy']

Acceptance Criteria:

  • Prometheus UI accessible at :9090.
  • All services discovered in Targets.
  • Metrics being scraped.

Task 8.2: Application Metrics (25 points)​

Metrics Types:

TypeUse CaseExample
CounterMonotonically increasingTotal requests, errors
GaugeCan go up/downActive connections, queue size
HistogramDistribution of valuesRequest latency, order value
SummarySimilar to histogramResponse size percentiles

Implementation with prometheus-fastapi-instrumentator:

# app/utils/metrics.py

def setup_metrics(app, service_name: str):
"""
Setup Prometheus metrics for FastAPI

1. Create Instrumentator with config:
- excluded_handlers: ["/metrics", "/health"]
- should_group_status_codes: False
2. Add default metrics (latency, requests)
3. Expose /metrics endpoint
"""
pass

# Custom business metrics to define:
# - orders_created_total (Counter with labels: status)
# - order_value_dollars (Histogram with buckets)
# - http_requests_in_progress (Gauge)

Recording Business Metrics:

# In OrderService - increment counter on order creation
ORDERS_CREATED.labels(status="success").inc()
ORDER_VALUE.observe(float(order.total_amount))

Acceptance Criteria:

  • /metrics endpoint returns Prometheus format.
  • Default HTTP metrics (latency, count).
  • Custom business metrics recording.

Task 8.3: Grafana Dashboards (30 points)​

Grafana Setup:

grafana:
image: grafana/grafana:10.1.0
container_name: grafana
ports:
- '3000:3000'
environment:
- GF_SECURITY_ADMIN_USER=admin
- GF_SECURITY_ADMIN_PASSWORD=admin123
- GF_USERS_ALLOW_SIGN_UP=false
volumes:
- ./deployment/observability/grafana/provisioning:/etc/grafana/provisioning
- ./deployment/observability/grafana/dashboards:/var/lib/grafana/dashboards
- grafana-data:/var/lib/grafana

Dashboard Provisioning:

# deployment/observability/grafana/provisioning/dashboards/default.yml
apiVersion: 1

providers:
- name: 'default'
orgId: 1
folder: ''
type: file
disableDeletion: false
editable: true
options:
path: /var/lib/grafana/dashboards

Required Dashboard Panels:

PanelPromQL QueryType
Request Ratesum(rate(http_requests_total[5m])) by (service)Graph
Error Raterate(http_requests_total{status=~"5.."}[5m]) / rate(http_requests_total[5m]) * 100Graph
P99 Latencyhistogram_quantile(0.99, http_request_duration_seconds_bucket)Graph
Active Requestshttp_requests_in_progressGauge
Orders/Hourincrease(orders_created_total[1h])Stat

Acceptance Criteria:

  • Grafana UI accessible at :3000.
  • Prometheus data source configured.
  • Overview dashboard with key metrics.
  • Service-specific dashboard.

Task 8.4: Alerting Rules (15 points)​

Required Alert Rules:

AlertConditionSeverity
HighErrorRateError rate > 5% for 5mincritical
HighLatencyP99 > 2s for 5minwarning
ServiceDownup == 0 for 1mincritical
HighMemoryUsageMemory > 512MB for 5minwarning

Alert Rule File Location: deployment/observability/prometheus/rules/alerts.yml

Acceptance Criteria:

  • Alert rules loaded in Prometheus.
  • Alerts fire correctly when triggered.
  • Alerts visible in Grafana.

🌟 Bonus Tasks​

Bonus 8.1: Custom Grafana Dashboard (15 points)​

Guideline: Create business dashboard showing: orders per hour, revenue, top products, user signups. Use variables for time range selection.

Bonus 8.2: Alertmanager Integration (15 points)​

Guideline: Setup Alertmanager to route alerts via Slack/Email/Discord. Configure silencing and inhibition rules.

Bonus 8.3: SLI/SLO Dashboard (15 points)​

Guideline: Define SLOs (99.9% availability, p99 < 500ms). Create error budget dashboard. Track SLO compliance over time.


πŸ“Š Rubric - Assignment 08​

CriteriaExcellent (100%)Good (80%)Satisfactory (60%)Needs Improvement (40%)
Prometheus Setup (20pts)Full config, all targetsMost targetsBasic setupNot working
App Metrics (25pts)Default + custom businessDefault metricsPartialNo metrics
Dashboards (30pts)Multiple dashboards, polishedOverview dashboardBasic panelsNo dashboards
Alerting (15pts)Rules work, testedRules configuredPartialNo alerts
Code Quality (10pts)Clean, documentedGoodAcceptablePoor

Total: 100 points Bonus: 45 points


πŸŽ“ Final Project: End-to-End Integration & Demo​

🎯 Objectives​

  • Deploy the entire microservices stack.
  • Demonstrate the E2E user journey.
  • Verify observability stack.
  • Present architecture decisions.

πŸ“‹ Final Project Requirements​

Part 1: Complete Deployment (30 points)​

Checklist:

ComponentRequirement
User ServiceRunning, healthy
Product ServiceRunning, cached
Order ServiceRunning, messaging
Notification ServiceConsuming messages
PostgreSQL (3 instances)Data persisted
RedisCaching working
RabbitMQQueues active
PrometheusScraping all services
GrafanaDashboards visible
LokiLogs aggregated
JaegerTraces visible

Verification Commands:

# Check all containers
docker compose ps

# Check service health
curl http://localhost:8001/health
curl http://localhost:8002/health
curl http://localhost:8003/health

# Check Prometheus targets
curl http://localhost:9090/api/v1/targets

# Check RabbitMQ queues
curl -u guest:guest http://localhost:15672/api/queues


Part 2: E2E User Journey Demo (40 points)​

Scenario Script:

# 1. Register User
curl -X POST http://localhost:8001/register \
-H "Content-Type: application/json" \
-d '{"username": "demo_user", "password": "Demo@123"}'

# 2. Login
TOKEN=$(curl -s -X POST http://localhost:8001/login \
-H "Content-Type: application/x-www-form-urlencoded" \
-d "username=demo_user&password=Demo@123" | jq -r '.access_token')

# 3. Browse Products (should hit cache after first request)
curl http://localhost:8002/products
curl http://localhost:8002/products # Cache hit

# 4. Create Product (if admin)
curl -X POST http://localhost:8002/products \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-d '{"name": "Demo Product", "price": 99.99, "quantity": 100}'

# 5. Create Order
curl -X POST http://localhost:8003/orders \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-d '{"items": [{"product_id": 1, "quantity": 2}]}'

# 6. Check Notification Service logs (should show order processed)
docker compose logs notification-service | grep "Processing order"

Demo Requirements:

StepVerification
RegistrationUser created, 201 response
LoginToken returned
Browse ProductsProducts listed, cache metrics change
Create OrderOrder created, stock reduced
NotificationConsumer log shows processing
TracingFull trace visible in Jaeger
MetricsDashboard shows request spike
LogsLogs searchable by trace_id

Part 3: Failure Scenarios (20 points)​

Test Resilience:

ScenarioExpected Behavior
Kill Product ServiceOrder Service returns 503, auto-recovers
Kill RedisProduct Service fallback to DB (slower)
Kill RabbitMQOrder created, notification delayed
Invalid Token401 Unauthorized
Insufficient StockOrder fails gracefully

Demo:

# Test: Kill Product Service
docker compose stop product-service

# Order should fail gracefully
curl -X POST http://localhost:8003/orders \
-H "Authorization: Bearer $TOKEN" \
-d '{"items": [{"product_id": 1, "quantity": 1}]}'
# Expected: 503 Service Unavailable

# Restart
docker compose start product-service

# Order should work now
curl -X POST http://localhost:8003/orders \
-H "Authorization: Bearer $TOKEN" \
-d '{"items": [{"product_id": 1, "quantity": 1}]}'
# Expected: 201 Created


Part 4: Architecture Presentation (10 points)​

Present:

  1. Architecture Overview
  • Service boundaries.
  • Communication patterns.
  • Database per service.
  1. Key Design Decisions
  • Why separate Auth service?
  • Why RabbitMQ for notifications?
  • Why Redis for products (not orders)?
  1. Trade-offs
  • Consistency vs Availability.
  • Complexity vs Simplicity.
  • Performance vs Cost.
  1. Future Improvements
  • API Gateway (Kong).
  • Service Mesh (Istio).
  • CI/CD Pipeline.

πŸ“Š Final Project Rubric​

CriteriaExcellent (100%)Good (80%)Satisfactory (60%)Needs Improvement (40%)
Deployment (30pts)All components runningMissing 1-2Core services onlyMultiple failures
E2E Demo (40pts)Full journey, all verificationsMost steps workCore flow worksBroken flow
Failure Handling (20pts)Graceful degradationMost scenariosSome handlingCrashes
Presentation (10pts)Clear, insightfulGood explanationBasicPoor

Total: 100 points


πŸ“š Course Summary & Grading​

Overall Progress Tracking​

AssignmentTopicPointsBonus
01User Service Extraction10030
02Product Service & API Gateway10040
03Async Communication10030
04SAGA Pattern - Order Service10045
05Performance - Redis Caching10040
06Observability - Tracing10035
07Observability - Logging10040
08Observability - Metrics10045
FinalE2E Integration100-
Total900305

Grading Scale​

GradePercentagePoints (of 900)
A+95-100%855-900
A90-94%810-854
B+85-89%765-809
B80-84%720-764
C+75-79%675-719
C70-74%630-674
D60-69%540-629
F< 60%< 540

Bonus Points: Can increase grade (max +10%)


πŸ“– Additional Resources​

Documentation​

Architecture Patterns​

Video Courses​


Last Updated: 2025-12-24 Version: 1.0.0 Syllabus Reference: Advanced Microservices Architecture