Skip to content

Folders and files

NameName
Last commit message
Last commit date

Latest commit

Β 

History

4 Commits
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 

Repository files navigation

πŸ“‹ TaskFlow β€” Microservice Task Management Backend

Node.js Express MongoDB RabbitMQ Docker

A production-ready, event-driven microservices backend built with Node.js, Express, MongoDB, RabbitMQ, and Docker β€” featuring an API Gateway, asynchronous notifications, service discovery via Docker DNS, and full container orchestration.


πŸ“Œ Table of Contents


🧭 Overview

TaskFlow is a distributed, microservice-based backend system that handles user management, task lifecycle, and asynchronous email notifications. Each service is independently deployable, owns its own data domain, and communicates either via HTTP (synchronous) or RabbitMQ (asynchronous) depending on the use case.

All client requests flow through a single API Gateway β€” which handles routing, CORS, and rate limiting β€” before reaching the appropriate service.

When a task is created, the system:

  1. Client hits the API Gateway (localhost:8080)
  2. Gateway forwards the request to task-service
  3. Task is persisted in MongoDB
  4. A TASK_CREATED event is published to RabbitMQ
  5. notification-service asynchronously notifies the assigned user via email β€” completely decoupled from the main task flow

πŸ—οΈ Architecture

β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚                         CLIENT / FRONTEND                       β”‚
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜
                       β”‚ HTTP Request
                       β–Ό
β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚          api-gateway :8080           β”‚
β”‚  β€’ Single entry point for client     β”‚
β”‚  β€’ CORS, Rate Limiting, Routing      β”‚
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜
             β”‚
     β”Œβ”€β”€β”€β”€β”€β”€β”€β”΄β”€β”€β”€β”€β”€β”€β”€β”€β”
     β”‚                β”‚
     β–Ό                β–Ό
β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”  β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚ user-service    β”‚  β”‚ task-service :3002            β”‚
β”‚ :3001           β”‚  β”‚ β€’ Validates & stores task     β”‚
β”‚ β€’ Manages users β”‚  β”‚ β€’ Publishes TASK_CREATED      β”‚
β””β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”˜  β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”˜
         β”‚                      β”‚           β”‚
  MongoDBβ”‚               MongoDBβ”‚           β”‚ RabbitMQ Publish
  (users)β”‚               (tasks)β”‚           β”‚
         β–Ό                      β–Ό           β–Ό
  β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”       β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”  β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
  β”‚  MongoDB   β”‚       β”‚   MongoDB   β”‚  β”‚    RabbitMQ      β”‚
  β”‚ (users DB) β”‚       β”‚  (tasks DB) β”‚  β”‚ task_created     β”‚
  β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜       β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜  β””β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜
                                                  β”‚ Consume
                                                  β–Ό
                                  β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
                                  β”‚  notification-service     β”‚
                                  β”‚  :3003                    β”‚
                                  β”‚  β€’ Consumes TASK_CREATED  β”‚
                                  β”‚  β€’ Calls user-service     β”‚
                                  β”‚  β€’ Sends email via SMTP   β”‚
                                  β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜

🧩 Services

πŸ”€ api-gateway β€” Port 8080

