A learning project built with Django 5.2.7 using a microservices architecture. The project demonstrates how to build independent services with separate databases, an API Gateway, JWT authentication, and eventually asynchronous communication using RabbitMQ.
Current Goal: Build a complete authentication flow through the API Gateway using synchronous communication between Auth Service and User Service. RabbitMQ/Kafka will be introduced later.
flowchart TD
C["Client"] --> G["API Gateway"]
G --> A["Auth Service"]
G --> U["User Service"]
A --> AD[("Auth Database")]
U --> UD[("User Database")]
A -. "UserCreated event later" .-> R["RabbitMQ"]
R -.-> U
R -.-> N["Notification Service"]
Responsible for:
- User authentication
- Password hashing
- JWT generation
- Refresh tokens
- Account activation
Database contains:
- id
- password_hash
- is_active
- created_at
Responsible for:
- User profile
- First name
- Last name
- Avatar
- Bio
- Preferences
Database contains:
- id
- auth_user_id
- first_name
- last_name
- bio
- avatar
- created_at
auth_user_idis an external identifier and not a database foreign key.
Responsible for:
- Email notifications
- Welcome messages
- Notification history
- Delivery status
Responsible for:
- Routing requests
- Rate limiting
- Request IDs
- Timeouts
- Forwarding client information
The gateway does not contain business logic.
POST /api/auth/register
↓
API Gateway
↓
Auth Service creates account
↓
Auth Service requests profile creation
from User Service
↓
Auth Service returns JWT and user info
Example request
{
"email": "anower@example.com",
"password": "StrongPassword123",
"first_name": "Anower",
"last_name": "Hossain"
}POST /api/auth/login
↓
Auth Service
↓
JWT Access Token
GET /api/users/me
Authorization: Bearer <access-token>
↓
API Gateway
↓
User Service
↓
Validate JWT
↓
Read auth_user_id
↓
Return profile
Example payload
{
"sub": "auth-user-uuid",
"email": "anower@example.com",
"roles": ["user"],
"iss": "auth-service",
"aud": "microservices-lab",
"exp": 1784350000
}Recommended approach:
- Auth Service signs JWT using an RSA private key.
- API Gateway validates using the public key.
- User Service validates using the public key.
- Private key exists only inside Auth Service.
Internal endpoint
POST /internal/profilesHeaders
X-Internal-Service-Key: <service-secret>Example body
{
"auth_user_id": "40e63125-0188-4454-9ae4-72ae477d83db",
"first_name": "Anower",
"last_name": "Hossain"
}Requirements
- Internal authentication
- Short timeout
- Idempotent requests
- Return existing profile if already created
- Never expose this endpoint publicly
- Docker Compose
- API Gateway
- Auth Service
- User Service
- PostgreSQL for Auth
- PostgreSQL for User
- Shared Docker network
- Environment variables
- Health checks
Routing
/api/auth/* → Auth Service
/api/users/* → User Service
Gateway responsibilities
- Routing
- Request IDs
- Rate limiting
- Timeouts
- Client forwarding
Implement synchronous communication
Auth Service
│
└────────HTTP────────► User Service
Create the user profile immediately after successful registration.
Example failure
Auth Service ✓
User Service ✗
Result
- Account exists
- Profile missing
Handle using
- HTTP timeout
- Retry
- Error logging
- PROFILE_PENDING status
- Reconciliation job
Avoid distributed database transactions.
Replace synchronous profile creation with RabbitMQ.
flowchart TD
A["Auth Service"] --> B["RabbitMQ"]
B --> C["User Service"]
B --> D["Notification Service"]
Registration flow
Auth Service
↓
Publish UserCreated
↓
RabbitMQ
↓
User Service
↓
Create Profile
↓
Notification Service
↓
Send Welcome Notification
Example event
{
"event_id": "unique-event-uuid",
"event_type": "user.created",
"event_version": 1,
"occurred_at": "2026-07-18T02:00:00Z",
"data": {
"auth_user_id": "40e63125-0188-4454-9ae4-72ae477d83db",
"email": "anower@example.com",
"first_name": "Anower",
"last_name": "Hossain"
}
}Future topics
- Transactional Outbox
- Event Versioning
- Retry Queues
- Dead Letter Queues
- At-least-once Delivery
- Idempotent Consumers
- Eventual Consistency
Kafka can be introduced later for event replay, durable event storage, and high-throughput processing.
| Benefit | Consequence |
|---|---|
| Independent services | More repositories |
| Separate databases | Eventual consistency |
| Independent deployment | More infrastructure |
| Failure isolation | Network failures |
| Team ownership | API contracts |
| Async messaging | Duplicate messages |
| Independent releases | Backward compatibility |
The immediate objective is to complete this flow:
Client
│
▼
API Gateway
│
├────────► Auth Service ───────► JWT
│
└────────► User Service ───────► Profile
Once the synchronous version works correctly in Docker Compose, RabbitMQ will be introduced to replace direct service-to-service communication and move toward an event-driven architecture.