Skip to content

Repository files navigation

Microservices Go

A production-ready microservices architecture built with Go, featuring REST APIs, GraphQL Gateway, RabbitMQ for async communication, PostgreSQL databases, and comprehensive observability.

🏗️ Architecture

┌─────────────────┐
│ Client Apps │
└────────┬────────┘
│
▼
┌─────────────────────┐
│ Nginx Reverse │ ← Port 80, Caching Layer
│ Proxy + Cache │ (5 min TTL, 100MB)
└────────┬────────────┘
│
▼
┌─────────────────────┐
│ GraphQL Gateway │ ← Port 4000
│ (Authentication, │
│ Rate Limiting, │
│ DataLoader) │
└────────┬────────────┘
│
┌────┴────┬────────┐
▼ ▼ ▼
┌────────┐ ┌────────┐ ┌────────┐
│ User │ │ Order │ │ Payment│ ← Services
│ Service│ │ Service│ │ Service│
│:8081 │ │:8082 │ │:8083 │
└───┬────┘ └───┬────┘ └───┬────┘
│ │ │
▼ ▼ ▼
┌────────┐ ┌────────┐ ┌────────┐
│Postgres│ │Postgres│ │Postgres│
│:5432 │ │:5433 │ │:5434 │
└────────┘ └────────┘ └────────┘
\ | /
\ | /
▼ ▼ ▼
┌─────────────────┐
│ RabbitMQ │ ← Message Broker
│ :5672/:15672 │
└─────────────────┘

🚀 Quick Start

Prerequisites

  • Docker & Docker Compose
  • Go 1.21+
  • Make

1. Clone and Setup

git clone <repository-url>cd microservices-go
cp .env.example .env

2. Start All Services

# Build and start all services
make up-d
# Or with logs
make up

3. Run GraphQL Gateway (Local)

make run-gateway

4. Access Services

ServiceURLNotes
Nginx (Recommended)http://localhostReverse proxy + caching
GraphQL Playground (Direct)http://localhost:4000Via Gateway
User Service RESThttp://localhost:8081/api/v1/usersDirect access
Order Service RESThttp://localhost:8082/api/v1/ordersDirect access
Payment Service RESThttp://localhost:8083/api/v1/paymentsDirect access
Adminer (DB UI)http://localhost:8080Database management
RabbitMQ Managementhttp://localhost:15672guest/guest

| Prometheus | http://localhost:9090 | Metrics |

💡 Recommendation: Use Nginx (port 80) for best performance with caching enabled.

📁 Project Structure

microservices-go/
├── gateway/ # GraphQL Gateway (runs locally)
│ ├── cmd/main.go
│ ├── graph/
│ │ ├── schema.graphqls # GraphQL schema definition
│ │ ├── resolver.go # GraphQL resolvers
│ │ ├── dataloader.go # N+1 query fix
│ │ └── model/
│ └── middleware/
│
├── services/
│ ├── user/ # User Service
│ │ ├── cmd/main.go
│ │ ├── internal/
│ │ │ ├── user/ # Feature-based: handler, service, repository, model
│ │ │ └── rabbit/ # Event publisher
│ │ ├── migrations/ # Database migrations
│ │ └── Dockerfile
│ │
│ ├── order/ # Order Service
│ │ └── ... (same structure)
│ │
│ └── payment/ # Payment Service
│ └── ... (same structure)
│
├── shared/ # Shared packages
│ ├── logger/ # Zerolog logging
│ ├── config/ # Environment configuration
│ ├── errors/ # Structured error handling
│ ├── middleware/ # Auth, rate limiting, logging
│ ├── validator/ # Input validation
│ ├── tracing/ # OpenTelemetry tracing
│ └── rabbitmq/ # RabbitMQ client
│
├── docker-compose.yml # Infrastructure & services
├── Makefile # Development commands
├── prometheus.yml # Prometheus configuration
└── README.md

🔧 Available Commands

