Skip to main content

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​

RequirementDescription
mono-srcCompleted monolithic project with FastAPI
DockerDocker Desktop installed
PythonPython 3.11+
KnowledgeFastAPI, SQLAlchemy, Pydantic, JWT

1.4 Definitions​

TermDefinition
Bounded ContextLogical boundary of a domain in DDD
Strangler FigMigration pattern from monolith to microservices
SAGAPattern for handling distributed transactions
ChoreographySAGA pattern using events, without an orchestrator
OrchestrationSAGA pattern with a central coordinator
Cache-asideCaching 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​

FromToMethodPurpose
ClientKongRESTAll API requests
KongUser ServiceRESTAuth routes
KongProduct ServiceRESTProduct routes
KongOrder ServiceRESTOrder routes
Product ServiceUser ServiceRESTToken validation
Order ServiceUser ServiceRESTToken validation
Order ServiceProduct ServiceRESTStock validation
Order ServiceRabbitMQAMQPPublish order.created
Notification ServiceRabbitMQAMQPConsume order.created

2.3 Technology Stack​

LayerTechnologyVersion
API GatewayKong3.x
Backend FrameworkFastAPI0.104+
ORMSQLAlchemy2.0
MigrationsAlembic1.12+
ValidationPydantic2.0
Authpython-jose, bcryptLatest
DatabasePostgreSQL15
CacheRedis7
Message BrokerRabbitMQ3.12
TracingOpenTelemetry, JaegerLatest
MetricsPrometheusLatest
LoggingLoki, PromtailLatest
VisualizationGrafanaLatest
ContainerDocker, Docker ComposeLatest
FrontendSvelte, SvelteKit5.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​

IDREQ-MS-1.1
DescriptionExtract User domain from monolith into an independent microservice
PriorityCritical
PrerequisiteCompleted 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

AttributeValue
DatabasePostgreSQL
Database Nameuser_service_db
Port5433 (different from mono)
IsolationCompletely isolated, not shared with other services

FR-1.1.3: API Endpoints

EndpointMethodDescriptionRequestResponse
/healthGETHealth check-{"status": "healthy", "service": "user-service"}
/registerPOSTUser registrationUserCreateUserResponse (201)
/loginPOSTUser loginOAuth2 formToken (200)
/validate-tokenPOSTToken validation{"token": "..."}{"valid": true, "user_id": 1, "username": "..."}
/users/meGETCurrent user infoBearer tokenUserResponse

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:

#CriteriaVerification
1Service runs independently on port 8001curl http://localhost:8001/health
2Separate database (user_service_db)Check PostgreSQL
3User registration successfulPOST /register returns 201
4Login returns JWT tokenPOST /login returns token
5Token validation endpoint worksPOST /validate-token
6Alembic migrations run automaticallyContainer logs
7Docker container healthydocker ps shows healthy

3.2 Unit 2: API Gateway (Assignment 02)​

REQ-MS-2.1: Product Service Extraction​

IDREQ-MS-2.1
DescriptionExtract Product domain into an independent microservice
PriorityCritical

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:

  1. Receive Bearer token from request header
  2. Call User Service /validate-token endpoint
  3. If valid β†’ continue processing
  4. If invalid β†’ return 401

FR-2.1.3: API Endpoints

EndpointMethodAuthDescription
/healthGETNoHealth check
/productsGETNoList products (paginated)
/products/{id}GETNoGet product by ID
/productsPOSTYesCreate product
/products/{id}PUTYesUpdate product
/products/{id}DELETEYesDelete product
/products/{id}/stockPUTYesUpdate stock quantity

REQ-MS-2.2: Kong API Gateway Setup​

IDREQ-MS-2.2
DescriptionSetup Kong Gateway to route traffic to services
PriorityCritical

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

ComponentConfiguration
Kong Port8000 (proxy), 8001 (admin)
DatabasePostgreSQL (kong_db)

FR-2.2.2: Services Registration

Service NameUpstream URLKong Route
user-servicehttp://user-service:8001/api/users/*
product-servicehttp://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

SettingValue
Pluginrate-limiting
Requests per minute100
Policylocal