The single entry point for all client requests. No business logic, no database β€” pure infrastructure.

  • Routes /api/users/* β†’ user-service:3001
  • Routes /api/tasks/* β†’ task-service:3002
  • Handles CORS so the React client can communicate freely
  • Enforces rate limiting (100 requests / 15 min per IP)
  • Logs every request via morgan

πŸ‘€ user-service β€” Port 3001

Manages user accounts and exposes REST APIs to create and retrieve user data. Acts as the source of truth for user identity β€” other services query it over the internal Docker network.

  • Stores users in the users MongoDB database
  • Exposes GET /users/:id used internally by notification-service
  • Exposes GET /users and POST /users for user management

βœ… task-service β€” Port 3002

Handles task creation and persistence. After storing a task, it publishes a TASK_CREATED event to RabbitMQ to trigger downstream processing without blocking the HTTP response.

  • Stores tasks in the tasks MongoDB database
  • Publishes messages to the task_created queue via amqplib
  • Implements retry logic for RabbitMQ connection initialization

πŸ“¬ notification-service β€” Port 3003

Runs as an event consumer β€” it continuously listens to the task_created RabbitMQ queue. On receiving a message, it:

  1. Extracts userId from the message payload
  2. Calls user-service over Docker's internal network (http://user-service:3001/...)
  3. Sends an email notification (currently simulated via console logging for development/testing purposes)
  4. Acknowledges (ack) the message on success

Failures in this service do not affect task creation β€” total decoupling via async messaging.


πŸ› οΈ Tech Stack

Layer Technology
Runtime Node.js
Framework Express.js
Database MongoDB (per-service logical DB)
Message Broker RabbitMQ (amqplib)
Gateway express-http-proxy, express-rate-limit, cors, morgan
Containerization Docker + Docker Compose
Service Discovery Docker DNS (internal hostnames)
Inter-service HTTP Native fetch API

πŸš€ Getting Started

Prerequisites

1. Clone the Repository

git clone https://github.com/your-username/taskflow.git
cd taskflow

2. Start the System

docker compose up --build

This spins up:

  • mongo β€” MongoDB instance
  • rabbitmq β€” RabbitMQ broker (management UI at http://localhost:15672)
  • api-gateway on port 8080 ← start here for all requests
  • user-service on port 3001
  • task-service on port 3002
  • notification-service on port 3003

3. Verify Services Are Running

docker compose ps

4. View Logs

# All services
docker compose logs -f

# Specific service
docker compose logs -f api-gateway

πŸ“‘ API Reference

All requests go through the API Gateway at http://localhost:8080

Users

Create a User

POST /api/users
Content-Type: application/json

{
  "name": "Alice Johnson",
  "email": "alice@example.com"
}

Response:

{
  "_id": "64f1a2b3c4d5e6f7a8b9c0d1",
  "name": "Alice Johnson",
  "email": "alice@example.com"
}

Get All Users

GET /api/users

Get a User by ID

GET /api/users/:id

Response:

{
  "_id": "64f1a2b3c4d5e6f7a8b9c0d1",
  "name": "Alice Johnson",
  "email": "alice@example.com"
}

Tasks

Create a Task

POST /api/tasks
Content-Type: application/json

{
  "title": "Review pull request",
  "description": "Review and merge the authentication PR",
  "userId": "64f1a2b3c4d5e6f7a8b9c0d1"
}

Response:

{
  "_id": "74a2b3c4d5e6f7a8b9c0d1e2",
  "title": "Review pull request",
  "description": "Review and merge the authentication PR",
  "userId": "64f1a2b3c4d5e6f7a8b9c0d1",
  "createdAt": "2024-09-01T10:30:00.000Z"
}

After a successful response, a TASK_CREATED event is published to RabbitMQ and the user will receive an email notification asynchronously.


Get All Tasks

GET /api/tasks

πŸ”„ Event Flow

Client
  β”‚
  β”‚  POST /api/tasks
  β–Ό
api-gateway :8080
  β”‚  (rate limit check β†’ route match β†’ forward)
  β–Ό
task-service :3002
  β”œβ”€β”€ Saves task to MongoDB (tasks DB)
  └── Publishes to RabbitMQ ──► queue: task_created
                                        β”‚
                                        β”‚ (async)
                                        β–Ό
                              notification-service
                                β”œβ”€β”€ Consumes message
                                β”œβ”€β”€ Extracts userId
                                β”œβ”€β”€ GET http://user-service:3001/users/:userId
                                β”‚         β”‚
                                β”‚         β–Ό
                                β”‚   user-service
                                β”‚   └── Returns { email, name }
                                β”‚
                                β”œβ”€β”€ Sends email (Simulation)
                                └── ack() β€” marks message as processed

Message Payload (TASK_CREATED)

{
  "event": "TASK_CREATED",
  "taskId": "74a2b3c4d5e6f7a8b9c0d1e2",
  "userId": "64f1a2b3c4d5e6f7a8b9c0d1",
  "title": "Review pull request",
  "createdAt": "2024-09-01T10:30:00.000Z"
}

πŸ’‘ Design Decisions

API Gateway as Single Entry Point

The gateway decouples the client from individual service ports. The React frontend only ever talks to localhost:8080 β€” it has no knowledge of internal service URLs. This also centralizes cross-cutting concerns like CORS, rate limiting, and logging in one place.

Domain Isolation via Logical Databases

MongoDB is shared at the infrastructure level across services, but each service connects to its own logical database (users, tasks). This maintains domain boundaries without the operational overhead of separate MongoDB instances.

Async Decoupling via RabbitMQ

Task creation and email notification are intentionally decoupled. A notification failure (e.g., SMTP timeout) has zero impact on the task creation response. This improves reliability and user experience.

Retry-Based RabbitMQ Connection

Services implement retry logic when connecting to RabbitMQ at startup β€” essential since RabbitMQ may not be ready immediately when containers start.

Service Discovery via Docker DNS

Internal services resolve each other using Docker Compose service names (e.g., http://user-service:3001). No hardcoded IPs β€” Docker's internal DNS handles routing automatically.

Message Acknowledgement

The notification-service only calls ack() after successfully processing a message. If processing fails, the message stays unacknowledged and can be requeued β€” preventing silent data loss.


🐳 Docker Compose Overview

services:
  mongo:                 # Shared MongoDB instance
  rabbitmq:              # Message broker (management UI: :15672)
  api-gateway:           # Single entry point β€” port 8080
  user-service:          # User management β€” port 3001
  task-service:          # Task CRUD + event publisher β€” port 3002
  notification-service:  # Event consumer + notification sender β€” port 3003

All services share a custom Docker bridge network, enabling hostname-based service discovery.


πŸ”Œ RabbitMQ Management UI

Once running, visit http://localhost:15672

Field Value
Username guest
Password guest

Built with ❀️ using Node.js · Express · MongoDB · RabbitMQ · Docker

About

A microservices backend for task management featuring async event-driven notifications, RabbitMQ messaging, MongoDB domain isolation, and full Docker Compose orchestration.

Topics

Resources

Stars

Watchers

Forks

Releases

Packages

Contributors

Languages