The Problem
One state change often needs several follow-up actions. Low inventory may update dashboards, trigger supplier alerts, and schedule marketing changes. If the publisher directly calls each reaction, it becomes tightly coupled to every downstream concern.
The Solution
Observer lets publishers broadcast events to subscribed observers. In Go, that usually means a small subscriber interface and a publisher that owns a slice of observers. The publisher emits domain events without knowing what each subscriber will do with them.
Structure
- Subject:
StockPublishermanages subscriptions and emits inventory events. - Observers: Dashboard and marketing subscribers react independently.
- Event value:
InventoryEventcarries the data for each notification. - Client: Registers subscribers with the publisher at setup time.
Implementation
This example broadcasts a low-stock event to two independent subscribers. The publisher does not know whether downstream actions update a dashboard, send alerts, or pause promotions.
package main
type InventoryEvent struct {
SKU string
Remaining int
}
type InventorySubscriber interface {
HandleInventory(event InventoryEvent)
}
type StockPublisher struct {
subscribers []InventorySubscriber
}
func (p *StockPublisher) Subscribe(subscriber InventorySubscriber) {
p.subscribers = append(p.subscribers, subscriber)
}
func (p StockPublisher) Publish(event InventoryEvent) {
for _, subscriber := range p.subscribers {
subscriber.HandleInventory(event)
}
} Best Practices
- Keep events domain-shaped and specific so subscribers receive useful context.
- Make subscriber interfaces narrow and synchronous by default; add async delivery only when needed deliberately.
- Be careful with subscriber side effects because one slow observer can delay the whole notification path.
- Use Observer to decouple reactions, not to hide a large implicit workflow.
- Prefer explicit subscription setup so it is easy to see who is listening to what.
When to Use
- One event should trigger several independent reactions.
- The publisher should not depend on each downstream consumer directly.
- Subscribers may vary by environment or deployment needs.
When NOT to Use
- Only one known consumer exists and a direct method call is clearer.
- The order and coordination between reactions are critical and need a mediator or orchestrator.
- Implicit side effects would make the system too hard to trace.