Einleitung: Warum Ihr Go-Projekt nach DDD verlangt?
\n\nHaben 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.
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\nIn 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\nWas ist DDD und warum braucht es gerade ein Go-Entwickler?
\n\nDomain-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.
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\nDie 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.
OrdermitOrderItem). \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
Lassen Sie uns das nun auf Go übertragen.
\n\nProjektstruktur: Wie man sich in Ordnern nicht verirrt
\n\nDie 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\nyour-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\nBeachten 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.
Die Domäne schreiben: Order und Value Objects
\n\nBeginnen wir mit dem Kern. Nehmen wir an, wir bauen ein Auftragsverwaltungssystem. So könnte die Entität Order aussehen:
// 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