# Build and run
make build # Build all Docker images
make up # Start all services
make up-d # Start all services (detached)
make down # Stop all services
make restart # Restart all services
make logs # View logs
make logs-f # Follow logs# Local development
make run-gateway # Run GraphQL Gateway locally
make run-user # Run User Service locally
make run-order # Run Order Service locally
make run-payment # Run Payment Service locally
make infra # Start only infrastructure (DB, RabbitMQ)# Nginx
make nginx-up # Start Nginx reverse proxy
make nginx-down # Stop Nginx
make nginx-restart # Restart Nginx
make nginx-logs # View Nginx logs
make nginx-clear-cache # Clear Nginx cache
make nginx-status # Check Nginx status# Testing
make test# Run all tests
make test-user # Run User Service tests
make test-coverage # Run tests with coverage# Database
make migrate-up # Run migrations
make migrate-down # Rollback migrations# Code quality
make lint # Run linter
make fmt # Format code
make tidy # Tidy Go modules# Utilities
make health # Check service health
make api-docs # Show API documentation
make clean # Clean containers
make clean-all # Clean everything

📡 GraphQL API Examples

Register User

mutation {
register(input: {
email: "user@example.com"password: "password123"firstName: "John"lastName: "Doe"
}) {
tokenuser {
idemailfullName
}
}
}

Login

mutation {
login(input: {
email: "user@example.com"password: "password123"
}) {
tokenuser {
idemail
}
}
}

Create Order (Authenticated)

mutation {
createOrder(input: {
shippingAddress: "123 Main St, City, Country"notes: "Please handle with care"items: [
{
productId: "prod-1"productName: "Laptop"quantity: 1unitPrice: 999.99
}
]
}) {
idstatustotalAmountitems {
productNamequantityunitPrice
}
}
}

Get Orders with User and Payment (N+1 Fixed)

query {
orders(limit: 10) {
data {
idstatustotalAmountuser {
idemailfullName
}
payment {
idstatusamount
}
}
pageInfo {
totalhasMore
}
}
}

Get Current User with Orders

query {
me {
idemailfirstNamelastNameorders {
idstatustotalAmountitems {
productNamequantity
}
}
}
}

🔌 REST API Endpoints

User Service (Port 8081)

MethodEndpointDescriptionAuth
POST/api/v1/users/registerRegister new userNo
POST/api/v1/users/loginLogin userNo
GET/api/v1/usersList usersYes
GET/api/v1/users/:idGet user by IDYes
GET/api/v1/users/meGet current userYes
PUT/api/v1/users/:idUpdate userYes
DELETE/api/v1/users/:idDelete userYes
GET/healthHealth checkNo

Order Service (Port 8082)

MethodEndpointDescriptionAuth
POST/api/v1/ordersCreate orderYes
GET/api/v1/ordersList ordersYes
GET/api/v1/orders/my-ordersGet my ordersYes
GET/api/v1/orders/:idGet order by IDYes
PATCH/api/v1/orders/:id/statusUpdate order statusYes
GET/healthHealth checkNo

Payment Service (Port 8083)

MethodEndpointDescriptionAuth
POST/api/v1/paymentsCreate paymentYes
GET/api/v1/paymentsList paymentsYes
GET/api/v1/payments/my-paymentsGet my paymentsYes
GET/api/v1/payments/:idGet payment by IDYes
GET/api/v1/payments/order/:orderIdGet payment by orderYes
POST/api/v1/payments/:id/processProcess paymentYes
POST/api/v1/payments/:id/refundRefund paymentYes
GET/healthHealth checkNo

🏛️ Architecture Patterns

1. Feature-Based Structure

Each service follows feature-based organization:

service/
├── cmd/main.go # Entry point
├── internal/
│ ├── feature/ # Feature package
│ │ ├── handler.go # HTTP handlers
│ │ ├── service.go # Business logic
│ │ ├── repository.go # Data access
│ │ ├── model.go # Domain models
│ │ └── validator.go # Input validation
│ └── rabbit/ # Event handling
├── migrations/ # Database migrations
└── Dockerfile

