Powrót do strony głównej

DDD i Clean Architecture w Go: pragmatyczne podejście

Artykuł wyjaśnia, jak pragmatycznie stosować DDD i Clean Architecture w Go, unikając nadmiernej struktury. Podano przykłady kodu, rekomendacje organizacji projektu i kluczowe zasady dla redukcji długu technicznego.

Go: DDD i Clean Architecture bez 200 plików
Advertisement 728x90

# DDD i Clean Architecture w Go: pragmatyczne podejście bez nadmiernego inżynieringu

Dług techniczny wynikający z problemów architektonicznych pochłania do 40% budżetu IT firm. W projektach Go często objawia się to nadmierną strukturą: 200 plików z pustymi interfejsami i warstwami, w których logika biznesowa gubi się w gąszczu. Przyczyny to błędne stosowanie DDD i Clean Architecture, zaczerpnięte z Java czy C#. Ale te wzorce, użyte w sposób pragmatyczny, rozwiązują problem nadmiernej złożoności. Pokazujemy, jak uniknąć nadmiernego inżynieringu i stworzyć system łatwy w utrzymaniu.

Dlaczego projekty w Go zamieniają się w labirynt plików?

Programiści przychodzący do Go z innych języków często przenoszą architektoniczne nawyki, które nie pasują do idiomatycznego Go. Zgodnie z Go Developer Survey 2024, głównym problemem jest utrzymanie jednolitych standardów kodu z powodu różnego poziomu doświadczenia i nieidiomatycznych wzorców. Na przykład tworzenie struktury z folderami domain, application, infrastructure na wzór Java, ale bez zrozumienia sedna.

Badanie Carnegie Mellon potwierdza: problemy architektoniczne to główne źródło długu technicznego. W Go wygląda to tak:

Google AdInline article slot
  • Folder dto z duplikującymi się strukturami.
  • mappers między warstwami, gdzie konwersja jest trywialna.
  • Interfejsy w domain, zależne od infrastruktury (np. database/sql).
  • Use case, który tylko wywołuje repozytorium bez logiki biznesowej.

Rezultat: 200 plików, ale ani jednego z opisem realnych reguł biznesowych. Clean Architecture i DDD stają się nie rozwiązaniem, lecz przyczyną problemu.

DDD: nie o foldery, lecz o język i granice

Domain-Driven Design to nie zestaw folderów, ale podejście do modelowania dziedziny problemowej. Kluczowe koncepcje:

Język wszechobecny (Ubiquitous Language)

Programiści i biznes muszą używać tych samych terminów wszędzie: w kodzie, dokumentacji i rozmowach. Jeśli menedżer mówi „potwierdzić zamówienie”, w kodzie powinna być metoda Confirm(), a nie SetStatus(). To eliminuje nieporozumienia i przyspiesza onboarding.

Google AdInline article slot

Ograniczony kontekst (Bounded Context)

Duży system składa się z kilku kontekstów. Na przykład „klient” w dziale sprzedaży (lead z lejkiem sprzedażowym) i w wsparciu (bilety) to różne modele. Bounded Context wyznacza jasne granice, w których model jest spójny. W mikrousługach jeden serwis zazwyczaj odpowiada jednemu kontekstowi.

Encja (Entity) i obiekt-wartość (Value Object)

  • Entity ma unikalny identyfikator i zachowuje tożsamość (np. Order). Zawiera reguły biznesowe.
  • Value Object jest niezmienny i definiowany przez atrybuty (np. Money). Dwa obiekty z identycznymi polami są równoważne.

Przykład Value Object w 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
}

Agregat (Aggregate)

Grupa obiektów traktowana jako całość. Ma korzeń (Aggregate Root), przez który odbywa się dostęp. Na przykład Order (korzeń) obejmuje OrderItem. Reguła: zmiany wprowadzane tylko przez korzeń, co gwarantuje spójność.

Google AdInline article slot

Clean Architecture: reguła zależności jako podstawa

Esencja Clean Architecture polega na tym, że logika biznesowa nie zależy od szczegółów (bazy danych, frameworki). Jedyna reguła: zależności wskazują do wewnątrz (Dependency Rule).

Jak to działa w Go

  • domain: zawiera model biznesowy (Entity, Value Object, interfejsy repozytoriów). Brak zewnętrznych importów poza stdlib.
  • application: use cases orkiestrowane domeną. Zależne tylko od domain.
  • infrastructure: implementacje adapterów (bazy danych, klienci HTTP). Zależne od domain przez interfejsy.
  • delivery: punkty wejścia (HTTP, gRPC). Zależne od application.

Jeśli domain importuje database/sql — architektura jest naruszona. Poprawny przykład: interfejs repozytorium zdefiniowany w domain, a implementacja w infrastructure/postgres.

Zalety połączenia DDD i Clean Architecture

| Aspekt | Efekt | Metryka poprawy |

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

| Testowalność | Izolacja domeny od infrastruktury | +40% coverage |

| Elastyczność | Wymiana adapterów w godziny | -90% czasu |

| Zrozumiałość | Jasne granice komponentów | -70% onboarding |

Rzeczywista struktura projektu: jak uniknąć 200 plików

Optymalna struktura dla serwisu Go:

internal/
  domain/
    order.go        # agregat Order
    order_repo.go   # interfejs repozytorium
  application/
    create_order.go # use case
  infrastructure/
    postgres/
      order_repo.go # implementacja
  delivery/
    http/
      order_handler.go

Kluczowe zasady:

  • Interfejsy repozytoriów — w domain, nie w infrastructure.
  • Brak folderów dto i mappers bez potrzeby: używaj tych samych struktur w warstwach, o ile nie narusza to granic.
  • Logika biznesowa — tylko w domain. Na przykład metoda Cancel() dla Order sprawdza status:
func (o *Order) Cancel() error {
    if o.status == StatusShipped {
        return errors.New("cannot cancel shipped order")
    }
    o.status = StatusCancelled
    return nil
}

Co najważniejsze

  • Ubiquitous Language — podstawa DDD. Jeśli terminy w kodzie nie zgadzają się z biznesem, model będzie błędny.
  • Dependency Rule — sedno Clean Architecture. Naruszenie prowadzi do spaghetti-kodu.
  • Pragmatyzm — nie twórz warstw bez potrzeby. W Go często można obejść się bez dto, jeśli struktury pasują.
  • Agregaty — granica transakcji. Zmiany tylko przez korzeń dla spójności.
  • Testowanie — domena testowana bez infrastruktury, co przyspiesza rozwój.

— Editorial Team

Advertisement 728x90

Czytaj dalej