Domain-Driven Design in Go: Wie man nicht in Abstraktionsebenen ertrinkt

Online Python-Trainer für Anfänger

Lernen Sie Python einfach ohne theoretische Überlastung. Lösen Sie praktische Aufgaben mit automatischer Überprüfung und schreiben Sie Code direkt im Browser.

Kurs starten

Einleitung: Warum Ihr Go-Projekt nach DDD verlangt?

\n\n

Haben Sie jemals einen Dienst in Go geschrieben, der mit ein paar Endpunkten und drei Datenbanktabellen begann und sich nach einem halben Jahr in ein Monster mit kilometerlangen handler.go-Dateien und magischen Zahlen, die über den gesamten Code verstreut sind, verwandelte? Kommt Ihnen bekannt vor? Mir auch. Ich bin auch durch diese Phase gegangen.

\n\n

Das Problem der klassischen Go-Entwicklung ist, dass wir zu früh an Datenbanken und JSON denken und das Wesentliche vergessen: die Geschäftslogik. Domain-Driven Design (DDD) kommt genau dann zur Hilfe, wenn man im Chaos Ordnung schaffen möchte. Aber wie setzt man DDD in Go um, wenn die Sprache uns nicht mit Klassen und Vererbung verwöhnt? Lassen Sie es uns herausfinden.

\n\n

In diesem Artikel zeige ich Ihnen, wie Sie Domain-Driven Design in Go-Projekten ohne übertriebenen Fanatismus anwenden, mit funktionierenden Codebeispielen und einer klaren Struktur. Sie erfahren, wie Sie die Spreu vom Weizen trennen und Code schreiben, der nicht nur für die Maschine, sondern auch für den Business-Analysten verständlich ist.

\n\n

Was ist DDD und warum braucht es gerade ein Go-Entwickler?

\n\n

Domain-Driven Design geht nicht um Technologien. Es geht um Sprache. Darum, dass der Code die Sprache des Geschäfts spricht. Wenn Ihre Fachdomäne ein Online-Shop ist, sollte der Code Order, Product, Cart enthalten, nicht DataTransferObject und EntityManager.

\n\n

Go mit seiner Einfachheit und dem Fehlen von Magie ist ideal für DDD. Es gibt keine Proxy-Klassen und Reflektion, die das Wesentliche verbergen. In Go sind Sie gezwungen, ehrlich zu sein: Wenn Sie ein Aggregat haben, ist es eine explizite Struktur; wenn Sie ein Repository haben, ist es ein Interface.

\n\n

Die grundlegenden Bausteine von DDD, die wir betrachten werden:

\n
    \n
  • Entity (Entität) — ein Objekt mit einer eindeutigen Kennung (z. B. User).
  • \n
  • Value Object (Wertobjekt) — ein unveränderliches Objekt, das Eigenschaften beschreibt (z. B. Address).
  • \n
  • Aggregate (Aggregat) — ein Cluster von Entitäten, der Konsistenz garantiert (z. B. Order mit OrderItem).
  • \n
  • Repository (Repository) — eine Sammlung von Objekten, die Speicherdetails verbirgt.
  • \n
  • Domain Service (Domänendienst) — eine Operation, die nicht in eine einzelne Entität passt.
  • \n
\n\n

Lassen Sie uns das nun auf Go übertragen.

\n\n

Projektstruktur: Wie man sich in Ordnern nicht verirrt

\n\n

Die gute Nachricht: Go erzwingt keine bestimmte Struktur. Die schlechte Nachricht: Ohne Struktur werden Sie schnell untergehen. Für DDD in Go empfehle ich die folgende Code-Organisation:

\n\n
your-project/\n├── cmd/\n│   └── server/\n│       └── main.go\n├── internal/\n│   ├── domain/          # Reiner Kern: Entitäten, Regeln\n│   │   ├── order.go\n│   │   ├── product.go\n│   │   └── errors.go\n│   ├── application/     # Use Cases / Anwendungsdienste\n│   │   ├── order_service.go\n│   │   └── product_service.go\n│   └── infrastructure/  # Datenbanken, externe APIs, HTTP-Handler\n│       ├── postgres/\n│       │   └── order_repository.go\n│       └── http/\n│           └── handler.go\n└── go.mod
\n\n

Beachten Sie: domain importiert infrastructure nicht. Dies ist die goldene Regel von DDD – die Domäne weiß nichts über Datenbanken und HTTP. Sie lebt im Vakuum reiner Strukturen und Interfaces.

\n\n

Die Domäne schreiben: Order und Value Objects

\n\n

Beginnen wir mit dem Kern. Nehmen wir an, wir bauen ein Auftragsverwaltungssystem. So könnte die Entität Order aussehen:

\n\n
// internal/domain/order.go\npackage domain\n\nimport (\n    "errors"\n    "time"\n)\n\ntype OrderStatus string\n\nconst (\n    OrderStatusPending   OrderStatus = "pending"\n    OrderStatusConfirmed OrderStatus = "confirmed"\n    OrderStatusCancelled OrderStatus = "cancelled"\n)\n\ntype OrderID string\n\ntype Order struct {\n    ID         OrderID\n    CustomerID string\n    Items      []OrderItem\n    Status     OrderStatus\n    CreatedAt  time.Time\n    Upd                

Blogs

Buchempfehlungen