Zurück zur Übersicht

Wissen, 3. Januar 2024

Hexagonale Packages

Viele Entwickler:innen fragen sich, was die geeignete Package-Struktur für hexagonal entworfene Systeme ist. Dieser Beitrag macht einen Vorschlag.

Ich habe in meinem Vorgängerbeitrag bereits von "Drinnen" und "Draußen"-Architekturen gesprochen. Anknüpfend an diese Sichtweise schlage ich die folgende Paketstruktur für Hexagonale Architekturen vor:

projectroot
	application
		domain
		usecases		
	ports
		in
		out
	context
		sql
		http
		messaging

Die Struktur spiegelt die drei zentralen Hexagone der Anwendung wieder. Im Kern findet sich das "application"-Hexagon. Dieses enthält das Core-Domain-Modell, sowie die Usecases der Anwendung. Das "ports"-Hexagon enthält die Interfaces für ein- und ausgehende Verbindungen. Interfaces für eingehende Verbindungen liegen im "ports/in"-Package und werden von Usecase-Typen implementiert. Beispiel: Das Interface BicyleConfigurations

package in
  
type BicyleConfigurations interface {  
    GetAll() ([]domain.Configuration, error)  
}

wird vom Usecase GetBicycleConfigurations implementiert:

package usecases  
  
type GetBicycleConfigurations struct{  
}  
  
func (g GetBicycleConfigurations) GetAll() ([]domain.Configuration, error) {  
   ...
}

Interfaces für die von der Anwendung genutzten Services liegen im Package "ports/out" und werden von Typen im "context"-Hexagon implementiert. Beispiel: Das Interface BikeRepository

package out  
  
type BikeRepository interface {  
    GetAll() ([]domain.Configuration, error)  
}

wird vom Adapter PostgresAdapter implementiert, der im context-Hexagon liegt:

package context  
  
type PostgresAdapter struct {  
    dbpool *pgxpool.Pool  
}  
  
func (a PostgresAdapter) GetAll() ([]domain.Configuration, error) {  
    rows, _ := a.dbpool.Query(context.Background(), "select * from configurations")  
    defer rows.Close()  
    var configurations []domain.Configuration  
    for rows.Next() {  
       // ...  
    }  
    return configurations, nil  
}  

Die vorgeschlagene Package-Struktur hat den Vorteil, dass die drei Toplevel-Hexagone unmittelbar erkennen lassen, um welchen Teil der Anwendung es sich jeweils handelt. Neue Entwickler:innen finden sich schnell zurecht, Diskussionen darüber, wo etwas implementiert wird, werden von vornherein vermieden. Der Usecase-zentrierte Ansatz forciert außerdem eine Domänen-orientierte Sichtweise, so dass sich Anwendungsfälle direkt im Code gespiegelt finden.

Links

Autor:

Ralf Wirdemann
Software-Entwickler
  • Schwerpunkt Go und Java

  • Software-Architektur und -Modernisierung

  • Autor und Vortragender

Auch spannend