Go (Golang) Tutorial 0/45 lessons ~6 min read Lesson 41

    Building Microservices

    Microservices in Go decompose systems into independently deployable services communicating via HTTP/gRPC and async messaging (Kafka, NATS, RabbitMQ).

    Course progress0%
    Focus
    10 guided sections
    Practice signal
    Examples included
    Career prep
    Interview Q&A included

    Introduction

    Microservices in Go decompose systems into independently deployable services communicating via HTTP/gRPC and async messaging (Kafka, NATS, RabbitMQ). Go's small binaries, fast startup, and concurrency model make it ideal for containerized microservices on Kubernetes.

    Each service owns its database (database-per-service pattern), exposes health endpoints, and propagates trace context. Service discovery, API gateways, and circuit breakers address distributed system challenges — interview favorites.

    This lesson outlines order and inventory microservices with HTTP communication, shared observability, and independent deployment — architecture patterns from real cloud-native systems.

    The story

    Uber's architecture splits ride matching, pricing, and payments into independent Go services communicating over gRPC and Kafka. Each service owns its database, exposes health and metrics endpoints, and deploys independently through Kubernetes — a failure in the pricing service doesn't take down trip history if circuit breakers and timeouts are configured correctly.

    Microservices trade operational complexity for team autonomy: Go's fast compile times and single-binary deployment make service proliferation manageable compared to JVM-based alternatives.

    Understanding the topic

    Key concepts

    • Single responsibility per service with bounded context.
    • Database per service — no shared tables across teams.
    • Synchronous: REST/gRPC between services.
    • Async: events via message broker for decoupling.
    • API Gateway routes external traffic to internal services.
    • Health (/health) and readiness (/ready) probes for K8s.
    text
    flowchart TB
    Gateway[API Gateway] --> SvcA[Order Service]
    Gateway --> SvcB[User Service]
    SvcA --> MQ[Message Queue]
    SvcB --> MQ

    Step-by-step explanation

    1. Client → API Gateway → Order Service.
    2. Order Service calls Inventory Service via HTTP client.
    3. Order created event published to Kafka.
    4. Notification Service consumes event, sends email.
    5. Each service has own repo, Dockerfile, K8s Deployment.
    6. OpenTelemetry traces propagate across service calls.

    Practical code example

    Order service calling inventory service with timeout and error handling:

    go
    package main
    import (
    "context"
    "encoding/json"
    "fmt"
    "net/http"
    "time"
    )
    type InventoryClient struct {
    base string
    client *http.Client
    }
    func NewInventoryClient(base string) *InventoryClient {
    return &InventoryClient{
    base: base,
    client: &http.Client{Timeout: 3 * time.Second},
    }
    }
    func (c *InventoryClient) CheckStock(ctx context.Context, sku string, qty int) (bool, error) {
    url := fmt.Sprintf("%s/stock/%s?qty=%d", c.base, sku, qty)
    req, err := http.NewRequestWithContext(ctx, http.MethodGet, url, nil)
    if err != nil {
    return false, err
    }
    resp, err := c.client.Do(req)
    if err != nil {
    return false, fmt.Errorf("inventory call: %w", err)
    }
    defer resp.Body.Close()
    if resp.StatusCode != http.StatusOK {
    return false, fmt.Errorf("inventory status %d", resp.StatusCode)
    }
    var result struct{ Available bool `json:"available"`}
    if err := json.NewDecoder(resp.Body).Decode(&result); err != nil {
    return false, err
    }
    return result.Available, nil
    }
    func orderHandler(inv *InventoryClient) http.HandlerFunc {
    return func(w http.ResponseWriter, r *http.Request) {
    ctx := r.Context()
    ok, err := inv.CheckStock(ctx, "SKU-42", 1)
    if err != nil {
    http.Error(w, "inventory unavailable", 503)
    return
    }
    if !ok {
    http.Error(w, "out of stock", 409)
    return
    }
    w.WriteHeader(http.StatusCreated)
    fmt.Fprint(w, `{"order_id":"ord-123"}`)
    }
    }

    Line-by-line code explanation

    • service boundaries — split by business capability, not by technical layer.
    • gRPC + protobuf — typed contracts between services with codegen from .proto files.
    • each service owns its datastore — no shared databases across service boundaries.
    • /healthz and /readyz endpoints for Kubernetes liveness and readiness probes.
    • Prometheus /metrics exposes request latency histograms and error counters.
    • distributed tracing — propagate trace IDs via OpenTelemetry context across gRPC metadata.
    • circuit breakers — fail fast when downstream services exceed error thresholds.
    • API gateway — Kong or Envoy terminates TLS and routes external traffic to internal services.

    Key takeaway: 503 when dependency down; 409 for business conflict. Context propagates cancellation. Each service deploys independently.

    Real-world use

    Where you'll use this in production

    • E-commerce order, payment, inventory, notification services.
    • Banking account and transaction processing domains.
    • IoT telemetry ingestion and alerting pipelines.
    • Multi-team SaaS platforms with service ownership.

    Best practices

    • Design for failure — timeouts, retries, circuit breakers.
    • Idempotent endpoints for safe retries.
    • Database per service — integrate via API/events only.
    • Distributed tracing with OpenTelemetry on all outbound calls.
    • Version internal APIs; avoid breaking changes without notice.
    • Document service contracts with OpenAPI or protobuf.

    Common mistakes

    • Distributed monolith — shared database across services.
    • No timeout on service calls — cascading failure.
    • Chatty synchronous calls — prefer events for non-critical paths.
    • Missing health checks — K8s routes traffic to broken pods.
    • Shared libraries with business logic coupling teams.

    Advanced interview questions

    Q1BeginnerMicroservice vs monolith?
    Microservices independent deploy/scale; monolith simpler ops — choose based on team scale.
    Q2BeginnerSync vs async communication?
    Sync HTTP/gRPC for query/command needing response; async events for notify/decouple.
    Q3IntermediateDatabase per service?
    Each service owns its data; others access via API — prevents tight coupling.
    Q4IntermediateHandle inventory service down?
    Timeout, return 503, optionally queue order for retry; circuit breaker stops hammering.
    Q5AdvancedDesign order flow across 4 microservices.
    Gateway→Order→Inventory(check)→Payment(charge)→Event→Notification; saga or outbox for consistency.

    Summary

    Go microservices deploy as small containers on Kubernetes. Database per service; integrate via HTTP/gRPC and events. Timeouts, health checks, and tracing are mandatory. API Gateway exposes unified external interface. Next lesson: performance optimization.

    Ready to mark this lesson complete?Track your journey across the entire course.