This project demonstrates a distributed, event-driven microservices architecture built with NestJS, Apache Kafka, and Docker. It includes three core services (customer-service
, transaction-service
, notification-service
) that communicate asynchronously via Kafka topics. The application is containerized, deployed to IBM Cloud Code Engine, and leverages GitHub Actions for CI/CD.
-
Customer Service β Manages customer data and publishes events when a new customer is created.
-
Transaction Service β Handles financial transactions and produces events when a transaction occurs.
-
Notification Service β Listens for customer and transaction events to send real-time notifications.
Here is the full tutorial :
-
Backend Framework: NestJS (TypeScript)
-
Event Streaming: Apache Kafka (via
kafka.js
) -
Containerization: Docker
-
Orchestration: Kubernetes (implied via IBM Cloud Code Engine)
-
CI/CD: GitHub Actions
-
Cloud Deployment: IBM Cloud (Code Engine, Event Streams)
-
Authentication: SASL/SSL for Kafka brokers
-
Logging & Monitoring: NestJS Logger, structured event tracking
-
Customer Service:
-
REST API to create customers.
-
Emits
Customer.created
Kafka events on successful registration.
-
-
Transaction Service:
-
Processes financial transactions (debit/credit).
-
Emits
transaction.processed
(success) ortransaction.failed
(error) events. -
Maintains in-memory account balances for demo purposes.
-
-
Notification Service:
-
Listens to Kafka events (
Customer.created
,transaction.processed
,transaction.failed
). -
Sends emails/notifications based on event payloads (e.g., welcome emails, transaction alerts).
-
-
Kafka Topics:
-
Customer.created
: Triggered by customer registration. -
transaction.requested
: Initiated for transaction processing. -
transaction.processed
/transaction.failed
: Outcomes of transaction processing.
-
-
Asynchronous Workflow:
-
A customer is created via
customer-service
βCustomer.created
event emitted. -
notification-service
consumes the event β sends a welcome email. -
A transaction request is sent to
transaction-service
β processed asynchronously. -
On success/failure,
transaction-service
emits corresponding events βnotification-service
sends alerts.
-
-
Broker Setup:
-
Brokers are configured via
EVENT_STREAMS_KAFKA_BROKERS_SASL
environment variable (supports JSON or comma-separated strings). -
SASL/SSL authentication using
EVENT_STREAMS_USER
andEVENT_STREAMS_PASSWORD
.
-
-
Reusable Utilities:
KafkaConnectionUtils
class standardizes Kafka client configuration (SSL, timeouts, retries) across services.
-
Docker:
-
Each service has a dedicated
Dockerfile
optimized with multi-stage builds. -
Images are pushed to IBM Container Registry (
au.icr.io
).
-
-
IBM Cloud Code Engine:
-
Services are deployed as serverless applications with auto-scaling.
-
GitHub Actions automates build, push, and deployment steps on
master
branch updates.
-
-
GitHub Actions Workflow:
-
Checks out code, installs IBM Cloud CLI, and authenticates via API key.
-
Builds/pushes Docker images for all services.
-
Deploys updates to IBM Cloud Code Engine using
ibmcloud ce
commands.
-
π Repository Structure
.
βββ apps/
β βββ customer-service/ # Customer registration and event emission
β βββ transaction-service/ # Transaction processing core
β βββ notification-service/ # Event-driven notifications
βββ .github/workflows/ # CI/CD pipelines for IBM Cloud deployment
βββ docker/ # Dockerfiles for each service
βββ nest-cli.json # Monorepo configuration
βββ kafka-connection-utils/ # Reusable Kafka client setup (SSL, retries, logging)
Run the following commands to delete any existing applications:
ibmcloud ce application delete --name customer-service --force
ibmcloud ce application delete --name transaction-service --force
ibmcloud ce application delete --name notification-service --force
List all service instances in your IBM Cloud account:
ibmcloud resource service-instances
Retrieve details of your Event Streams instance:
ibmcloud resource service-instance my-event-streams --output json
ibmcloud resource service-instance-create my-event-streams messagehub standard au-syd
Verify and initialize the Event Streams instance:
ibmcloud es init -i my-event-streams
ibmcloud ce application create --name customer-service \
--image nginx:alpine \
--port 80 \
--registry-secret icr-secret
ibmcloud ce application create --name transaction-service \
--image nginx:alpine \
--port 80 \
--registry-secret icr-secret
ibmcloud ce application create --name notification-service \
--image nginx:alpine \
--port 80 \
--registry-secret icr-secret
ibmcloud ce application bind --name customer-service \
--service-instance my-event-streams \
--prefix EVENT_STREAMS
ibmcloud ce application bind --name transaction-service \
--service-instance my-event-streams \
--prefix EVENT_STREAMS
ibmcloud ce application bind --name notification-service \
--service-instance my-event-streams \
--prefix EVENT_STREAMS
Verify bindings:
ibmcloud ce application get --name notification-service
docker build -t au.icr.io/mycodeengine/customer-service:latest -f Dockerfile.customer-service .
docker build -t au.icr.io/mycodeengine/transaction-service:latest -f Dockerfile.transaction-service .
docker build -t au.icr.io/mycodeengine/notification-service:latest -f Dockerfile.notification-service .
docker push au.icr.io/mycodeengine/customer-service:latest
docker push au.icr.io/mycodeengine/transaction-service:latest
docker push au.icr.io/mycodeengine/notification-service:latest
ibmcloud ce application update --name customer-service \
--image au.icr.io/mycodeengine/customer-service:latest
ibmcloud ce application update --name transaction-service \
--image au.icr.io/mycodeengine/transaction-service:latest
ibmcloud ce application update --name notification-service \
--image au.icr.io/mycodeengine/notification-service:latest
curl -X POST http://customer-service/app/customers \
-H "Content-Type: application/json" \
-d '{"id": "cust-001", "name": "John Doe", "email": "john@example.com"}'
curl -X POST http://transaction-service/transactions \
-H "Content-Type: application/json" \
-d '{"from": "cust-001", "to": "cust-002", "amount": 100, "transactionId": "txn-123"}