Acceptance Criteria:

#CriteriaVerification
1Product Service runs on port 8002curl http://localhost:8002/health
2Product CRUD worksTest all endpoints
3Auth via User ServiceCreate product requires valid token
4Kong routes to User Servicecurl http://localhost:8000/api/users/login
5Kong routes to Product Servicecurl http://localhost:8000/api/products
6Rate limiting worksSend 101 requests in 1 minute

3.3 Unit 3: Async Communication (Assignment 03)​

REQ-MS-3.1: RabbitMQ Integration​

IDREQ-MS-3.1
DescriptionIntegrate RabbitMQ for async communication
PriorityHigh

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

ComponentValue
Port5672 (AMQP), 15672 (Management UI)
Default Userguest/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:

#CriteriaVerification
1RabbitMQ runninghttp://localhost:15672
2Exchange createdCheck RabbitMQ Management UI
3Queue created and boundCheck RabbitMQ Management UI
4Event published on registerRegister user, check queue
5Notification Service consumesCheck logs

3.4 Unit 4 & 5: SAGA Pattern (Assignment 04)​

REQ-MS-4.1: Order Service with SAGA​

IDREQ-MS-4.1
DescriptionImplement Order Service with SAGA pattern (Choreography)
PriorityCritical

Business Context:

When user creates an order, require:

  1. Validate user exists (User Service)
  2. Check stock availability (Product Service)
  3. Reserve stock (Product Service)
  4. Create order record (Order Service)
  5. Publish order.created event
  6. 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

StatusDescription
pendingOrder created, awaiting stock reservation
confirmedStock reserved, order confirmed
cancelledOrder cancelled (compensation)
completedOrder 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

EndpointMethodAuthDescription
/healthGETNoHealth check
/ordersPOSTYesCreate order (SAGA)
/ordersGETYesList user's orders
/orders/{id}GETYesGet order details
/orders/{id}/cancelPOSTYesCancel 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:

#CriteriaVerification
1Order Service runs on port 8003Health check
2Create order happy pathPOST /orders success
3Stock validationOrder fails if no stock
4Stock reservationProduct quantity decreases
5Compensation on failureOrder status = cancelled
6Event publishedCheck RabbitMQ
7Notification receivedCheck Notification Service logs

3.5 Unit 6: Redis Caching (Assignment 05)​

REQ-MS-5.1: Cache-Aside Pattern​

IDREQ-MS-5.1
DescriptionImplement caching for Product Service
PriorityHigh

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

ComponentValue
Port6379
Max Memory256MB
Eviction Policyallkeys-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

PatternExampleTTL
product:{id}product:1300s
products:list:{skip}:{limit}products:list:0:2060s
user:profile:{id}user:profile:5300s

FR-5.1.4: Cache Invalidation

EventAction
Product createdInvalidate products:list:*
Product updatedInvalidate product:{id}, products:list:*
Product deletedInvalidate product:{id}, products:list:*
Stock updatedInvalidate 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:

#CriteriaVerification
1Redis runningredis-cli ping
2Cache HIT on second requestCheck response headers or logs
3Cache invalidation on updateUpdate product, verify cache cleared
4TTL expirationWait 5 min, verify cache miss
5User profile cachedCheck Redis keys

3.6 Unit 6-7: Observability - Tracing (Assignment 06)​

REQ-MS-6.1: OpenTelemetry Integration​

IDREQ-MS-6.1
DescriptionIntegrate distributed tracing with OpenTelemetry + Jaeger
PriorityHigh

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

ComponentURL
Jaeger UIhttp://localhost:16686
Agent Port6831 (UDP)

Acceptance Criteria:

#CriteriaVerification
1Jaeger UI accessiblehttp://localhost:16686
2Services appear in JaegerCheck service dropdown
3Trace spans multiple servicesCreate order, view trace
4Custom spans visibleCheck create_order spans
5Latency breakdownIdentify slow operations

3.7 Unit 7: Observability - Logging (Assignment 07)​

REQ-MS-7.1: Centralized Logging with Loki​

