No description
  • TypeScript 50.1%
  • C# 46.6%
  • Dockerfile 2.4%
  • HTML 0.5%
  • CSS 0.4%
Find a file
Manuel Graf 1863459a55 cleanup
2026-06-01 20:41:42 +02:00
.idea/.idea.KafkaOrderDemo/.idea Initial Commit 2026-05-31 11:20:31 +02:00
src cleanup 2026-06-01 20:41:42 +02:00
.gitignore services 2026-05-31 13:52:25 +02:00
compose.yml cleanup 2026-06-01 20:41:42 +02:00
init-databases.sql services 2026-05-31 13:52:25 +02:00
KafkaOrderDemo.slnx cleanup 2026-06-01 20:41:42 +02:00
package-lock.json services 2026-05-31 13:52:25 +02:00
README.md cleanup 2026-06-01 20:41:42 +02:00

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 OrderCreated events
  • Listens for payment results
  • Tracks order status

API Endpoints:

  • GET /api/orders - List all orders
  • GET /api/orders/{id} - Get order by ID
  • POST /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 products
  • GET /api/products/{id} - Get product by ID
  • POST /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 payments
  • GET /api/payments/{id} - Get payment by ID
  • GET /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 users
  • GET /api/users/{id} - Get user by ID
  • POST /api/users - Create a new user

Demo Users (pre-seeded):

Event Flow

  1. OrderCreated (orders topic)

    • OrderService publishes when a new order is created
    • Consumed by ProductService and PaymentService
  2. StockReserved (products topic)

    • ProductService publishes when stock is successfully reserved
    • Consumed by PaymentService to trigger payment processing
  3. StockReservationFailed (products topic)

    • ProductService publishes when stock cannot be reserved
    • Consumed by PaymentService to fail the payment
  4. 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:

  1. 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
  1. 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
  1. 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:

  1. Customer selects a product (e.g., Laptop)
  2. OrderService creates an order (status = "Pending")
  3. OrderService publishes OrderCreated event
  4. ProductService reserves stock (Laptop stock: 10 → 9)
  5. ProductService publishes StockReserved event
  6. PaymentService receives StockReserved and processes payment
  7. PaymentService simulates payment (80% success rate)
  8. PaymentService publishes PaymentSucceeded or PaymentFailed
  9. OrderService receives payment result and updates order status
  10. 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_LISTENERS is set to PLAINTEXT://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