Zurück zur Startseite

DDD und Clean Architecture in Go: pragmatischer Ansatz

Der Artikel erklärt, wie man DDD und Clean Architecture pragmatisch in Go anwendet und übermäßige Struktur vermeidet. Code-Beispiele, Empfehlungen zur Projektorganisation und wichtige Regeln zur Reduzierung technischer Schulden werden bereitgestellt.

Go: DDD und Clean Architecture ohne 200 Dateien
Advertisement 728x90

DDD und Clean Architecture in Go: Ein pragmatischer Ansatz ohne Überengineering

Technische Schulden durch architekturelle Probleme verzehren bis zu 40 % der IT-Budgets von Unternehmen. In Go-Projekten äußert sich das oft in überkomplizierten Strukturen: 200 Dateien voller leerer Interfaces und Schichten, in denen die Geschäftslogik verschüttet geht. Die Ursache ist die Fehlanwendung von DDD- und Clean-Architecture-Mustern, die aus Java oder C# übernommen wurden. Pragmatisch eingesetzt, lösen diese Muster jedoch Komplexitätsprobleme. So vermeiden Sie Überengineering und bauen ein wartbares System auf.

Warum werden Go-Projekte zu einem Labyrinth aus Dateien?

Entwickler, die aus anderen Sprachen zu Go kommen, schleppen oft architektonische Gewohnheiten mit, die nicht zum idiomatischen Go passen. Laut Go Developer Survey 2024 ist das größte Problem die Aufrechterhaltung einheitlicher Code-Standards aufgrund unterschiedlicher Erfahrungsstufen und nicht-idiomatischer Muster. Zum Beispiel Folderstrukturen wie domain, application und infrastructure, die Java nachahmen, ohne die Kernprinzipien zu verstehen.

Forschung der Carnegie Mellon bestätigt: Architekturprobleme sind die Hauptquelle technischer Schulden. In Go sieht das so aus:

Google AdInline article slot
  • Ein dto-Ordner mit duplizierten Structs.
  • mappers zwischen Schichten für triviale Umwandlungen.
  • Interfaces in domain, die von Infrastructure abhängen (z. B. database/sql).
  • Use Cases, die nur Repositories aufrufen, ohne Geschäftslogik.

Das Ergebnis: 200 Dateien, aber keine beschreibt echte Geschäftsregeln. Clean Architecture und DDD werden nicht zur Lösung, sondern zum Problem selbst.

DDD: Nicht um Ordner, sondern um Sprache und Grenzen

Domain-Driven Design geht nicht um eine Reihe von Ordnern – es ist ein Ansatz zur Modellierung des Problembereichs. Wichtige Konzepte:

Ubiquitous Language

Entwickler und Geschäftspartner müssen überall dieselben Begriffe verwenden: im Code, in der Dokumentation und in Gesprächen. Wenn ein Manager "Bestellung bestätigen" sagt, sollte der Code eine Confirm()-Methode haben, nicht SetStatus(). Das beseitigt Missverständnisse und beschleunigt das Einarbeiten.

Google AdInline article slot

Bounded Context

Ein großes System besteht aus mehreren Kontexten. Zum Beispiel ist "Kunde" im Vertrieb (ein Lead mit Trichter) und im Support (Tickets) unterschiedlich modelliert. Bounded Context definiert klare Grenzen, in denen das Modell konsistent bleibt. In Microservices entspricht typischerweise ein Service einem Kontext.

Entity und Value Object

  • Entity hat eine eindeutige ID und behält ihre Identität (z. B. Order). Sie enthält Geschäftsregeln.
  • Value Object ist unveränderlich und durch seine Attribute definiert (z. B. Money). Zwei Objekte mit identischen Feldern sind äquivalent.

Beispiel Value Object in Go:

type Money struct {
    Amount   int64
    Currency string
}

func (m Money) Add(other Money) (Money, error) {
    if m.Currency != other.Currency {
        return Money{}, errors.New("currency mismatch")
    }
    return Money{Amount: m.Amount + other.Amount, Currency: m.Currency}, nil
}

Aggregate

Eine Gruppe von Objekten, die als Einheit behandelt wird. Es hat eine Wurzel (Aggregate Root), über die aller Zugriff erfolgt. Zum Beispiel umfasst Order (Wurzel) OrderItem. Regel: Änderungen nur über die Wurzel, um Konsistenz zu gewährleisten.

Google AdInline article slot

Clean Architecture: Die Dependency Rule als Grundlage

Das Wesen von Clean Architecture ist, dass Geschäftslogik nicht von Implementierungsdetails (Datenbanken, Frameworks) abhängen sollte. Die eine Regel: Abhängigkeiten zeigen nach innen (Dependency Rule).

So funktioniert es in Go

  • domain: Enthält das Geschäftsmodell (Entity, Value Object, Repository-Interfaces). Keine externen Imports außer stdlib.
  • application: Use Cases, die die Domäne orchestrieren. Hängt nur von domain ab.
  • infrastructure: Adapter-Implementierungen (Datenbanken, HTTP-Clients). Hängt über Interfaces von domain ab.
  • delivery: Einstiegspunkte (HTTP, gRPC). Hängt von application ab.

Wenn domain database/sql importiert, ist die Architektur kaputt. Korrektes Beispiel: Repository-Interface in domain deklariert, Implementierung in infrastructure/postgres.

Vorteile der Kombination aus DDD und Clean Architecture

| Aspekt | Effekt | Verbesserungsmetrik |

|---------------------|-------------------------------------|---------------------|

| Testbarkeit | Isolierung der Domäne von Infrastructure | +40% Abdeckung |

| Flexibilität | Adapter in Stunden austauschbar | -90% Zeit |

| Verständlichkeit | Klare Komponentengrenzen | -70% Einarbeitung |

Reale Projektstruktur: 200 Dateien vermeiden

Optimale Struktur für einen Go-Service:

internal/
  domain/
    order.go        # Aggregate Order
    order_repo.go   # Repository interface
  application/
    create_order.go # Use case
  infrastructure/
    postgres/
      order_repo.go # Implementation
  delivery/
    http/
      order_handler.go

Wichtige Prinzipien:

  • Repository-Interfaces in domain, nicht infrastructure.
  • Keine dto- oder mappers-Ordner, es sei denn, wirklich nötig: Wiederverwenden Sie dieselben Structs über Schichten hinweg, wenn Grenzen nicht verletzt werden.
  • Geschäftslogik nur in domain. Zum Beispiel prüft die Cancel()-Methode für Order den Status:
func (o *Order) Cancel() error {
    if o.status == StatusShipped {
        return errors.New("cannot cancel shipped order")
    }
    o.status = StatusCancelled
    return nil
}

Wichtige Erkenntnisse

  • Ubiquitous Language ist die Grundlage von DDD. Wenn Code-Begriffe nicht zur Geschäftssprache passen, ist das Modell falsch.
  • Dependency Rule ist der Kern von Clean Architecture. Verstöße führen zu Spaghetti-Code.
  • Pragmatismus – schaffen Sie keine Schichten ohne Bedarf. In Go können Sie oft dto überspringen, wenn Structs passen.
  • Aggregates definieren Transaktionsgrenzen. Ändern Sie nur über die Wurzel für Konsistenz.
  • Testing – Domänen-Tests laufen ohne Infrastructure und beschleunigen die Entwicklung.

— Editorial Team

Advertisement 728x90

Weiterlesen