2. Database Per Service

  • User Service: PostgreSQL on port 5432
  • Order Service: PostgreSQL on port 5433
  • Payment Service: PostgreSQL on port 5434

3. Async Communication via RabbitMQ

Events published:

  • user.created - When a new user registers
  • order.created - When a new order is placed
  • order.status_changed - When order status updates
  • payment.success - When payment is successful
  • payment.failed - When payment fails

4. ACID Transactions

Each service handles its own transactions:

// Repository pattern with transactionsfunc (r*Repository) Create(ctx context.Context, order*Order) error {
tx, err:=r.db.BeginTx(ctx, nil)
iferr!=nil {
returnerr
}
defertx.Rollback()
// Insert order// Insert order items// Commit transactionreturntx.Commit()
}

5. Structured Error Handling

{
"error": {
"code": "INVALID_INPUT",
"message": "Validation failed",
"details": "email: Invalid email format"
}
}

6. Security

  • JWT authentication at Gateway
  • Rate limiting (100 req/s default)
  • Security headers (CSP, HSTS, X-Frame-Options)
  • CORS configuration
  • SQL injection safe (parameterized queries)

7. Standardized Responses

All services use a standardized JSON response format via the shared/response package:

// Success Responsereturnresponse.Success(w, data, "Operation successful")
// Error Responsereturnresponse.Error(w, errors.ErrNotFound, "Resource not found")

Structure:

{
"success": true,
"message": "Operation successful",
"data": { ... },
"meta": { ... }
}

8. Soft Delete

Implemented using GORM's DeletedAt:

  • User & Order Services: Records are marked as deleted (timestamped) rather than physically removed.
  • Data Safety: Prevents accidental data loss and maintains audit history.
  • ** Automatic Filtering**: GORM automatically excludes soft-deleted records from standard queries.

9. Observability

  • Logging: Zerolog with structured JSON
  • Health Checks: /health endpoint on each service
  • Tracing: OpenTelemetry (prepared)

Note: Prometheus container running (port 9090) but not actively collecting metrics - to be implemented later.

10. Nginx Reverse Proxy with Caching

Nginx serves as the entry point for all client requests, providing:

  • Reverse Proxy: Routes requests to GraphQL Gateway
  • Caching Layer: 5-minute cache TTL for GraphQL queries
  • Rate Limiting: Additional protection (1000 req/min backup)
  • Static Assets: 1-year cache for CSS/JS files
  • Security Headers: X-Frame-Options, X-Content-Type-Options

Cache Headers:

X-Cache-Status: HIT ← Served from cache (< 1ms)
X-Cache-Status: MISS ← Fetched from gateway

Performance Improvement:

  • Without Nginx: ~15ms response time, 3,240 RPS
  • With Nginx: ~4ms response time, 11,878 RPS
  • 3.7x faster with caching! 🚀

🧪 Testing

# Run all tests
make test# Run specific service tests
make test-user
make test-order
make test-payment
# Run with coverage
make test-coverage

🚀 Performance Testing

Test your API performance with built-in benchmarking tools.

Quick Performance Report

make test-performance-quick

Sample Output:

1. SERVICE STATUS
-----------------
User Service: healthy
Order Service: healthy
Payment Service: healthy
2. RESPONSE TIME TEST
---------------------
User Health: 18ms average
Order Health: 18ms average
Payment Health: 20ms average
Gateway: 15ms average
Nginx: 16ms average
3. NGINX CACHING PERFORMANCE
----------------------------
First request (cache MISS): 0.81s
Second request (cache HIT): 1.10ms
Improvement: -35% faster

Full Performance Test Suite

make test-performance

Runs comprehensive tests including:

  • Single request latency
  • Cache performance comparison
  • Load tests (100-500 concurrent requests)
  • Concurrent user simulation

Load Test Specific Endpoint

