Go in der Großindustrie: Warum es längst nicht mehr nur "eine Sprache für Microservices" ist
\n\nWenn Sie "Enterprise Go" hören, was kommt Ihnen in den Sinn? Langweilige Unternehmensrichtlinien? Endlose Code-Reviews? Oder vielleicht das Gefühl, dass Go nur für Startups und DevOps ist?
\n\nTatsächlich ist Go im Enterprise-Umfeld eine völlig andere Stufe der Disziplin und Ingenieurskultur. In großen Unternehmen (Uber, Dropbox, Salesforce, Twitch) schreibt man mit Go nicht nur Microservices, sondern ganze Plattformen, die Millionen von Anfragen pro Sekunde verarbeiten. Und hier reicht "es funktioniert einfach" nicht aus.
\n\nIn diesem Artikel analysieren wir die wichtigsten Besonderheiten der Go-Entwicklung im Enterprise: vom Paketdesign bis zum Production-Logging. Es wird viel Code und wenig Fülltext geben.
\n\n1. Architektur: Wiederholen Sie nicht den Fehler "Monolith in Microservices"
\n\nDas häufigste Problem, das ich in Go-Projekten auf Enterprise-Niveau sehe, ist der Versuch, eine Java-ähnliche Architektur auf Go zu übertragen. Das Ergebnis: Hunderte von unnötigen Interfaces und Abstraktionsebenen, die die Entwicklung nur verlangsamen.
\n\nDie Regel "Package by feature, not by layer"
\n\nAnstatt Ordner wie /models, /handlers, /services zu erstellen, gruppieren Sie nach Geschäftsdomänen:
myapp/\n├── cmd/\n│ └── server/\n│ └── main.go\n├── internal/\n│ ├── user/ # Alles, was mit Benutzern zu tun hat\n│ │ ├── handler.go\n│ │ ├── service.go\n│ │ ├── repository.go\n│ │ └── user.go # Datenmodell\n│ ├── payment/ # Alles, was mit Zahlungen zu tun hat\n│ │ ├── handler.go\n│ │ ├── service.go\n│ │ ├── gateway.go\n│ │ └── payment.go\n│ └── pkg/ # Allgemeine Hilfsmittel\n└── go.mod\n\nDieser Ansatz ist eine der Best Practices für Go im Enterprise. Er reduziert die Kopplung (Coupling) und macht den Code vorhersagbar. Wenn ein neuer Entwickler das Projekt öffnet, sieht er sofort die Geschäftslogik, nicht die Abstraktionsebenen.
\n\nInterfaces: Akzeptieren Sie Interfaces, geben Sie Strukturen zurück
\n\nDies ist ein klassischer Ratschlag aus der Go-Community, aber im Enterprise ist er entscheidend. Interfaces sollten klein sein (1-2 Methoden) und dort definiert werden, wo sie verwendet werden, nicht dort, wo sie implementiert werden.
\n\n// SCHLECHT: Interface neben der Implementierung definiert\npackage user\n\ntype UserRepository interface {\n FindByID(id string) (*User, error)\n Save(user *User) error\n}\n\ntype postgresRepo struct { ... }\n\n// GUT: Interface dort, wo es benötigt wird\npackage user\n\ntype Storage interface {\n Get(id string) (*User, error)\n Put(user *User) error\n}\n\ntype service struct {\n db Storage\n}\n\nfunc NewService(db Storage) *service {\n return &service{db: db}\n}\n\nWarum ist das wichtig? Weil Sie im Enterprise oft die Backends wechseln (PostgreSQL -> CockroachDB, Redis -> KeyDB). Interfaces, die auf der Verbraucherseite definiert sind, ermöglichen dies schmerzfrei.
\n\n2. Fehlerbehandlung: Hören Sie auf, _ zu ignorieren
\n\nGo hat keine Ausnahmen, und das ist großartig. Aber in Enterprise-Projekten sind Fehler nicht einfach nur if err != nil. Es ist eine ganze Kultur.
Wrappen oder nicht wrappen?
\n\nVerwenden Sie fmt.Errorf mit %w, um Fehlerketten zu erstellen. Dies bewahrt den Aufrufstapel und vereinfacht das Debugging.
func (s *service) ProcessOrder(ctx context.Context, orderID string) error {\n order, err := s.orderRepo.Get(ctx, orderID)\n if err != nil {\n return fmt.Errorf("getting order %s: %w", orderID, err)\n }\n\n if err := s.paymentGateway.Charge(ctx, order.Amount); err != nil {\n return fmt.Errorf("charging for order %s: %w", orderID, err)\n }\n\n return nil\n}\n\nAuf der obersten Ebene (im Handler/Middleware) verwenden Sie errors.Is() und errors.As(), um Fehlertypen zu überprüfen:
if errors.Is(err, ErrNotFo