Software Requirements Specification (SRS)
E-Commerce Microservices Platformβ
Version: 1.0.0
Date: 2025-12-24
Based on: Advanced Microservices Architecture Training Syllabus
Prerequisite: Completed mono-src (Building High-Performance APIs with FastAPI)
1. Introductionβ
1.1 Purposeβ
This document describes the software requirements for the E-Commerce Microservices Platform - a microservices system migrated from a monolithic application (mono-src), serving the purpose of learning microservices architecture according to the training syllabus.
1.2 Scopeβ
The system includes:
- 4 Independent Microservices (User, Product, Order, Notification)
- API Gateway (Kong)
- Asynchronous Communication (RabbitMQ)
- Distributed Caching (Redis)
- Full Observability Stack (Prometheus, Grafana, Loki, Jaeger)
1.3 Prerequisitesβ
| Requirement | Description |
|---|---|
| mono-src | Completed monolithic project with FastAPI |
| Docker | Docker Desktop installed |
| Python | Python 3.11+ |
| Knowledge | FastAPI, SQLAlchemy, Pydantic, JWT |
1.4 Definitionsβ
| Term | Definition |
|---|---|
| Bounded Context | Logical boundary of a domain in DDD |
| Strangler Fig | Migration pattern from monolith to microservices |
| SAGA | Pattern for handling distributed transactions |
| Choreography | SAGA pattern using events, without an orchestrator |
| Orchestration | SAGA pattern with a central coordinator |
| Cache-aside | Caching pattern: check cache β miss β load from DB β update cache |
2. System Architectureβ
2.1 High-Level Architectureβ
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
β CLIENT LAYER β
β Svelte SPA (Port 5173) β
βββββββββββββββββββββββββββββββββββ¬ββββββββββββββββββββββββββββββββββββββββββββ
β
βββββββββββββββββββββββββββββββββββΌββββββββββββββββββββββββββββββββββββββββββββ
β API GATEWAY β
β Kong (Port 8000) β
β β’ Routing β’ Rate Limiting β’ Authentication β
ββββββββββ¬βββββββββββββββββββ¬βββββββββββββββββββ¬βββββββββββββββββββββββββββββββ
β β β
βΌ βΌ βΌ
βββββββββββββββββββ βββββββββββββββββββ βββββββββββββββββββ
β USER SERVICE β β PRODUCT SERVICE β β ORDER SERVICE β
β (Port 8001) β β (Port 8002) β β (Port 8003) β
β β β β β β
β β’ Registration β β β’ CRUD Products β β β’ Create Orders β
β β’ Login/JWT β β β’ Cache (Redis) β β β’ SAGA Pattern β
β β’ Token Verify β β β’ Stock Mgmt β β β’ Event Publish β
ββββββββββ¬βββββββββ ββββββββββ¬βββββββββ ββββββββββ¬βββββββββ
β β β
βΌ βΌ βΌ
βββββββββββββββββββ βββββββββββββββββββ βββββββββββββββββββ
β PostgreSQL β β PostgreSQL β β PostgreSQL β
β user_db β β product_db β β order_db β
βββββββββββββββββββ ββββββββββ¬βββββββββ ββββββββββ¬βββββββββ
β β
ββββββββββΌβββββββββ β
β REDIS β β
β Cache Layer β β
βββββββββββββββββββ β
β
ββββββββββββββββββββββββββββββββββββββββββββββββΌβββββββββββββββββββββββββββββββ
β MESSAGE BROKER β
β RabbitMQ (Port 5672) β
β β
β Exchanges: Queues: β
β β’ order_events (topic) β’ order_notifications β
β β’ user_events (topic) β’ inventory_updates β
βββββββββββββββββββββββββββββββββββ¬ββββββββββββββββββββββββββββββββββββββββββββ
β
βββββββββββββββΌββββββββββββββ
β NOTIFICATION SERVICE β
β (Port 8004) β
β β
β β’ Consume order.created β
β β’ Send notifications β
βββββββββββββββββββββββββββββ
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
β OBSERVABILITY STACK β
βββββββββββββββββββ¬ββββββββββββββββββ¬ββββββββββββββββββ¬ββββββββββββββββββββββββ€
β Prometheus β Grafana β Loki β Jaeger β
β (Port 9090) β (Port 3000) β (Port 3100) β (Port 16686) β
β β β β β
β Metrics β Dashboards β Log Aggregationβ Distributed Tracing β
β Collection β Visualization β (via Promtail) β (OpenTelemetry) β
βββββββββββββββββββ΄ββββββββββββββββββ΄ββββββββββββββββββ΄ββββββββββββββββββββββββ
2.2 Service Communication Matrixβ
| From | To | Method | Purpose |
|---|---|---|---|
| Client | Kong | REST | All API requests |
| Kong | User Service | REST | Auth routes |
| Kong | Product Service | REST | Product routes |
| Kong | Order Service | REST | Order routes |
| Product Service | User Service | REST | Token validation |
| Order Service | User Service | REST | Token validation |
| Order Service | Product Service | REST | Stock validation |
| Order Service | RabbitMQ | AMQP | Publish order.created |
| Notification Service | RabbitMQ | AMQP | Consume order.created |
2.3 Technology Stackβ
| Layer | Technology | Version |
|---|---|---|
| API Gateway | Kong | 3.x |
| Backend Framework | FastAPI | 0.104+ |
| ORM | SQLAlchemy | 2.0 |
| Migrations | Alembic | 1.12+ |
| Validation | Pydantic | 2.0 |
| Auth | python-jose, bcrypt | Latest |
| Database | PostgreSQL | 15 |
| Cache | Redis | 7 |
| Message Broker | RabbitMQ | 3.12 |
| Tracing | OpenTelemetry, Jaeger | Latest |
| Metrics | Prometheus | Latest |
| Logging | Loki, Promtail | Latest |
| Visualization | Grafana | Latest |
| Container | Docker, Docker Compose | Latest |
| Frontend | Svelte, SvelteKit | 5.x |
3. Functional Requirements by Training Unitβ
3.1 Unit 1: Microservices Fundamentals & Refactoring (Assignment 01)β
Context: Strangler Fig Patternβ
Start migration from mono-src to microservices by extracting the User Service into an independent service.
REQ-MS-1.1: User Service Extractionβ
| ID | REQ-MS-1.1 |
|---|---|
| Description | Extract User domain from monolith into an independent microservice |
| Priority | Critical |
| Prerequisite | Completed mono-src project |
Business Context:
- Team decides to migrate to microservices for independent scaling.
- User Service selected first due to clear bounded context.
- Apply Strangler Fig: run mono + micro in parallel during migration.
Functional Requirements:
FR-1.1.1: Service Structure
user-service/
βββ app/
β βββ __init__.py
β βββ main.py # FastAPI application entry
β βββ api/
β β βββ __init__.py
β β βββ auth.py # Auth endpoints
β β βββ deps.py # Dependencies
β βββ config/
β β βββ __init__.py
β β βββ settings.py # Environment config
β βββ database/
β β βββ __init__.py
β β βββ database.py # DB connection
β βββ models/
β β βββ __init__.py
β β βββ user.py # User SQLAlchemy model
β βββ repositories/
β β βββ __init__.py
β β βββ base.py # Generic repository
β β βββ 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/
β βββ env.py
β βββ versions/
βββ tests/
βββ Dockerfile
βββ requirements.txt
βββ alembic.ini
βββ start.sh # Auto-migration startup
FR-1.1.2: Database per Service
| Attribute | Value |
|---|---|
| Database | PostgreSQL |
| Database Name | user_service_db |
| Port | 5433 (different from mono) |
| Isolation | Completely isolated, not shared with other services |
FR-1.1.3: API Endpoints
| Endpoint | Method | Description | Request | Response |
|---|---|---|---|---|
/health | GET | Health check | - | {"status": "healthy", "service": "user-service"} |
/register | POST | User registration | UserCreate | UserResponse (201) |
/login | POST | User login | OAuth2 form | Token (200) |
/validate-token | POST | Token validation | {"token": "..."} | {"valid": true, "user_id": 1, "username": "..."} |
/users/me | GET | Current user info | Bearer token | UserResponse |
FR-1.1.4: Token Validation Endpoint (NEW)
New endpoint for other services to validate JWT tokens without sharing the secret key:
POST /validate-token
Content-Type: application/json
Request:
{
"token": "eyJhbGciOiJIUzI1NiIs..."
}
Response 200 (Valid):
{
"valid": true,
"user_id": 1,
"username": "john_doe"
}
Response 200 (Invalid):
{
"valid": false,
"user_id": null,
"username": null
}
FR-1.1.5: Dockerization
Dockerfile requirements:
- Python 3.11-slim base image
- Multi-stage build (optional but recommended)
- Non-root user
- Health check command
- Auto-run migrations on startup
Acceptance Criteria:
| # | Criteria | Verification |
|---|---|---|
| 1 | Service runs independently on port 8001 | curl http://localhost:8001/health |
| 2 | Separate database (user_service_db) | Check PostgreSQL |
| 3 | User registration successful | POST /register returns 201 |
| 4 | Login returns JWT token | POST /login returns token |
| 5 | Token validation endpoint works | POST /validate-token |
| 6 | Alembic migrations run automatically | Container logs |
| 7 | Docker container healthy | docker ps shows healthy |
3.2 Unit 2: API Gateway (Assignment 02)β
REQ-MS-2.1: Product Service Extractionβ
| ID | REQ-MS-2.1 |
|---|---|
| Description | Extract Product domain into an independent microservice |
| Priority | Critical |
Business Context:
- Product catalog has high traffic, needs separate scaling.
- Needs caching layer to reduce DB load.
- Product Service must verify user tokens with User Service.
FR-2.1.1: Service Structure
product-service/
βββ app/
β βββ main.py
β βββ api/
β β βββ products.py # Product 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 # Call User Service
β βββ cache.py # Redis utilities
βββ alembic/
βββ Dockerfile
βββ requirements.txt
FR-2.1.2: Inter-Service Authentication
Product Service DOES NOT decode JWT directly. Instead:
- Receive Bearer token from request header
- Call User Service
/validate-tokenendpoint - If valid β continue processing
- If invalid β return 401
FR-2.1.3: API Endpoints
| Endpoint | Method | Auth | Description |
|---|---|---|---|
/health | GET | No | Health check |
/products | GET | No | List products (paginated) |
/products/{id} | GET | No | Get product by ID |
/products | POST | Yes | Create product |
/products/{id} | PUT | Yes | Update product |
/products/{id} | DELETE | Yes | Delete product |
/products/{id}/stock | PUT | Yes | Update stock quantity |
REQ-MS-2.2: Kong API Gateway Setupβ
| ID | REQ-MS-2.2 |
|---|---|
| Description | Setup Kong Gateway to route traffic to services |
| Priority | Critical |
Business Context:
- Single entry point for all API requests.
- Centralized routing, rate limiting, logging.
- Easy to add/remove services.
FR-2.2.1: Kong Configuration
| Component | Configuration |
|---|---|
| Kong Port | 8000 (proxy), 8001 (admin) |
| Database | PostgreSQL (kong_db) |
FR-2.2.2: Services Registration
| Service Name | Upstream URL | Kong Route |
|---|---|---|
| user-service | http://user-service:8001 | /api/users/* |
| product-service | http://product-service:8002 | /api/products/* |
FR-2.2.3: Kong Routes
# User Service Routes
/api/users/register β POST β user-service/register
/api/users/login β POST β user-service/login
/api/users/me β GET β user-service/users/me
# Product Service Routes
/api/products β GET β product-service/products
/api/products β POST β product-service/products
/api/products/{id} β * β product-service/products/{id}
FR-2.2.4: Rate Limiting Plugin
| Setting | Value |
|---|---|
| Plugin | rate-limiting |
| Requests per minute | 100 |
| Policy | local |
Acceptance Criteria:
| # | Criteria | Verification |
|---|---|---|
| 1 | Product Service runs on port 8002 | curl http://localhost:8002/health |
| 2 | Product CRUD works | Test all endpoints |
| 3 | Auth via User Service | Create product requires valid token |
| 4 | Kong routes to User Service | curl http://localhost:8000/api/users/login |
| 5 | Kong routes to Product Service | curl http://localhost:8000/api/products |
| 6 | Rate limiting works | Send 101 requests in 1 minute |
3.3 Unit 3: Async Communication (Assignment 03)β
REQ-MS-3.1: RabbitMQ Integrationβ
| ID | REQ-MS-3.1 |
|---|---|
| Description | Integrate RabbitMQ for async communication |
| Priority | High |
Business Context:
- When user registers, send welcome email (async).
- Do not want user to wait for email completion before response.
- Decoupling: User Service does not need to know about Email Service.
FR-3.1.1: RabbitMQ Setup
| Component | Value |
|---|---|
| Port | 5672 (AMQP), 15672 (Management UI) |
| Default User | guest/guest |
| Virtual Host | / |
FR-3.1.2: Event Publishing (User Service)
When user registers successfully, publish event:
Exchange: user_events (type: topic)
Routing Key: user.created
Queue: user_notifications
Message:
{
"event_type": "user.created",
"timestamp": "2024-12-24T10:00:00Z",
"data": {
"user_id": 1,
"username": "john_doe",
"email": "john@example.com"
}
}
FR-3.1.3: Producer Implementation
# User Service publishes event after registration
class EventPublisher:
async def publish_user_created(self, user_data: dict):
# Connect to RabbitMQ
# Declare exchange (topic type)
# Publish message with routing key "user.created"
pass
FR-3.1.4: Consumer Implementation (Notification Service)
# Notification Service consumes user.created events
class NotificationConsumer:
async def process_user_created(self, message: dict):
# Log the event
# Send welcome email (simulated)
# Acknowledge message
pass
Acceptance Criteria:
| # | Criteria | Verification |
|---|---|---|
| 1 | RabbitMQ running | http://localhost:15672 |
| 2 | Exchange created | Check RabbitMQ Management UI |
| 3 | Queue created and bound | Check RabbitMQ Management UI |
| 4 | Event published on register | Register user, check queue |
| 5 | Notification Service consumes | Check logs |
3.4 Unit 4 & 5: SAGA Pattern (Assignment 04)β
REQ-MS-4.1: Order Service with SAGAβ
| ID | REQ-MS-4.1 |
|---|---|
| Description | Implement Order Service with SAGA pattern (Choreography) |
| Priority | Critical |
Business Context:
When user creates an order, require:
- Validate user exists (User Service)
- Check stock availability (Product Service)
- Reserve stock (Product Service)
- Create order record (Order Service)
- Publish order.created event
- Notification Service sends confirmation
If any step fails β Compensating Transactions
FR-4.1.1: Order Service Structure
order-service/
βββ app/
β βββ main.py
β βββ api/
β β βββ orders.py
β β βββ deps.py
β βββ models/
β β βββ order.py
β β βββ order_item.py
β βββ schemas/
β β βββ order.py
β βββ services/
β β βββ order_service.py # SAGA logic here
β βββ utils/
β βββ auth_client.py # Validate token
β βββ product_client.py # Check/reserve stock
β βββ event_publisher.py # Publish events
βββ alembic/
βββ Dockerfile
FR-4.1.2: Order Model
class Order:
id: int
user_id: int
status: str # pending, confirmed, cancelled, completed
total_amount: Decimal
created_at: datetime
updated_at: datetime
class OrderItem:
id: int
order_id: int
product_id: int
quantity: int
unit_price: Decimal
subtotal: Decimal
FR-4.1.3: Order Statuses
| Status | Description |
|---|---|
| pending | Order created, awaiting stock reservation |
| confirmed | Stock reserved, order confirmed |
| cancelled | Order cancelled (compensation) |
| completed | Order fulfilled |
FR-4.1.4: SAGA Happy Path
βββββββββββββββ βββββββββββββββ βββββββββββββββ βββββββββββββββ
β Client β βOrder Serviceβ βProduct Svc β βNotification β
ββββββββ¬βββββββ ββββββββ¬βββββββ ββββββββ¬βββββββ ββββββββ¬βββββββ
β β β β
β POST /orders β β β
βββββββββββββββββββ>β β β
β β β β
β β Validate Token β β
β ββββββββββββββββββββ> (User Service) β
β β<βββββββββββββββββββ valid=true β
β β β β
β β Check Stock β β
β βββββββββββββββββββ>β β
β β<βββββββββββββββββββ available=true β
β β β β
β β Reserve Stock β β
β βββββββββββββββββββ>β β
β β<βββββββββββββββββββ reserved=true β
β β β β
β β Create Order β β
β β (status=confirmed)β β
β β β β
β β Publish order.created β
β ββββββββββββββββββββββββββββββββββββββββ>
β β β β
β 201 Created β β β
β<βββββββββββββββββββ β β
β β β Send Email β
β β β (async) β
FR-4.1.5: SAGA Failure Path (Compensation)
Scenario: Stock reservation fails
1. Client sends POST /orders
2. Order Service validates token β OK
3. Order Service checks stock β OK (available)
4. Order Service reserves stock β FAIL (concurrent order took last items)
Compensation:
5. Order Service sets order status = "cancelled"
6. Order Service publishes order.cancelled event
7. Return 409 Conflict to client
Scenario: Product Service unavailable
1. Client sends POST /orders
2. Order Service validates token β OK
3. Order Service checks stock β TIMEOUT (Product Service down)
Compensation:
4. Order Service does NOT create order
5. Return 503 Service Unavailable to client
6. Log error for monitoring
FR-4.1.6: API Endpoints
| Endpoint | Method | Auth | Description |
|---|---|---|---|
/health | GET | No | Health check |
/orders | POST | Yes | Create order (SAGA) |
/orders | GET | Yes | List user's orders |
/orders/{id} | GET | Yes | Get order details |
/orders/{id}/cancel | POST | Yes | Cancel order (compensation) |
FR-4.1.7: Create Order Request/Response
POST /orders
Authorization: Bearer <token>
Content-Type: application/json
Request:
{
"items": [
{"product_id": 1, "quantity": 2},
{"product_id": 3, "quantity": 1}
],
"shipping_address": "123 Main St"
}
Response 201:
{
"id": 1,
"user_id": 5,
"status": "confirmed",
"items": [
{
"product_id": 1,
"product_name": "Widget A",
"quantity": 2,
"unit_price": 29.99,
"subtotal": 59.98
},
{
"product_id": 3,
"product_name": "Gadget B",
"quantity": 1,
"unit_price": 49.99,
"subtotal": 49.99
}
],
"total_amount": 109.97,
"shipping_address": "123 Main St",
"created_at": "2024-12-24T10:00:00Z"
}
Response 409 (Stock not available):
{
"detail": "Insufficient stock for product ID 1. Available: 1, Requested: 2"
}
Response 503 (Service unavailable):
{
"detail": "Product service temporarily unavailable"
}
FR-4.1.8: Event Publishing
Exchange: order_events (type: topic)
Event: order.created
Routing Key: order.created
{
"event_type": "order.created",
"timestamp": "2024-12-24T10:00:00Z",
"data": {
"order_id": 1,
"user_id": 5,
"username": "john_doe",
"total_amount": 109.97,
"items_count": 2
}
}
Event: order.cancelled
Routing Key: order.cancelled
{
"event_type": "order.cancelled",
"timestamp": "2024-12-24T10:05:00Z",
"data": {
"order_id": 1,
"user_id": 5,
"reason": "insufficient_stock"
}
}
Acceptance Criteria:
| # | Criteria | Verification |
|---|---|---|
| 1 | Order Service runs on port 8003 | Health check |
| 2 | Create order happy path | POST /orders success |
| 3 | Stock validation | Order fails if no stock |
| 4 | Stock reservation | Product quantity decreases |
| 5 | Compensation on failure | Order status = cancelled |
| 6 | Event published | Check RabbitMQ |
| 7 | Notification received | Check Notification Service logs |
3.5 Unit 6: Redis Caching (Assignment 05)β
REQ-MS-5.1: Cache-Aside Patternβ
| ID | REQ-MS-5.1 |
|---|---|
| Description | Implement caching for Product Service |
| Priority | High |
Business Context:
- Product catalog is read-heavy.
- Database queries for product details are resource-intensive.
- Cache helps reduce latency and DB load.
FR-5.1.1: Redis Setup
| Component | Value |
|---|---|
| Port | 6379 |
| Max Memory | 256MB |
| Eviction Policy | allkeys-lru |
FR-5.1.2: Cache-Aside Implementation
GET /products/{id}
βββββββββββββββ βββββββββββββββ βββββββββββββββ βββββββββββββββ
β Client β βProduct Svc β β Redis β β PostgreSQL β
ββββββββ¬βββββββ ββββββββ¬βββββββ ββββββββ¬βββββββ ββββββββ¬βββββββ
β β β β
β GET /products/1 β β β
βββββββββββββββββββ>β β β
β β β β
β β GET product:1 β β
β βββββββββββββββββββ>β β
β β β β
β β<βββββββββββββββββββ β
β β Cache HIT β β
β<βββββββββββββββββββ (return cached) β β
β β β β
β β --- OR --- β β
β β β β
β β<βββββββββββββββββββ β
β β Cache MISS β β
β β β β
β β SELECT * FROM ... β β
β βββββββββββββββββββββββββββββββββββββββ>β
β β<βββββββββββββββββββββββββββββββββββββββ
β β β β
β β SET product:1 β β
β βββββββββββββββββββ>β β
β β β β
β<βββββββββββββββββββ β β
FR-5.1.3: Cache Keys
| Pattern | Example | TTL |
|---|---|---|
| product:{id} | product:1 | 300s |
| products:list:{skip}:{limit} | products:list:0:20 | 60s |
| user:profile:{id} | user:profile:5 | 300s |
FR-5.1.4: Cache Invalidation
| Event | Action |
|---|---|
| Product created | Invalidate products:list:* |
| Product updated | Invalidate product:{id}, products:list:* |
| Product deleted | Invalidate product:{id}, products:list:* |
| Stock updated | Invalidate product:{id} |
FR-5.1.5: Extend to User Service
Apply caching for User Profile endpoint:
GET /users/me
Cache key: user:profile:{user_id}
TTL: 300 seconds
Invalidate on: user update
Acceptance Criteria:
| # | Criteria | Verification |
|---|---|---|
| 1 | Redis running | redis-cli ping |
| 2 | Cache HIT on second request | Check response headers or logs |
| 3 | Cache invalidation on update | Update product, verify cache cleared |
| 4 | TTL expiration | Wait 5 min, verify cache miss |
| 5 | User profile cached | Check Redis keys |
3.6 Unit 6-7: Observability - Tracing (Assignment 06)β
REQ-MS-6.1: OpenTelemetry Integrationβ
| ID | REQ-MS-6.1 |
|---|---|
| Description | Integrate distributed tracing with OpenTelemetry + Jaeger |
| Priority | High |
Business Context:
- When a request passes through multiple services, need to trace the entire journey.
- Debug performance issues need to know which service is slow.
- Jaeger UI helps visualize traces.
FR-6.1.1: OpenTelemetry Setup
# Each service needs:
from opentelemetry import trace
from opentelemetry.instrumentation.fastapi import FastAPIInstrumentor
from opentelemetry.exporter.jaeger.thrift import JaegerExporter
from opentelemetry.sdk.trace import TracerProvider
from opentelemetry.sdk.trace.export import BatchSpanProcessor
# Setup
tracer_provider = TracerProvider()
jaeger_exporter = JaegerExporter(
agent_host_name="jaeger",
agent_port=6831,
)
tracer_provider.add_span_processor(BatchSpanProcessor(jaeger_exporter))
trace.set_tracer_provider(tracer_provider)
# Instrument FastAPI
FastAPIInstrumentor.instrument_app(app)
FR-6.1.2: Trace Propagation
When service A calls service B, trace context must be propagated:
# In auth_client.py
from opentelemetry.propagate import inject
async def validate_token(token: str):
headers = {}
inject(headers) # Inject trace context
async with httpx.AsyncClient() as client:
response = await client.post(
f"{USER_SERVICE_URL}/validate-token",
json={"token": token},
headers=headers
)
FR-6.1.3: Custom Spans
tracer = trace.get_tracer(__name__)
async def create_order(order_data: OrderCreate):
with tracer.start_as_current_span("create_order") as span:
span.set_attribute("user_id", current_user.id)
with tracer.start_as_current_span("validate_stock"):
# Check stock
pass
with tracer.start_as_current_span("reserve_stock"):
# Reserve stock
pass
with tracer.start_as_current_span("save_order"):
# Save to DB
pass
FR-6.1.4: Jaeger UI
| Component | URL |
|---|---|
| Jaeger UI | http://localhost:16686 |
| Agent Port | 6831 (UDP) |
Acceptance Criteria:
| # | Criteria | Verification |
|---|---|---|
| 1 | Jaeger UI accessible | http://localhost:16686 |
| 2 | Services appear in Jaeger | Check service dropdown |
| 3 | Trace spans multiple services | Create order, view trace |
| 4 | Custom spans visible | Check create_order spans |
| 5 | Latency breakdown | Identify slow operations |
3.7 Unit 7: Observability - Logging (Assignment 07)β
REQ-MS-7.1: Centralized Logging with Lokiβ
| ID | REQ-MS-7.1 |
|---|---|
| Description | Setup centralized logging with Loki + Grafana |
| Priority | High |
Business Context:
- Logs from multiple services need aggregation in one place.
- Search logs by service, level, timestamp.
- Correlate logs with traces.
FR-7.1.1: Structured Logging
import structlog
logger = structlog.get_logger()
# Log with context
logger.info(
"Order created",
order_id=order.id,
user_id=user.id,
total_amount=str(order.total_amount),
trace_id=get_current_trace_id()
)
# Log format (JSON)
{
"timestamp": "2024-12-24T10:00:00Z",
"level": "info",
"message": "Order created",
"service": "order-service",
"order_id": 1,
"user_id": 5,
"total_amount": "109.97",
"trace_id": "abc123..."
}
FR-7.1.2: Promtail Configuration
# promtail-config.yml
scrape_configs:
- job_name: containers
static_configs:
- targets:
- localhost
labels:
job: containerlogs
__path__: /var/log/containers/*.log
pipeline_stages:
- json:
expressions:
level: level
service: service
message: message
- labels:
level:
service:
FR-7.1.3: Loki Queries
# All errors from order-service
{service="order-service"} |= "error"
# Orders created in last hour
{service="order-service"} |= "Order created" | json
# Logs with specific trace ID
{service=~".+"} |= "trace_id=abc123"
Acceptance Criteria:
| # | Criteria | Verification |
|---|---|---|
| 1 | Loki running | http://localhost:3100/ready |
| 2 | Logs appear in Grafana | Explore β Loki |
| 3 | Filter by service | Query {service="..."} |
| 4 | Filter by level | Query level="error" |
| 5 | Logs contain trace_id | Correlate with Jaeger |
3.8 Unit 9: Observability - Metrics (Assignment 08)β
REQ-MS-8.1: Prometheus Metricsβ
| ID | REQ-MS-8.1 |
|---|---|
| Description | Instrument services with Prometheus metrics |
| Priority | High |
Business Context:
- Monitor request rates, error rates, latencies.
- Alert when metrics exceed thresholds.
- Capacity planning based on metrics.
FR-8.1.1: Metrics Endpoint
Each service exposes /metrics endpoint:
# HELP http_requests_total Total HTTP requests
# TYPE http_requests_total counter
http_requests_total{method="POST",endpoint="/orders",status="201"} 150
http_requests_total{method="POST",endpoint="/orders",status="409"} 12
http_requests_total{method="GET",endpoint="/products",status="200"} 5000
# HELP http_request_duration_seconds HTTP request latency
# TYPE http_request_duration_seconds histogram
http_request_duration_seconds_bucket{endpoint="/orders",le="0.1"} 100
http_request_duration_seconds_bucket{endpoint="/orders",le="0.5"} 145
http_request_duration_seconds_bucket{endpoint="/orders",le="1.0"} 149
FR-8.1.2: Key Metrics
| Metric | Type | Labels | Description |
|---|---|---|---|
| http_requests_total | Counter | method, endpoint, status | Total requests |
| http_request_duration_seconds | Histogram | method, endpoint | Latency |
| orders_created_total | Counter | status | Orders created |
| cache_hits_total | Counter | cache_name | Cache hits |
| cache_misses_total | Counter | cache_name | Cache misses |
| rabbitmq_messages_published_total | Counter | exchange | Events published |
FR-8.1.3: Prometheus Configuration
# prometheus.yml
scrape_configs:
- job_name: 'user-service'
static_configs:
- targets: ['user-service:8001']
metrics_path: /metrics
- job_name: 'product-service'
static_configs:
- targets: ['product-service:8002']
- job_name: 'order-service'
static_configs:
- targets: ['order-service:8003']
FR-8.1.4: Grafana Dashboard
Required panels:
- Request Rate (requests/second)
- Error Rate (%)
- P50, P95, P99 Latency
- Orders Created (per minute)
- Cache Hit Rate
- Service Health Status
Acceptance Criteria:
| # | Criteria | Verification |
|---|---|---|
| 1 | /metrics endpoint works | curl service:port/metrics |
| 2 | Prometheus scraping | Check Prometheus targets |
| 3 | Grafana dashboard | Import/create dashboard |
| 4 | Request rate panel | Shows current RPS |
| 5 | Error rate panel | Shows error % |
| 6 | Latency percentiles | Shows P50, P95, P99 |
3.9 Unit 8: E2E Review & Final Projectβ
REQ-MS-9.1: E2E Integrationβ
| ID | REQ-MS-9.1 |
|---|---|
| Description | Complete E2E flow with all components |
| Priority | Critical |
E2E Happy Path Test:
#!/bin/bash
# test-e2e.sh
# 1. Register user
USER_RESPONSE=$(curl -s -X POST http://localhost:8000/api/users/register \
-H "Content-Type: application/json" \
-d '{"username":"testuser","password":"Test@123"}')
echo "User created: $USER_RESPONSE"
# 2. Login
TOKEN_RESPONSE=$(curl -s -X POST http://localhost:8000/api/users/login \
-d "username=testuser&password=Test@123")
TOKEN=$(echo $TOKEN_RESPONSE | jq -r '.access_token')
echo "Token: $TOKEN"
# 3. Create product (admin)
PRODUCT=$(curl -s -X POST http://localhost:8000/api/products \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-d '{"name":"Test Product","price":29.99,"quantity":100}')
PRODUCT_ID=$(echo $PRODUCT | jq -r '.id')
echo "Product created: $PRODUCT_ID"
# 4. Create order
ORDER=$(curl -s -X POST http://localhost:8000/api/orders \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-d "{\"items\":[{\"product_id\":$PRODUCT_ID,\"quantity\":2}]}")
ORDER_ID=$(echo $ORDER | jq -r '.id')
echo "Order created: $ORDER_ID"
# 5. Check traces in Jaeger
echo "View trace: http://localhost:16686"
# 6. Check logs in Grafana
echo "View logs: http://localhost:3000"
# 7. Check metrics
echo "View metrics: http://localhost:9090"
E2E Failure Path Test:
# Test: Order with insufficient stock
# Create product with quantity=1
curl -X POST http://localhost:8000/api/products \
-H "Authorization: Bearer $TOKEN" \
-d '{"name":"Limited Product","price":99.99,"quantity":1}'
# First order succeeds
curl -X POST http://localhost:8000/api/orders \
-H "Authorization: Bearer $TOKEN" \
-d '{"items":[{"product_id":2,"quantity":1}]}'
# Response: 201 Created
# Second order fails (no stock)
curl -X POST http://localhost:8000/api/orders \
-H "Authorization: Bearer $TOKEN" \
-d '{"items":[{"product_id":2,"quantity":1}]}'
# Response: 409 Conflict
# Check: order.cancelled event in RabbitMQ
FR-9.1.1: Debug Workflow
- Issue: Order creation slow
- Check Jaeger: Which span is slow?
- Check Prometheus: Latency metrics
- Check Loki: Any errors in logs?
- Issue: Orders failing
- Check Loki: Filter by service="order-service", level="error"
- Check Jaeger: Find failed trace
- Check RabbitMQ: Messages in dead letter queue?
- Issue: High error rate
- Check Grafana: Error rate dashboard
- Check Prometheus:
http_requests_total{status=~"5.."} - Check Loki: Recent errors
Acceptance Criteria:
| # | Criteria | Verification |
|---|---|---|
| 1 | E2E happy path passes | Run test script |
| 2 | E2E failure path handles compensation | Verify cancelled order |
| 3 | Trace visible end-to-end | Jaeger shows full trace |
| 4 | Logs correlated with trace | Same trace_id in logs |
| 5 | Metrics reflect activity | Grafana shows requests |
| 6 | System recovers from failure | Stop/start service |
4. Non-Functional Requirementsβ
4.1 Performanceβ
| ID | Requirement |
|---|---|
| NFR-1 | Response time < 500ms for 95th percentile |
| NFR-2 | Support 50 concurrent users |
| NFR-3 | Cache hit rate > 80% for product reads |
| NFR-4 | Message processing latency < 1s |
4.2 Reliabilityβ
| ID | Requirement |
|---|---|
| NFR-5 | Services auto-restart on failure |
| NFR-6 | Database connections with retry |
| NFR-7 | Circuit breaker for inter-service calls |
| NFR-8 | Message persistence in RabbitMQ |
4.3 Securityβ
| ID | Requirement |
|---|---|
| NFR-9 | JWT secret not shared between services |
| NFR-10 | All inter-service auth via User Service |
| NFR-11 | No sensitive data in logs |
| NFR-12 | Database credentials in environment variables |
4.4 Observabilityβ
| ID | Requirement |
|---|---|
| NFR-13 | All services expose /health endpoint |
| NFR-14 | All services expose /metrics endpoint |
| NFR-15 | Structured JSON logging |
| NFR-16 | Trace context propagation |
5. Docker Compose Overviewβ
# Key services
services:
# Infrastructure
postgres-user: # Port 5433
postgres-product: # Port 5434
postgres-order: # Port 5435
redis: # Port 6379
rabbitmq: # Ports 5672, 15672
# API Gateway
kong: # Ports 8000, 8001
kong-db: # Kong's PostgreSQL
# Application Services
user-service: # Port 8001
product-service: # Port 8002
order-service: # Port 8003
notification-service:# Port 8004
# Observability
prometheus: # Port 9090
grafana: # Port 3000
loki: # Port 3100
promtail: # Log collector
jaeger: # Ports 16686, 6831
# Frontend
svelte-client: # Port 5173
6. Implementation Checklistβ
Assignment Progress Trackerβ
| Assignment | Unit | Component | Status | Points |
|---|---|---|---|---|
| 01 | 1 | User Service Extraction | β¬ | 100 |
| 02 | 2 | Product Service + Kong | β¬ | 100 |
| 03 | 3 | RabbitMQ Integration | β¬ | 100 |
| 04 | 4-5 | Order Service + SAGA | β¬ | 100 |
| 05 | 6 | Redis Caching | β¬ | 100 |
| 06 | 6-7 | OpenTelemetry + Jaeger | β¬ | 100 |
| 07 | 7 | Loki Logging | β¬ | 100 |
| 08 | 9 | Prometheus + Grafana | β¬ | 100 |
| Final | 8 | E2E Integration | β¬ | 100 |
Quick Commandsβ
# Start all services
docker-compose up -d
# Check status
docker-compose ps
# View logs
docker-compose logs -f order-service
# Run E2E test
./test-e2e.sh
# Access UIs
# Kong Admin: http://localhost:8001
# RabbitMQ: http://localhost:15672
# Jaeger: http://localhost:16686
# Grafana: http://localhost:3000
# Prometheus: http://localhost:9090
7. Appendixβ
A. Environment Variables Templateβ
# User Service
USER_SERVICE_DATABASE_URL=postgresql://user:pass@postgres-user:5432/user_db
USER_SERVICE_SECRET_KEY=your-secret-key
USER_SERVICE_ALGORITHM=HS256
# Product Service
PRODUCT_SERVICE_DATABASE_URL=postgresql://user:pass@postgres-product:5432/product_db
PRODUCT_SERVICE_USER_SERVICE_URL=http://user-service:8001
PRODUCT_SERVICE_REDIS_URL=redis://redis:6379/0
# Order Service
ORDER_SERVICE_DATABASE_URL=postgresql://user:pass@postgres-order:5432/order_db
ORDER_SERVICE_USER_SERVICE_URL=http://user-service:8001
ORDER_SERVICE_PRODUCT_SERVICE_URL=http://product-service:8002
ORDER_SERVICE_RABBITMQ_URL=amqp://guest:guest@rabbitmq:5672/
# RabbitMQ
RABBITMQ_DEFAULT_USER=guest
RABBITMQ_DEFAULT_PASS=guest
# Jaeger
JAEGER_AGENT_HOST=jaeger
JAEGER_AGENT_PORT=6831
B. Troubleshootingβ
| Issue | Solution |
|---|---|
| Service can't connect to DB | Check if postgres container is healthy, verify DATABASE_URL |
| Token validation fails | Ensure User Service is running, check USER_SERVICE_URL |
| Messages not consumed | Check RabbitMQ Management UI, verify queue bindings |
| Traces not appearing | Verify Jaeger agent host/port, check OTel setup |
| Logs not in Loki | Check Promtail config, verify log paths |
Document Version: 1.0.0
Created: 2025-12-24
Author: CongDX
Based on: Advanced Microservices Architecture
Status: Draft