gRPC Project: Building a Microservice System
In this tutorial, you will learn about grpc project: building a microservice system. We cover key concepts, practical examples, and best practices to help you master this topic.
This project brings together everything you have learned about gRPC. You will build a complete microservice system with multiple services, authentication, streaming, error handling, and production deployment.
Project Overview
Build an e-commerce platform backend with three gRPC services: User Service, Product Service, and Order Service. Services communicate via gRPC with mutual TLS, client-side load balancing, and comprehensive error handling.
What You'll Build
- Protobuf definitions for all services
- User Service with authentication
- Product Service with server-side streaming for inventory
- Order Service with bidirectional streaming for order processing
- API Gateway with gRPC-Web for browser access
- Docker deployment with health checking
Why This Project
This project simulates a real-world microservice architecture. You will face the same challenges encountered when building production gRPC systems: service coordination, data consistency, error handling, and deployment complexity.
Flow Chart
flowchart TD
A[Browser] --> B[Envoy Proxy]
B --> C[API Gateway]
C --> D[User Service]
C --> E[Product Service]
C --> F[Order Service]
D --> G[(User DB)]
E --> H[(Product DB)]
F --> I[(Order DB)]
F --> E
F --> D
Architecture
Service Definitions
syntax = "proto3";
service UserService {
rpc Register(RegisterRequest) returns (UserResponse);
rpc Login(LoginRequest) returns (TokenResponse);
rpc GetUser(GetUserRequest) returns (UserResponse);
rpc UpdateUser(UpdateUserRequest) returns (UserResponse);
}
service ProductService {
rpc GetProduct(GetProductRequest) returns (Product);
rpc ListProducts(ListProductsRequest) returns (stream Product);
rpc UpdateInventory(UpdateInventoryRequest) returns (InventoryResponse);
}
service OrderService {
rpc CreateOrder(CreateOrderRequest) returns (Order);
rpc GetOrder(GetOrderRequest) returns (Order);
rpc ProcessOrders(stream OrderAction) returns (stream OrderStatus);
rpc CancelOrder(CancelOrderRequest) returns (OrderResponse);
}
Authentication Interceptor
class AuthInterceptor(grpc.ServerInterceptor):
def __init__(self, jwt_secret):
self.jwt_secret = jwt_secret
def intercept_service(self, continuation, handler_call_details):
method = handler_call_details.method
public_methods = [
'/ecommerce.UserService/Register',
'/ecommerce.UserService/Login',
]
if method in public_methods:
return continuation(handler_call_details)
metadata = dict(handler_call_details.invocation_metadata)
token = metadata.get('authorization', '').replace(
'Bearer ', '')
try:
payload = jwt.decode(
token, self.jwt_secret,
algorithms=['HS256'])
return continuation(handler_call_details)
except jwt.InvalidTokenError:
return self._deny_handler()
def _deny_handler(self):
return grpc.unary_unary_rpc_method_handler(
lambda request, context: context.abort(
grpc.StatusCode.UNAUTHENTICATED,
'Invalid or expired token'))
Order Processing with Bidirectional Streaming
func (s *orderServer) ProcessOrders(
stream pb.OrderService_ProcessOrdersServer) error {
for {
action, err := stream.Recv()
if err == io.EOF {
return nil
}
if err != nil {
return err
}
switch action.Type {
case pb.OrderAction_CREATE:
order, err := s.createOrder(
action.GetCreateRequest())
if err != nil {
stream.Send(&pb.OrderStatus{
OrderId: action.OrderId,
Status: pb.OrderStatus_FAILED,
Error: err.Error(),
})
continue
}
stream.Send(&pb.OrderStatus{
OrderId: order.Id,
Status: pb.OrderStatus_CONFIRMED,
})
case pb.OrderAction_CANCEL:
err := s.cancelOrder(action.OrderId)
status := pb.OrderStatus_CANCELLED
errorMsg := ""
if err != nil {
status = pb.OrderStatus_FAILED
errorMsg = err.Error()
}
stream.Send(&pb.OrderStatus{
OrderId: action.OrderId,
Status: status,
Error: errorMsg,
})
}
}
}
Health Checking
import grpc
from grpc_health.v1 import health_pb2
from grpc_health.v1 import health_pb2_grpc
class HealthServicer(health_pb2_grpc.HealthServicer):
def Check(self, request, context):
return health_pb2.HealthCheckResponse(
status=health_pb2.HealthCheckResponse.SERVING)
def Watch(self, request, context):
yield health_pb2.HealthCheckResponse(
status=health_pb2.HealthCheckResponse.SERVING)
Docker Compose Deployment
version: '3.8'
services:
envoy:
image: envoyproxy/envoy:v1.28.0
ports:
- "8080:8080"
volumes:
- ./envoy.yaml:/etc/envoy/envoy.yaml
depends_on:
- api-gateway
api-gateway:
build: ./gateway
ports:
- "50051:50051"
depends_on:
- user-service
- product-service
- order-service
user-service:
build: ./services/user
ports:
- "50052:50051"
environment:
- DB_HOST=user-db
- JWT_SECRET=${JWT_SECRET}
depends_on:
- user-db
product-service:
build: ./services/product
ports:
- "50053:50051"
depends_on:
- product-db
order-service:
build: ./services/order
ports:
- "50054:50051"
depends_on:
- order-db
- user-service
- product-service
user-db:
image: postgres:15
environment:
POSTGRES_DB: users
product-db:
image: postgres:15
environment:
POSTGRES_DB: products
order-db:
image: postgres:15
environment:
POSTGRES_DB: orders
Common Mistakes
| Mistake | Explanation |
|---|---|
| Tight coupling between services | Services should communicate through defined protobuf interfaces, not direct database access |
| Ignoring idempotency | Order creation and payment processing must be idempotent to handle retries safely |
| Missing metrics and tracing | Add OpenTelemetry instrumentation from the start to debug production issues |
| Not testing streaming edge cases | Test client disconnects, slow consumers, and network interruptions during streams |
| Overlooking schema evolution | Plan for protobuf field deprecation and wire compatibility from day one |
FAQ
Mini Project
Complete the full e-commerce platform implementation. Extend it with a payment service, notification service, and admin dashboard. Add OpenTelemetry tracing, Prometheus metrics, and structured logging. Write integration tests in at least two languages.
What's Next
Learn about AsyncAPI for event-driven API documentation
Built by the developers of DodaTech
Doda Browser, DodaZIP & Durga Antivirus Pro