Architecture Overview¶
Table of Contents¶
- System Architecture
- Services
- Communication Patterns
- Database Architecture
- Security Architecture
- Scalability Considerations
System Architecture¶
Mint follows a microservices architecture with event-driven communication using RabbitMQ. Each service is independently deployable, scalable, and maintains its own database following the "Database per Service" pattern.
High-Level Architecture¶
flowchart TB
%% =========================
%% API GATEWAY
%% =========================
subgraph Gateway["๐ API Gateway"]
NGINX["NGINX<br/>:80<br/><small>Rate Limiting ยท Load Balancing</small>"]
end
%% =========================
%% CORE SERVICES
%% =========================
subgraph Services["๐งฉ Core Services"]
AUTH["๐ Auth<br/>:4001"]
WALLET["๐ฐ Wallet<br/>:4003"]
TXN["๐ณ Transactions<br/>:4004"]
NOTIF["๐ Notifications<br/>:4002"]
end
%% =========================
%% DATA & MESSAGING
%% =========================
subgraph Infra["๐๏ธ Data & Messaging"]
REDIS[(โก Redis<br/>:6379)]
RABBIT["๐จ RabbitMQ<br/>:5672<br/><small>auth.events ยท transaction.events</small>"]
MONGO[(๐๏ธ MongoDB<br/>:27017<br/><small>auth ยท wallet ยท transactions</small>)]
end
%% =========================
%% OBSERVABILITY
%% =========================
subgraph Observability["๐ Observability"]
PROM["๐ Prometheus<br/>:9090"]
GRAF["๐ Grafana<br/>:3000"]
LOKI["๐ Loki<br/>:3100"]
ALLOY["๐ Alloy<br/><small>Log Collector</small>"]
end
%% =========================
%% TRAFFIC FLOW
%% =========================
NGINX --> AUTH
NGINX --> WALLET
NGINX --> TXN
%% =========================
%% CACHE
%% =========================
AUTH <--> |cache| REDIS
WALLET <--> |cache| REDIS
TXN <--> |cache| REDIS
%% =========================
%% ASYNC EVENTS
%% =========================
AUTH --> |auth.events| RABBIT
WALLET --> |wallet.events| RABBIT
TXN --> |transaction.events| RABBIT
RABBIT --> |consume| NOTIF
%% =========================
%% DATABASE
%% =========================
AUTH --> |users| MONGO
WALLET --> |wallets| MONGO
TXN --> |transactions| MONGO
%% =========================
%% METRICS
%% =========================
AUTH --> |/metrics| PROM
WALLET --> |/metrics| PROM
TXN --> |/metrics| PROM
NOTIF --> |/metrics| PROM
PROM --> GRAF
%% =========================
%% LOGS
%% =========================
AUTH --> |logs| ALLOY
WALLET --> |logs| ALLOY
TXN --> |logs| ALLOY
NOTIF --> |logs| ALLOY
ALLOY --> LOKI
LOKI --> GRAF
Communication Patterns¶
- Synchronous (HTTP/REST): Client โ NGINX โ Microservices
- Asynchronous (Events): Service โ RabbitMQ โ Consumer Services
- Service-to-Service: Via RabbitMQ events (no direct HTTP calls between services)
Services¶
๐ Auth Service¶
Port: 4001
Database: auth_db
Responsibilities¶
- User registration and authentication
- JWT token generation and validation
- JWKS endpoint for public key distribution
- User profile management
- Session management
Tech Stack¶
- Express.js - Web framework
- MongoDB/Mongoose - Database
- Redis/IORedis - Caching layer
- Argon2 - Password hashing
- Jose - JWT handling with RS256
- Zod - Request validation
- RabbitMQ - Event publishing
- Prometheus - Metrics collection
- Swagger/OpenAPI - API documentation
Key Features¶
- RS256 asymmetric JWT encryption
- HTTP-only cookie sessions
- JWKS endpoint for key rotation
- Secure password hashing with Argon2
- User search functionality
Events Published¶
user.signup- When a new user registers
๐ฐ Wallet Service¶
Port: 4003
Database: wallet_db
Responsibilities¶
- Wallet creation for new users
- Balance management
- Transaction application to wallets
- Wallet history tracking
- Balance integrity maintenance
Tech Stack¶
- Express.js - Web framework
- MongoDB/Mongoose - Database with optimistic locking
- RabbitMQ - Event consumption & publishing
- Jose - JWT verification
- Prometheus - Metrics collection
Key Features¶
- Automatic wallet creation on user signup
- Optimistic locking for concurrent transactions
- Event-driven balance updates
- Automatic rollback on failed transactions
- Balance consistency guarantees
Events Consumed¶
user.signup- Creates wallet with initial balancetransaction.completed- Updates wallet balancestransaction.failed- Reverts wallet changes
Events Published¶
wallet.transactionFinalized- Confirms transaction completion
๐ Transactions Service¶
Port: 4004
Database: transactions_db
Responsibilities¶
- Transaction creation (top-up, transfer)
- Transaction state management
- Transaction validation
- Transaction history
- Orchestrating transaction workflow
Tech Stack¶
- Express.js - Web framework
- MongoDB/Mongoose - Database
- Redis/IORedis - Caching layer
- RabbitMQ - Event consumption & publishing
- Zod - Request validation
- Jose - JWT verification
- Prometheus - Metrics collection
- Swagger/OpenAPI - API documentation
Key Features¶
- Multi-state transaction lifecycle (PENDING โ PROCESSING โ COMPLETED/FAILED)
- Balance validation before transfers
- Idempotency support
- Event-driven workflow orchestration
- Automatic failure handling
Transaction Types¶
- Top-Up: Add funds to user wallet
- Transfer: Send funds between users
Events Consumed¶
transaction.created- Begins processingwallet.transactionFinalized- Finalizes state
Events Published¶
transaction.created- Initiates workflowtransaction.completed- Success notificationtransaction.failed- Failure notification
๐จ Notifications Service¶
Port: 4002 Database: None (stateless)
Responsibilities¶
- Welcome email on user registration
- Transaction success notifications
- Transaction failure alerts
- Email template management
Tech Stack¶
- Express.js - Web framework
- RabbitMQ - Event consumption
- Nodemailer - Email sending
- Winston - Logging
Key Features¶
- Event-driven email delivery
- Template-based emails
- SMTP integration
- Async processing (doesn't block transactions)
Events Consumed¶
user.signup- Sends welcome emailtransaction.completed- Sends success notificationtransaction.failed- Sends failure alert
๐ API Gateway (NGINX)¶
Port: 80
Responsibilities¶
- Single entry point for all client requests
- Request routing to services
- Rate limiting
- Load balancing
- Health monitoring
Key Features¶
- Rate limiting: 10 req/s, burst 20
- Keepalive connections
- Automatic backend failover
- Health check monitoring
- Request/response buffering
- Graceful error handling
Route Configuration¶
/api/v1/auth/*โ Auth Service (4001)/api/v1/users/*โ Auth Service (4001)/api/v1/wallet/*โ Wallet Service (4003)/api/v1/transactions/*โ Transactions Service (4004)/.well-known/*โ Auth Service (4001)/metrics/*โ Prometheus metrics endpoints/api-docsโ Swagger API documentation/healthโ Gateway health check
โก Redis Cache¶
Port: 6379
Responsibilities¶
- In-memory caching for frequently accessed data
- Reducing database load
- Improving API response times
- Session data storage (future)
Tech Stack¶
- Redis 7 - In-memory data store
- IORedis - Node.js Redis client
- AOF Persistence - Append-only file for durability
Caching Strategy¶
- Pattern: Cache-aside (lazy loading)
- TTL: 5 minutes for user data, 3 minutes for transactions
- Key Structure:
<service>:<resource>:<identifier> - Failure Mode: Graceful degradation to database
Cached Data¶
- User Data (
auth:user:email:*,auth:user:exists:*) - Login lookups: 85% hit rate
- User existence checks: 80% hit rate
-
TTL: 5 minutes
-
Transaction Lists (
transactions:list:*) - Paginated queries: 80% hit rate
-
TTL: 3 minutes
-
Transaction Details (
transactions:detail:*) - Individual lookups: 75% hit rate
- TTL: 3 minutes
Cache Invalidation¶
- User Changes: Delete on signup
- Transaction Creation: Pattern delete on topup/transfer
- Manual: Flush via Redis CLI if needed
Performance Impact¶
- Before Caching: Login ~40ms, Transaction list ~80ms
- After Caching (hit): Login ~8ms, Transaction list ~12ms
- Improvement: 80-90% latency reduction on cache hits
๐ Prometheus¶
Port: 9090
Responsibilities¶
- Metrics collection from all services
- Time-series data storage
- Alerting rule evaluation
- PromQL query engine
- Target discovery and health monitoring
Tech Stack¶
- Prometheus - Metrics platform
- Prom-client - Node.js metrics library
- TSDB - Time-series database with 15-day retention
Metrics Scraped¶
- HTTP Metrics: Request duration, total requests, active connections
- Database Metrics: Query duration, operation counts
- Cache Metrics: Hit/miss rates, error counts
- Transaction Metrics: Creation rates, amount distribution
- System Metrics: CPU, memory, event loop lag, GC duration
Scrape Configuration¶
- Interval: 15 seconds
- Timeout: 10 seconds
- Targets: All service
/metricsendpoints - Storage: 15 days (configurable)
Sample Metrics¶
# API request rate
rate(http_requests_total[5m])
# P95 latency
histogram_quantile(0.95, http_request_duration_seconds_bucket)
# Cache hit rate
sum(rate(cache_hits_total[5m])) /
(sum(rate(cache_hits_total[5m])) + sum(rate(cache_misses_total[5m])))
# Database query time
histogram_quantile(0.95, db_query_duration_seconds_bucket)
๐ Grafana¶
Port: 3000 Default Credentials: admin/admin
Responsibilities¶
- Metrics visualization
- Dashboard creation and management
- Alerting and notifications
- Data source integration
- User management
Key Features¶
- Pre-configured Prometheus data source
- Custom dashboards for each service
- Real-time metric updates (30s refresh)
- Alert notifications (email, Slack, webhook)
- Dashboard provisioning via config files
Pre-built Dashboards¶
- Service Health Overview
- Uptime, error rates, active connections
- Request rate trends
-
Status code distribution
-
Performance Metrics
- API latency (P50, P95, P99)
- Database query times
- Cache hit rates
-
Event loop lag
-
Cache Analytics
- Hit/miss ratios by key prefix
- Cache operation errors
- Cache size trends
-
TTL effectiveness
-
Transaction Analytics
- Transaction creation rates
- Success vs failure ratios
- Amount distributions
-
Processing duration
-
System Resources
- Memory usage and GC patterns
- CPU utilization
- Active handles and requests
- Node.js metrics
Dashboard Provisioning¶
# grafana/provisioning/datasources/prometheus.yml
apiVersion: 1
datasources:
- name: Prometheus
type: prometheus
url: http://prometheus:9090
isDefault: true
# grafana/provisioning/dashboards/mint.json
# Pre-configured dashboards loaded on startup
๐ Loki¶
Port: 3100
Responsibilities¶
- Centralized log aggregation and storage
- Log indexing and querying via LogQL
- Long-term log retention
- Integration with Grafana for visualization
- Multi-tenant log isolation (future)
Tech Stack¶
- Loki - Log aggregation system
- LogQL - Query language for logs
- Object Storage - Log chunk storage (filesystem in dev)
Key Features¶
- Efficient Storage: Indexes only metadata, not full log content
- LogQL Queries: Powerful query language similar to PromQL
- Label-based Indexing: Fast filtering by service, level, etc.
- Retention Policies: Configurable log retention periods
- Compression: Efficient log chunk compression
- No Schema: No predefined log schema required
Log Storage¶
- Index: Label-based metadata (service, level, job)
- Chunks: Compressed log content
- Retention: 7 days default (configurable)
- Storage Backend: Filesystem (local), S3, GCS (production)
Sample LogQL Queries¶
# All logs from auth service
{service="@mint/auth"}
# Error logs across all services
{level="error"}
# Logs matching text pattern
{service="@mint/transactions"} |= "payment"
# JSON parsing and filtering
{service="@mint/auth"} | json | userId="507f1f77bcf86cd799439011"
# Metric from logs (count)
sum(rate({level="error"}[5m])) by (service)
# Top 10 error messages
topk(10, sum by(message) (rate({level="error"}[1h])))
๐ Grafana Alloy¶
Agent Type: Log Collection & Forwarding
Responsibilities¶
- Collect logs from all microservices
- Parse and enrich log data
- Forward logs to Loki
- Add labels and metadata
- Filter and transform log streams
Tech Stack¶
- Grafana Alloy - Unified observability agent
- Loki Client - Log shipping to Loki
- Docker Logs Driver - Container log collection
Log Collection Strategy¶
- Source: Docker container stdout/stderr
- Collection: Alloy reads from Docker socket
- Parsing: JSON log parsing with Winston format
- Enrichment: Adds job labels (service names)
- Delivery: Batched push to Loki
Configuration¶
# alloy/config.alloy
loki.source.docker "containers" {
host = "unix:///var/run/docker.sock"
targets = [
{
__path__ = "/var/lib/docker/containers/*/*.log",
job = "docker",
},
]
forward_to = [loki.write.endpoint.receiver]
}
loki.write "endpoint" {
endpoint {
url = "http://loki:3100/loki/api/v1/push"
}
}
Log Labels Applied¶
- service: Service name (@mint/auth, @mint/wallet, @mint/transactions, @mint/notifications)
- container_name: Docker container name
- environment: dev/staging/production
- level: Log level (error, warn, info, debug)
Communication Patterns¶
1. Synchronous Communication (REST API)¶
Used for client-facing operations that require immediate responses.
Characteristics: - HTTP/REST protocol - Request-response pattern - Immediate feedback to client - Used for queries and commands
Examples: - User login - Get wallet balance - Create transaction - Search users
2. Asynchronous Communication (Events)¶
Used for inter-service communication and decoupling.
Characteristics: - Publish-subscribe pattern - Eventual consistency - Loose coupling - Resilient to service failures - Enables multiple consumers
Examples: - User signup triggers wallet creation and welcome email - Transaction completion updates wallet and sends notification - Transaction failure triggers rollback
Database Architecture¶
Database per Service Pattern¶
Each service owns its data and database:
| Service | Database | Collections |
|---|---|---|
| Auth | auth_db |
users |
| Wallet | wallet_db |
wallets |
| Transactions | transactions_db |
transactions |
| Notifications | None | Stateless |
Benefits¶
- Loose coupling: Services are independent
- Technology flexibility: Each service can use appropriate database
- Scalability: Scale databases independently
- Fault isolation: Database issues don't cascade
Challenges¶
- Data consistency: Eventual consistency via events
- Joins: Cannot join across services (denormalization needed)
- Transactions: Distributed transactions require Saga pattern
Data Consistency¶
The system uses eventual consistency through events:
- Optimistic Locking: Wallet service uses version fields
- Event Ordering: RabbitMQ preserves message order per queue
- Idempotency: Events can be replayed safely
- Compensation: Failed transactions trigger reversal events
Security Architecture¶
Authentication Flow¶
1. User โ POST /api/v1/auth/signup
2. Auth Service โ Hash password with Argon2
3. Auth Service โ Store user in auth_db
4. Auth Service โ Publish user.signup event
5. Auth Service โ Return user info
Login:
1. User โ POST /api/v1/auth/login
2. Auth Service โ Verify password with Argon2
3. Auth Service โ Generate JWT (RS256)
4. Auth Service โ Set HTTP-only cookie
5. Auth Service โ Return user info
Authorization Flow¶
1. Client โ Request with cookie
2. NGINX โ Forward to service
3. Service โ Extract JWT from cookie
4. Service โ Fetch public key from JWKS endpoint
5. Service โ Verify JWT signature
6. Service โ Validate issuer, audience, expiry
7. Service โ Extract user ID from claims
8. Service โ Process request
Security Features¶
- Password Security
- Argon2 hashing (memory-hard, resistant to GPU attacks)
- No plaintext password storage
-
Secure random salts
-
JWT Security
- RS256 asymmetric encryption
- Private key only on auth service
- Public key distribution via JWKS
- Short expiration (7 days)
-
Issuer and audience validation
-
Cookie Security
- HTTP-only (no JavaScript access)
- Secure flag in production
- SameSite protection
-
Signed cookies
-
API Security
- Rate limiting at gateway
- Request validation with Zod
- Error messages don't leak info
- CORS configuration
Scalability Considerations¶
Horizontal Scaling¶
Each service can be scaled independently:
# docker-compose.yml
auth:
replicas: 3 # Scale auth service
wallet:
replicas: 5 # Scale wallet service more if needed
Database Scaling¶
- Read Replicas: Add MongoDB replicas for read-heavy services
- Sharding: Shard by userId for large-scale deployments
- Caching: Add Redis for frequently accessed data (future)
Message Queue Scaling¶
- Multiple Consumers: Scale consumers horizontally
- Queue Partitioning: Use routing keys for load distribution
- Dead Letter Queues: Handle failures gracefully (future)
Load Balancing¶
NGINX provides: - Round-robin load balancing - Health check-based routing - Keepalive connection pooling - Automatic failover
Bottlenecks to Monitor¶
- Database Connections: Monitor connection pool usage
- RabbitMQ Queue Depth: Watch for consumer lag
- JWT Verification: Cache JWKS keys
- Network I/O: Monitor service-to-service latency
System Qualities¶
Reliability¶
- Automatic transaction rollback on failures
- Event replay capability
- Health monitoring for all services
- Graceful degradation
Availability¶
- Stateless services (easy to replicate)
- No single point of failure (with scaling)
- Health checks and automatic restart
- Circuit breaker pattern (via NGINX retry)
Maintainability¶
- Clear service boundaries
- Consistent code structure across services
- Comprehensive logging
- Type-safe with TypeScript
Performance¶
- Async processing for non-critical paths
- Connection pooling
- Efficient database queries
- Rate limiting prevents overload
Design Decisions¶
Why RabbitMQ?¶
- Reliable message delivery
- Message persistence
- Flexible routing
- Wide ecosystem support
Why MongoDB?¶
- Schema flexibility
- JSON-native (matches TypeScript objects)
- Good performance for read-heavy workloads
- Horizontal scaling support
Why RS256 JWT?¶
- More secure than HS256 (asymmetric)
- Public key can be distributed safely
- Enables key rotation
- Industry standard
Why NGINX?¶
- Battle-tested reverse proxy
- Excellent performance
- Built-in rate limiting
- Comprehensive configuration options
Related Documentation¶
- Event Architecture - Detailed event flows
- Deployment Guide - Production deployment
- Development Guide - Local development
- API Reference - API documentation