- TypeScript 50.1%
- C# 46.6%
- Dockerfile 2.4%
- HTML 0.5%
- CSS 0.4%
| .idea/.idea.KafkaOrderDemo/.idea | ||
| src | ||
| .gitignore | ||
| compose.yml | ||
| init-databases.sql | ||
| KafkaOrderDemo.slnx | ||
| package-lock.json | ||
| README.md | ||
Kafka Order Demo - Event-Driven Architecture
A demonstration of Event-Driven Architecture using Kafka, .NET 10, and PostgreSQL. This solution shows how multiple microservices communicate through event streaming to process orders.
Project Structure
src/
├── Services/
│ ├── OrderService/ # Order management and order creation
│ ├── ProductService/ # Product catalog and stock management
│ ├── PaymentService/ # Payment processing simulation
│ └── UserService/ # User management
└── Frontend/ # (Future Angular 21 frontend)
compose.yml # Docker Compose configuration
init-databases.sql # PostgreSQL database initialization
Services Overview
OrderService (Port 8001)
- Creates and manages orders
- Publishes
OrderCreatedevents - Listens for payment results
- Tracks order status
API Endpoints:
GET /api/orders- List all ordersGET /api/orders/{id}- Get order by IDPOST /api/orders- Create a new order
ProductService (Port 8002)
- Manages product catalog
- Tracks available stock
- Reserves stock when orders are created
- Publishes stock events
API Endpoints:
GET /api/products- List all productsGET /api/products/{id}- Get product by IDPOST /api/products- Create a new product
Demo Products (pre-seeded):
- Laptop: $1299.99 (10 in stock)
- Mouse: $29.99 (50 in stock)
- Keyboard: $99.99 (25 in stock)
PaymentService (Port 8003)
- Simulates payment processing
- Listens for stock reservation events
- Randomly succeeds or fails payments (80% success rate)
- Publishes payment result events
API Endpoints:
GET /api/payments- List all paymentsGET /api/payments/{id}- Get payment by IDGET /api/payments/order/{orderId}- Get payment by order ID
UserService (Port 8004)
- Manages user information
- Provides demo users for testing
API Endpoints:
GET /api/users- List all usersGET /api/users/{id}- Get user by IDPOST /api/users- Create a new user
Demo Users (pre-seeded):
- John Doe (john@example.com)
- Jane Smith (jane@example.com)
- Bob Johnson (bob@example.com)
Event Flow
-
OrderCreated (orders topic)
- OrderService publishes when a new order is created
- Consumed by ProductService and PaymentService
-
StockReserved (products topic)
- ProductService publishes when stock is successfully reserved
- Consumed by PaymentService to trigger payment processing
-
StockReservationFailed (products topic)
- ProductService publishes when stock cannot be reserved
- Consumed by PaymentService to fail the payment
-
PaymentSucceeded / PaymentFailed (payments topic)
- PaymentService publishes after payment processing
- Consumed by OrderService to update order status
Event Schema
All events follow this structure:
{
"id": "event-uuid",
"type": "EventType",
"occurredAt": "2026-05-31T12:00:00Z",
"payload": {
// Event-specific data
}
}
Prerequisites
- Docker and Docker Compose
- .NET 10 SDK (for local development)
Running the Demo
Using Docker Compose
# Build and start all services
docker compose --profile app up --build
# Build and start Kafka and PostgreSQL
docker compose up --build
# View logs
docker compose logs -f
# Stop all services
docker compose down
# Clean up volumes
docker compose down -v
Local Development
If you want to run services locally without Docker:
- Start PostgreSQL:
docker run -d \
-e POSTGRES_USER=postgres \
-e POSTGRES_PASSWORD=postgres \
-e POSTGRES_INITDB_ARGS="-c max_connections=200" \
-v init-databases.sql:/docker-entrypoint-initdb.d/init-databases.sql \
-p 5432:5432 \
postgres:17-alpine
- Start Kafka:
docker run -d \
--name zookeeper \
-e ZOOKEEPER_CLIENT_PORT=2181 \
confluentinc/cp-zookeeper:7.5.0
docker run -d \
--name kafka \
-e KAFKA_BROKER_ID=1 \
-e KAFKA_ZOOKEEPER_CONNECT=zookeeper:2181 \
-e KAFKA_ADVERTISED_LISTENERS=PLAINTEXT://kafka:9092 \
-e KAFKA_OFFSETS_TOPIC_REPLICATION_FACTOR=1 \
-e KAFKA_TRANSACTION_STATE_LOG_MIN_ISR=1 \
-e KAFKA_TRANSACTION_STATE_LOG_REPLICATION_FACTOR=1 \
-p 9092:9092 \
confluentinc/cp-kafka:7.5.0
- Run each service from its directory:
cd src/Services/OrderService
dotnet run
cd src/Services/ProductService
dotnet run
cd src/Services/PaymentService
dotnet run
cd src/Services/UserService
dotnet run
Testing the Demo Flow
1. Create a User
curl -X POST http://localhost:8004/api/users \
-H "Content-Type: application/json" \
-d '{
"name": "Test User",
"email": "test@example.com"
}'
2. List Products
curl http://localhost:8002/api/products
3. Create an Order
curl -X POST http://localhost:8001/api/orders \
-H "Content-Type: application/json" \
-d '{
"userId": "10000000-0000-0000-0000-000000000001",
"productId": "00000000-0000-0000-0000-000000000001",
"quantity": 1,
"totalPrice": 1299.99
}'
4. Check Payment Status
# Get the order ID from step 3 response
bash
curl http://localhost:8003/api/payments/order/{orderId}
5. Check Order Status
# The order status should be updated to "Paid" or "Failed"
curl http://localhost:8001/api/orders/{orderId}
Database
Each service has its own PostgreSQL database:
- orders_db - Order data
- products_db - Product catalog and stock reservations
- payments_db - Payment records
- users_db - User information
Connection string format:
Host=postgres;Port=5432;Database={service}_db;Username=postgres;Password=postgres
Kafka Topics
Four main topics handle event communication:
- orders - Order-related events
- products - Product stock events
- payments - Payment processing events
- users - User-related events
All topics are created automatically by Kafka when services start.
Architecture Highlights
Event-Driven Communication
Services communicate asynchronously through Kafka topics, enabling loose coupling and scalability.
Separate Databases
Each service owns its own database, following the database-per-service pattern.
Minimal API Endpoints
Uses ASP.NET Core's minimal APIs for a clean, lightweight implementation.
Docker Containerization
All services run in Docker containers for consistent environments.
Health Checks
Each service exposes a /health endpoint for monitoring.
Demo Scenario
The typical flow for a customer order:
- Customer selects a product (e.g., Laptop)
- OrderService creates an order (status = "Pending")
- OrderService publishes
OrderCreatedevent - ProductService reserves stock (Laptop stock: 10 → 9)
- ProductService publishes
StockReservedevent - PaymentService receives
StockReservedand processes payment - PaymentService simulates payment (80% success rate)
- PaymentService publishes
PaymentSucceededorPaymentFailed - OrderService receives payment result and updates order status
- Frontend polls order status and shows result to customer
Future Enhancements
- Angular 21 frontend with product browsing and cart
- Message outbox pattern for guaranteed message delivery
- Dead letter queues for failed messages
- Distributed tracing with OpenTelemetry
- API Gateway for routing
- Authentication and authorization
Troubleshooting
Services can't connect to Kafka
- Ensure Kafka container is running and healthy
- Check that
KAFKA_ADVERTISED_LISTENERSis set toPLAINTEXT://kafka:9092 - Verify services are using the correct bootstrap servers:
kafka:9092
Database connection errors
- Verify PostgreSQL container is running
- Check that all databases are created (see
init-databases.sql) - Verify connection strings in
appsettings.json
Orders not progressing
- Check service logs:
docker compose logs {service-name} - Verify Kafka connectivity by checking consumer logs
- Ensure all services are running and healthy
License
MIT