IDREQ-MS-7.1
DescriptionSetup centralized logging with Loki + Grafana
PriorityHigh

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:

#CriteriaVerification
1Loki runninghttp://localhost:3100/ready
2Logs appear in GrafanaExplore β†’ Loki
3Filter by serviceQuery {service="..."}
4Filter by levelQuery level="error"
5Logs contain trace_idCorrelate with Jaeger

3.8 Unit 9: Observability - Metrics (Assignment 08)​

REQ-MS-8.1: Prometheus Metrics​

IDREQ-MS-8.1
DescriptionInstrument services with Prometheus metrics
PriorityHigh

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

MetricTypeLabelsDescription
http_requests_totalCountermethod, endpoint, statusTotal requests
http_request_duration_secondsHistogrammethod, endpointLatency
orders_created_totalCounterstatusOrders created
cache_hits_totalCountercache_nameCache hits
cache_misses_totalCountercache_nameCache misses
rabbitmq_messages_published_totalCounterexchangeEvents 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:

#CriteriaVerification
1/metrics endpoint workscurl service:port/metrics
2Prometheus scrapingCheck Prometheus targets
3Grafana dashboardImport/create dashboard
4Request rate panelShows current RPS
5Error rate panelShows error %
6Latency percentilesShows P50, P95, P99

3.9 Unit 8: E2E Review & Final Project​

REQ-MS-9.1: E2E Integration​

IDREQ-MS-9.1
DescriptionComplete E2E flow with all components
PriorityCritical

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

  1. Issue: Order creation slow
  • Check Jaeger: Which span is slow?
  • Check Prometheus: Latency metrics
  • Check Loki: Any errors in logs?
  1. Issue: Orders failing
  • Check Loki: Filter by service="order-service", level="error"
  • Check Jaeger: Find failed trace
  • Check RabbitMQ: Messages in dead letter queue?
  1. Issue: High error rate
  • Check Grafana: Error rate dashboard
  • Check Prometheus: http_requests_total{status=~"5.."}
  • Check Loki: Recent errors

Acceptance Criteria:

#CriteriaVerification
1E2E happy path passesRun test script
2E2E failure path handles compensationVerify cancelled order
3Trace visible end-to-endJaeger shows full trace
4Logs correlated with traceSame trace_id in logs
5Metrics reflect activityGrafana shows requests
6System recovers from failureStop/start service

4. Non-Functional Requirements​

4.1 Performance​

IDRequirement
NFR-1Response time < 500ms for 95th percentile
NFR-2Support 50 concurrent users
NFR-3Cache hit rate > 80% for product reads
NFR-4Message processing latency < 1s

4.2 Reliability​

IDRequirement
NFR-5Services auto-restart on failure
NFR-6Database connections with retry
NFR-7Circuit breaker for inter-service calls
NFR-8Message persistence in RabbitMQ

4.3 Security​

IDRequirement
NFR-9JWT secret not shared between services
NFR-10All inter-service auth via User Service
NFR-11No sensitive data in logs
NFR-12Database credentials in environment variables

4.4 Observability​

IDRequirement
NFR-13All services expose /health endpoint
NFR-14All services expose /metrics endpoint
NFR-15Structured JSON logging
NFR-16Trace 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​

AssignmentUnitComponentStatusPoints
011User Service Extraction⬜100
022Product Service + Kong⬜100
033RabbitMQ Integration⬜100
044-5Order Service + SAGA⬜100
056Redis Caching⬜100
066-7OpenTelemetry + Jaeger⬜100
077Loki Logging⬜100
089Prometheus + Grafana⬜100
Final8E2E 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​

IssueSolution
Service can't connect to DBCheck if postgres container is healthy, verify DATABASE_URL
Token validation failsEnsure User Service is running, check USER_SERVICE_URL
Messages not consumedCheck RabbitMQ Management UI, verify queue bindings
Traces not appearingVerify Jaeger agent host/port, check OTel setup
Logs not in LokiCheck Promtail config, verify log paths

Document Version: 1.0.0

Created: 2025-12-24

Author: CongDX

Based on: Advanced Microservices Architecture

Status: Draft