GoStream Academy

Master event-driven architecture and real-time data systems with Go and Apache Kafka. Built by a backend specialist who's shipped production...
2 joined
Profile picture
@santorellistubbertProfile pictureMay 31

Why Your Go Microservices Should Stop Making HTTP Calls to Each Other

If you're building microservices in Go and they communicate via synchronous HTTP, you're building a distributed monolith. Here's why — and the pattern that fixes it.


The Problem with Synchronous Chaining


Order Service → POST /payments → Payment Service → POST /inventory → Inventory Service → POST /shipping → Shipping Service


What happens when the Inventory Service is slow? Everything is slow. What happens when Shipping is down? Orders fail. You've coupled every service's availability to every other service. Congratulations, you have a monolith with network latency.


The Fix: Event-Driven Architecture


Instead of calling services directly, each service publishes events and reacts to events:


// Order Service publishes an event — doesn't care who consumes it
event := OrderCreated{
    OrderID:    order.ID,
    CustomerID: order.CustomerID,
    Items:      order.Items,
    Total:      order.Total,
    CreatedAt:  time.Now(),
}
producer.Produce(ctx, "orders.created", event)


Now the Payment Service, Inventory Service, and Shipping Service each independently subscribe to orders.created and do their work on their own timeline. If Shipping is down for 5 minutes, it processes the backlog when it comes back. Orders never fail.


The 3 Properties You Gain


  1. Temporal decoupling — services don't need to be online simultaneously

  2. Failure isolation — one service crashing doesn't cascade

  3. Independent scalability — scale consumers based on their own throughput needs


The Tool: Apache Kafka + Go


Kafka gives you durable, ordered, partitioned event streams. Go gives you lightweight goroutines to consume them efficiently. Together they're the standard for serious event-driven systems.


The key primitives:

  • Topics — named streams of events (e.g. orders.created, payments.completed)

  • Partitions — parallelism within a topic (Go consumers in a group split the load)

  • Consumer groups — multiple instances of a service sharing the work

  • Offsets — each consumer tracks its position, enabling replay and exactly-once processing


A Minimal Consumer in Go


reader := kafka.NewReader(kafka.ReaderConfig{
    Brokers:  []string{"localhost:9092"},
    GroupID:  "payment-service",
    Topic:    "orders.created",
})
defer reader.Close()

for {
    msg, err := reader.ReadMessage(ctx)
    if err != nil {
        log.Error("read failed", "err", err)
        continue
    }
    
    var event OrderCreated
    if err := json.Unmarshal(msg.Value, &event); err != nil {
        // Send to dead-letter queue
        dlq.Produce(ctx, "orders.created.dlq", msg.Value)
        continue
    }
    
    if err := processPayment(ctx, event); err != nil {
        // Retry logic or DLQ
        handleFailure(ctx, event, err)
        continue
    }
}


This is 30 lines of code that makes your Payment Service independently deployable, independently scalable, and failure-isolated from every other service.


The Bottom Line


Synchronous HTTP between microservices is a trap. It feels simple until it isn't. Event-driven architecture with Kafka gives you the decoupling that microservices were supposed to provide in the first place.


---


I teach the complete path from EDA fundamentals to production Kafka deployments in Go — 7 modules, 21 hands-on lessons, capstone project. Check out the course if you want to go deep.