Why Event-driven Architecture in Go is the New Black Belt in Development?
\n\nImagine an orchestra where each musician plays their part without waiting for a conductor, yet creates a perfect symphony. This is exactly how event-driven architecture in Go works — each service lives its own life, reacts to events and produces new ones, without blocking the overall flow.
\n\nIn a world where users demand instant response and systems process millions of requests per second, synchronous REST APIs start to "choke". Go with its lightweight goroutines and native concurrency support becomes the ideal language for building reactive systems. But how to turn the chaos of asynchronous calls into a coherent architecture? Let's figure it out.
\n\nWhat is Event-driven Architecture (EDA) and Why Go?
\n\nEvent-driven architecture in Go is a paradigm where system components communicate through events rather than direct calls. Instead of service A waiting for a response from service B, it simply publishes a message to a broker, and B picks it up when ready.
\n\nWhy is Go ideal for EDA?
\n- \n
- Goroutines — cheap "threads" for parallel event processing (you can run a million without issues) \n
- Channels — built-in mechanism for passing events between goroutines \n
- Fast compilation — iterations in microservices become lightning-fast \n
- Simplicity — less "magic", more control \n
Building Your First Event-driven System in Go: From Simple to Complex
\n\nLevel 1: Internal Events via Channels
\nLet's start with the basics. Imagine we are writing a server monitoring system. Each event (memory drop, CPU spike) must be processed asynchronously.
\n\npackage main\n\nimport (\n "fmt"\n "math/rand"\n "time"\n)\n\ntype Event struct {\n Type string\n Payload interface{}\n}\n\nfunc main() {\n // Create a channel for events\n eventChannel := make(chan Event, 100)\n \n // Producer: generate events\n go func() {\n for {\n time.Sleep(time.Duration(rand.Intn(1000)) * time.Millisecond)\n event := Event{\n Type: "system.memory.alert",\n Payload: map[string]interface{}{\n "usage_pct": rand.Float64() * 100,\n "timestamp": time.Now(),\n },\n }\n eventChannel <- event\n }\n }()\n \n // Consumer: process events\n go func() {\n for event := range eventChannel {\n fmt.Printf("Event received: %s\\n", event.Type)\n processEvent(event)\n }\n }()\n \n time.Sleep(5 * time.Second)\n}\n\nfunc processEvent(e Event) {\n // Simulate processing\n time.Sleep(200 * time.Millisecond)\n fmt.Printf("Processed: %v\\n", e.Payload)\n}\n\nThis is the basics, but for production you need something more. When the number of services exceeds a dozen, message brokers enter the stage.
\n\nLevel 2: Event-driven Architecture in Go with NATS
\nNATS is a lightweight broker that works perfectly with Go. It is faster than RabbitMQ and simpler than Kafka.
\n\npackage main\n\nimport (\n "log"\n "time"\n "github.com/nats-io/nats.go"\n)\n\ntype OrderEvent struct {\n OrderID string `json:"order_id"`\n Status string `json:"status"`\n Amount float64 `json:"amount"`\n Time time.Time `json:"time"`\n}\n\nfunc main() {\n // Connect to NATS\n nc, err := nats.Connect(nats.DefaultURL)\n if err != nil {\n log.Fatal(err)\n }\n defer nc.Close()\n \n // Publish event (producer)\n event := OrderEvent{\n OrderID: "ORD-12345",\n Status: "created",\n Amount: 299.99,\n Time: time.Now(),\n }\n \n data, _ := json.Marshal(event)\n