README
¶
JSON Microservices Example
This example demonstrates a complete microservices architecture using Weave with JSON serialization.
Architecture
┌─────────────────┐ ┌─────────────────┐ ┌─────────────────┐
│ API Client │────▶│ User Service │────▶│ Profile Service │
│ (client-only) │ │ (server+client) │ │ (server-only) │
└─────────────────┘ └─────────────────┘ └─────────────────┘
│ │ │
│ │ │
▼ ▼ ▼
users.get users.get profiles.get
users.list users.list profiles.list
events.user profiles.get
Services
Profile Service (Leaf Service)
- Type: Server-only
- Routes:
profiles.get,profiles.list - Description: Manages user profiles. This is a "leaf" service with no external dependencies.
User Service (Intermediate Service)
- Type: Server + Client
- Routes:
users.get,users.list - Description: Manages users and enriches responses with profile data by calling the Profile Service.
API Client (External Consumer)
- Type: Client-only
- Description: Simulates an external application making requests to the User Service.
Running the Example
Prerequisites
- RabbitMQ running locally (or via Docker)
- Go 1.21+
Start RabbitMQ
# Using Docker
docker run -d --name rabbitmq -p 5672:5672 -p 15672:15672 rabbitmq:3-management
Run the Services
Terminal 1 - Start Profile Service (start this first):
cd examples/json/profile-service
go run main.go
Terminal 2 - Start User Service:
cd examples/json/user-service
go run main.go
Terminal 3 - Run API Client:
cd examples/json/client
go run main.go
Message Flow
Get User by ID
- Client calls
users.getwith{"id": "1"} - User Service receives request, looks up user
- User Service calls
profiles.getwith{"user_id": "1"} - Profile Service returns profile data
- User Service combines user + profile
- Client receives enriched user response
List All Users
- Client calls
users.listwith{} - User Service retrieves all users
- User Service calls
profiles.getfor each user (concurrent) - Profile Service returns each profile
- User Service combines all data
- Client receives list of enriched users
Key Concepts Demonstrated
Server Pattern
// Create server with handlers
server, _ := weave.NewServer(config)
server.Handle("profiles.get", handleGetProfile)
server.Handle("profiles.list", handleListProfiles)
server.Start(ctx)
Client Pattern
// Create client for RPC calls
client, _ := weave.NewClient(config)
client.Connect(ctx)
response, _ := client.Call(ctx, "users.get", message, weave.WithTimeout(5*time.Second))
Combined Server + Client
// User Service acts as both
server, _ := weave.NewServer(config) // Receive requests
client, _ := weave.NewClient(config) // Call Profile Service
Fire-and-Forget Publishing
// Publish events without waiting for response
client.Publish(ctx, "events.user", eventMessage)
Data Models
User
{
"id": "1",
"name": "Alice Smith",
"email": "alice@example.com"
}
Profile
{
"user_id": "1",
"bio": "Software engineer and open source enthusiast",
"avatar": "https://example.com/avatars/alice.jpg",
"location": "San Francisco, CA",
"website": "https://alice.dev",
"verified": true
}
Enriched User (Combined)
{
"id": "1",
"name": "Alice Smith",
"email": "alice@example.com",
"profile": {
"user_id": "1",
"bio": "Software engineer and open source enthusiast",
"location": "San Francisco, CA",
"verified": true
}
}
Error Handling
- User not found: Returns
{"error": "user not found"} - Profile not found: User returned without profile data
- Timeout: Demonstrates handling with short timeout example
- Service unavailable: Graceful degradation
Extending This Example
- Add authentication middleware - Validate tokens before processing
- Add circuit breaker - Protect against cascading failures
- Add caching - Cache profile data to reduce calls
- Add metrics - Track request latency and error rates
- Add tracing - Distributed tracing across services
Click to show internal directories.
Click to hide internal directories.