Mikroservices in Go: Architektur und fortgeschrittene Praxis
Der Übergang vom Monolithen zur Mikroservice-Architektur ist eine unvermeidliche Phase in der Entwicklung vieler hochbelasteter Systeme. Die Sprache Go (Golang) hat sich aufgrund ihrer Leistungsfähigkeit, der integrierten Unterstützung für Nebenläufigkeit und ihrer minimalistischen Laufzeitumgebung zu einem der führenden Akteure in diesem Bereich entwickelt. In diesem Artikel betrachten wir fortgeschrittene Architekturmuster und praktische Techniken zum Aufbau von Mikroservices in Go, die Ihnen helfen, zuverlässige, skalierbare und ausfallsichere Systeme zu erstellen.
1. Architekturmuster: CQRS und Event Sourcing in Go
Für komplexe Domänenmodelle reicht das klassische CRUD oft nicht aus. Die Muster CQRS (Command Query Responsibility Segregation) und Event Sourcing ermöglichen es, Schreib- und Leseoperationen zu trennen sowie den Verlauf von Zustandsänderungen zu speichern.
Implementierung von CQRS in Go
Wir teilen das Modell in Befehle (Zustandsänderung) und Abfragen (Lesen) auf. Als Beispiel nehmen wir einen Dienst zur Bestellverwaltung.
// commands.gopackage orders
type CreateOrderCommand struct { UserID string Items []OrderItem}
type OrderCommandHandler struct { repo OrderRepository bus EventBus}
func (h *OrderCommandHandler) Handle(ctx context.Context, cmd CreateOrderCommand) error { order := NewOrder(cmd.UserID, cmd.Items) if err := h.repo.Save(ctx, order); err != nil { return err } h.bus.Publish(ctx, OrderCreatedEvent{ OrderID: order.ID, UserID: order.UserID, }) return nil}
// queries.gotype OrderQueryHandler struct { readRepo OrderReadRepository}
func (h *OrderQueryHandler) GetOrderByID(ctx context.Context, orderID string) (*OrderView, error) { return h.readRepo.FindByID(ctx, orderID)}Für Event Sourcing verwenden Sie die Bibliothek github.com/looplab/eventhorizon oder schreiben Ihre eigene Implementierung basierend auf Go-Kanälen.
2. Saga-Muster: verteilte Transaktionen
In der Welt der Mikroservices sind ACID-Transaktionen über mehrere Dienste hinweg nicht möglich. Das Saga-Muster löst dieses Problem durch eine Abfolge lokaler Transaktionen mit kompensierenden Aktionen.
Choreografie vs. Orchestrierung
Man unterscheidet zwei Ansätze: Choreografie (Dienste kommunizieren über Ereignisse) und Orchestrierung (zentraler Koordinator). Für Go-Projekte wird aufgrund der Dezentralisierung oft die Choreografie gewählt.
// saga.gopackage saga
type SagaStep struct { Name string Action func(ctx context.Context) error Compensate func(ctx context.Context) error}
type Saga struct { steps []SagaStep}
func (s *Saga) Execute(ctx context.Context) error { executed := []SagaStep{} for _, step := range s.steps { if err := step.Action(ctx); err != nil { // Kompensation aller ausgeführten Schritte for i := len(executed) - 1; i >= 0; i-- { executed[i].Compensate(ctx) } return err } executed = append(executed, step) } return nil}
// Beispiel zur Verwendung:func CreateOrderSaga(ctx context.Context) error { saga := Saga{ steps: []SagaStep{ {Name: "ReserveInventory", Action: reserveInventory, Compensate: releaseInventory}, {Name: "ChargePayment", Action: chargePayment, Compensate: refundPayment}, {Name: "SendEmail", Action: sendEmail, Compensate: noop}, }, } return saga.Execute(ctx)}Verwenden Sie für Nachrichtenbroker RabbitMQ oder NATS – diese lassen sich hervorragend mit Go über die Pakete github.com/streadway/amqp und github.com/nats-io/nats.go integrieren.
3. gRPC und dienstübergreifende Kommunikation
REST-APIs werden aufgrund des Textformats und der