# Test any endpoint
make load-test URL=http://localhost/ CONCURRENCY=50 REQUESTS=500

Performance Results

See detailed results in PERFORMANCE-REPORT.md:

make test-performance-report

Key Metrics:

MetricValueStatus
Response Time1-3ms average✅ Excellent
Throughput3,000-12,000 RPS✅ Excellent
Cache Hit Rate~90%✅ Good
Failed Requests< 1%✅ Excellent

Performance by Service:

ServiceRPSLatency
User Service5,2521.90ms
Order Service3,1593.17ms
Payment Service~3,159~3.17ms
Gateway (Direct)3,24015.43ms
Gateway + Nginx11,8784.21ms

Architecture Comparison:

Without Nginx (Direct Gateway):
3,240 RPS, 15ms latency
With Nginx (Cached):
11,878 RPS, 4ms latency
🚀 3.7x improvement!

🚢 Deployment

Docker Compose (Development)

make up-d

Production Considerations

  1. Environment Variables: Set production values in .env
  2. Secrets: Use Docker secrets or external secret management
  3. SSL/TLS: Enable HTTPS with reverse proxy (nginx/traefik)
  4. Scaling: Use Docker Swarm or Kubernetes
  5. Monitoring: Set up alerts in Prometheus/Grafana
  6. Backups: Configure database backups

☸️ Kubernetes (k3s) Deployment

This project includes Kubernetes manifests for deploying to k3s, a lightweight Kubernetes distribution.

Prerequisites

  • k3s installed and running
  • kubectl configured

Quick Start

# Setup (verify k3s and create namespace)
make k3s-setup
# Build images and load into k3s
make k3s-build
make k3s-import
# Deploy to k3s
make k3s-deploy

Available Commands

CommandDescription
make k3s-setupVerify k3s and create namespace
make k3s-buildBuild and load images into k3s
make k3s-deployDeploy to k3s
make k3s-upBuild and deploy (one command)
make k3s-statusCheck pod status
make k3s-logs SERVICE=gatewayView service logs
make k3s-redeployRedeploy services
make k3s-cleanDelete namespace (cleanup)
make k3s-testRun API tests

Access URLs (After Deployment)

NodePort (Direct):

Ingress (without host):

Ingress (with host):

For more details, see k8s/README.md.

📊 Monitoring

ToolURLPurpose
Nginxhttp://localhost/healthReverse proxy health
Adminerhttp://localhost:8080Database management UI
RabbitMQ Managementhttp://localhost:15672Message queue monitoring
Prometheushttp://localhost:9090Metrics collection (Zombie)

Connecting to Databases via Adminer

When using Adminer (http://localhost:8080) to manage databases, use the following connection details:

  • System: PostgreSQL
  • Server: (Use the service name from docker-compose)
    • postgres-user (for User Service)
    • postgres-order (for Order Service)
    • postgres-payment (for Payment Service)
  • Username: postgres (or as defined in your .env)
  • Password: password (or as defined in your .env)
  • Database: user, order, or payment (corresponding to the server)

Note: Use port 5432 (default) within the Docker network.

🛠️ Tech Stack

ComponentTechnology
LanguageGo 1.21+
Web FrameworkChi Router
GraphQLgqlgen
DatabasePostgreSQL 15
Message BrokerRabbitMQ
Reverse ProxyNginx
AuthenticationJWT
LoggingZerolog
Configurationgodotenv (Auto-loading)
MetricsPrometheus (Zombie)
Validationgo-playground/validator
TestingGo testing + testify
Performance TestingApache Bench (ab)

🤝 Contributing

  1. Fork the repository
  2. Create a feature branch
  3. Make your changes
  4. Run tests: make test
  5. Run linter: make lint
  6. Submit a pull request

📝 License

MIT License - see LICENSE file for details.

📧 Support

For questions or issues, please open a GitHub issue or contact the maintainers.

About

No description, website, or topics provided.

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages