Communitygithub.com

hexagonal-architecture

Entwerfen, Implementieren und Refactoren von Ports & Adapters-Systemen mit klaren Domänengrenzen, Abhängigkeitsinversion und testbarer Use-Case-Orchestrierung in TypeScript, Java, Kotlin und Go. Verwenden Sie diese Fähigkeit bei der Einführung oder dem Refactoring hin zu Ports und Adaptern oder wenn die Domänenlogik mit I/O verflochten ist.

Was ist hexagonal-architecture?

Diese Fähigkeit unterstützt Sie bei der Konzeption und Umsetzung der hexagonalen Architektur (Ports and Adapters), um Geschäftslogik sauber von Frameworks, Transportprotokollen und Persistenzmechanismen zu trennen. Sie ist ideal für die Entwicklung neuer Features mit Fokus auf langfristige Wartbarkeit und Testbarkeit, sowie für das Refactoring bestehender, framework-lastiger Systeme. Die Fähigkeit deckt die Kernkonzepte ab: Domänenmodell, Anwendungsfälle (Use Cases), eingehende und ausgehende Ports als abstrakte Verträge und konkrete Adapter für die technische Umsetzung. Sie wird durch detaillierte Schritt-für-Schritt-Anleitungen, mehrsprachige Codebeispiele (TypeScript, Java, Kotlin, Go), Anti-Patterns, einen Migrations-Playbook und umfassende Teststrategien ergänzt. Die Implementierung folgt dem Prinzip der Abhängigkeitsinversion: Adapter zeigen nach innen auf die Anwendungs- und Domänenschicht, die Domäne bleibt vollständig frei von Framework- und Infrastrukturabhängigkeiten.

Funktioniert mit~Claude Code~Codex CLI~Cursor
npx skills add https://github.com/affaan-m/everything-claude-code/tree/main/skills/hexagonal-architecture

In Ihrer bevorzugten KI fragen

Öffnet einen neuen Chat, in dem dieser Agent-Skill bereits geladen ist.

Dokumentation

Hexagonale Architektur

Die hexagonale Architektur (Ports and Adapters) hält die Geschäftslogik unabhängig von Frameworks, Transport- und Persistenzdetails. Die Kernanwendung hängt von abstrakten Ports ab, und Adapter implementieren diese Ports an den Rändern.

Verwendungszweck

  • Entwicklung neuer Funktionen, bei denen langfristige Wartbarkeit und Testbarkeit wichtig sind.
  • Refactoring von geschichteten oder framework-lastigen Codebasen, in denen Domänenlogik mit I/O-Belangen vermischt ist.
  • Unterstützung mehrerer Schnittstellen für denselben Anwendungsfall (HTTP, CLI, Queue-Worker, Cron-Jobs).
  • Austausch von Infrastruktur (Datenbank, externe APIs, Message Bus), ohne Geschäftsregeln neu schreiben zu müssen.

Verwenden Sie diese Fähigkeit, wenn es um Grenzen, domainzentriertes Design, Refactoring eng gekoppelter Dienste oder die Entkopplung der Anwendungslogik von bestimmten Bibliotheken geht.

Kernkonzepte

  • Domänenmodell: Geschäftsregeln und Entitäten/Wertobjekte. Keine Framework-Imports.
  • Anwendungsfälle (Anwendungsschicht): Orchestrieren von Domänenverhalten und Workflow-Schritten.
  • Eingehende Ports: Verträge, die beschreiben, was die Anwendung tun kann (Befehle/Anfragen/Use-Case-Schnittstellen).
  • Ausgehende Ports: Verträge für Abhängigkeiten, die die Anwendung benötigt (Repositories, Gateways, Event-Publisher, Uhr, UUID usw.).
  • Adapter: Infrastruktur- und Bereitstellungsimplementierungen von Ports (HTTP-Controller, DB-Repositories, Queue-Consumer, SDK-Wrapper).
  • Composition Root: Ein einziger Ort, an dem konkrete Adapter an Anwendungsfälle gebunden werden.

Ausgehende Port-Schnittstellen liegen normalerweise in der Anwendungsschicht (oder nur in der Domäne, wenn die Abstraktion wirklich domänenspezifisch ist), während Infrastrukturadapter sie implementieren.

Die Abhängigkeitsrichtung ist immer nach innen gerichtet:

  • Adapter -> Anwendung/Domäne
  • Anwendung -> Port-Schnittstellen (eingehende/ausgehende Verträge)
  • Domäne -> domäneninterne Abstraktionen (keine Framework- oder Infrastrukturabhängigkeiten)
  • Domäne -> nichts Externes

Funktionsweise

Schritt 1: Modellieren einer Use-Case-Grenze

Definieren Sie einen einzelnen Anwendungsfall mit einem klaren Eingabe- und Ausgabe-DTO. Halten Sie Transportdetails (Express req, GraphQL context, Job-Payload-Wrapper) außerhalb dieser Grenze.

Schritt 2: Ausgehende Ports zuerst definieren

Identifizieren Sie jeden Seiteneffekt als einen Port:

  • Persistenz (UserRepositoryPort)
  • Externe Aufrufe (BillingGatewayPort)
  • Querschnittliche Aspekte (LoggerPort, ClockPort)

Ports sollten Fähigkeiten modellieren, nicht Technologien.

Schritt 3: Anwendungsfall mit reiner Orchestrierung implementieren

Die Use-Case-Klasse/-Funktion erhält Ports über Konstruktor/Argumente. Sie validiert anwendungsspezifische Invarianten, koordiniert Domänenregeln und gibt einfache Datenstrukturen zurück.

Schritt 4: Adapter an den Rändern bauen

  • Eingehender Adapter wandelt Protokolleingabe in Use-Case-Eingabe um.
  • Ausgehender Adapter bildet Anwendungsverträge auf konkrete APIs/ORM/Query-Builder ab.
  • Das Mapping bleibt in Adaptern, nicht innerhalb von Anwendungsfällen.

Schritt 5: Alles in einem Composition Root verdrahten

Instanziieren Sie Adapter und injizieren Sie sie dann in Anwendungsfälle. Halten Sie diese Verdrahtung zentralisiert, um verstecktes Service-Locator-Verhalten zu vermeiden.

Schritt 6: Pro Grenze testen

  • Komponententests für Anwendungsfälle mit gefälschten Ports.
  • Integrationstests für Adapter mit echten Infrastrukturabhängigkeiten.
  • End-to-End-Tests für benutzerseitige Abläufe durch eingehende Adapter.

Architekturdiagramm

flowchart LR
  Client["Client (HTTP/CLI/Worker)"] --> InboundAdapter["Eingehender Adapter"]
  InboundAdapter -->|"ruft auf"| UseCase["Anwendungsfall (Anwendungsschicht)"]
  UseCase -->|"verwendet"| OutboundPort["Ausgehender Port (Schnittstelle)"]
  OutboundAdapter["Ausgehender Adapter"] -->|"implementiert"| OutboundPort
  OutboundAdapter --> ExternalSystem["DB/API/Queue"]
  UseCase --> DomainModel["Domänenmodell"]

Vorgeschlagene Modulstruktur

Verwenden Sie feature-orientierte Organisation mit expliziten Grenzen:

src/
  features/
    orders/
      domain/
        Order.ts
        OrderPolicy.ts
      application/
        ports/
          inbound/
            CreateOrder.ts
          outbound/
            OrderRepositoryPort.ts
            PaymentGatewayPort.ts
        use-cases/
          CreateOrderUseCase.ts
      adapters/
        inbound/
          http/
            createOrderRoute.ts
        outbound/
          postgres/
            PostgresOrderRepository.ts
          stripe/
            StripePaymentGateway.ts
      composition/
        ordersContainer.ts

TypeScript-Beispiel

Port-Definitionen

export interface OrderRepositoryPort {
  save(order: Order): Promise<void>;
  findById(orderId: string): Promise<Order | null>;
}

export interface PaymentGatewayPort {
  authorize(input: { orderId: string; amountCents: number }): Promise<{ authorizationId: string }>;
}

Anwendungsfall

type CreateOrderInput = {
  orderId: string;
  amountCents: number;
};

type CreateOrderOutput = {
  orderId: string;
  authorizationId: string;
};

export class CreateOrderUseCase {
  constructor(
    private readonly orderRepository: OrderRepositoryPort,
    private readonly paymentGateway: PaymentGatewayPort
  ) {}

  async execute(input: CreateOrderInput): Promise<CreateOrderOutput> {
    const order = Order.create({ id: input.orderId, amountCents: input.amountCents });

    const auth = await this.paymentGateway.authorize({
      orderId: order.id,
      amountCents: order.amountCents,
    });

    // markAuthorized gibt eine neue Order-Instanz zurück; sie mutiert nicht an Ort und Stelle.
    const authorizedOrder = order.markAuthorized(auth.authorizationId);
    await this.orderRepository.save(authorizedOrder);

    return {
      orderId: order.id,
      authorizationId: auth.authorizationId,
    };
  }
}

Ausgehender Adapter

export class PostgresOrderRepository implements OrderRepositoryPort {
  constructor(private readonly db: SqlClient) {}

  async save(order: Order): Promise<void> {
    await this.db.query(
      "insert into orders (id, amount_cents, status, authorization_id) values ($1, $2, $3, $4)",
      [order.id, order.amountCents, order.status, order.authorizationId]
    );
  }

  async findById(orderId: string): Promise<Order | null> {
    const row = await this.db.oneOrNone("select * from orders where id = $1", [orderId]);
    return row ? Order.rehydrate(row) : null;
  }
}

Composition Root

export const buildCreateOrderUseCase = (deps: { db: SqlClient; stripe: StripeClient }) => {
  const orderRepository = new PostgresOrderRepository(deps.db);
  const paymentGateway = new StripePaymentGateway(deps.stripe);

  return new CreateOrderUseCase(orderRepository, paymentGateway);
};

Sprachübergreifendes Mapping

Verwenden Sie dieselben Grenzregeln in allen Ökosystemen; nur Syntax und Verdrahtungsstil ändern sich.

  • TypeScript/JavaScript
    • Ports: application/ports/* als Schnittstellen/Typen.
    • Anwendungsfälle: Klassen/Funktionen mit Konstruktor-/Argument-Injektion.
    • Adapter: adapters/inbound/*, adapters/outbound/*.
    • Komposition: explizite Factory-/Container-Module (keine versteckten globalen Variablen).
  • Java
    • Pakete: domain, application.port.in, application.port.out, application.usecase, adapter.in, adapter.out.
    • Ports: Schnittstellen in application.port.*.
    • Anwendungsfälle: einfache Klassen (Spring @Service ist optional, nicht erforderlich).
    • Komposition: Spring-Konfiguration oder manuelle Verdrahtungsklasse; Verdrahtung außerhalb von Domänen-/Use-Case-Klassen halten.
  • Kotlin
    • Module/Pakete spiegeln die Java-Aufteilung wider (domain, application.port, application.usecase, adapter).
    • Ports: Kotlin-Schnittstellen.
    • Anwendungsfälle: Klassen mit Konstruktor-Injektion (Koin/Dagger/Spring/manuell).
    • Komposition: Moduldefinitionen oder dedizierte Kompositionsfunktionen; Service-Locator-Muster vermeiden.
  • Go
    • Pakete: internal/<feature>/domain, application, ports, adapters/inbound, adapters/outbound.
    • Ports: kleine Schnittstellen, die dem konsumierenden Anwendungspaket gehören.
    • Anwendungsfälle: Structs mit Schnittstellenfeldern und expliziten New...-Konstruktoren.
    • Komposition: Verdrahtung in cmd/<app>/main.go (oder dediziertes Verdrahtungspaket), Konstruktoren explizit halten.

Anti-Patterns, die vermieden werden sollten

  • Domänenentitäten importieren ORM-Modelle, Web-Framework-Typen oder SDK-Clients.
  • Anwendungsfälle lesen direkt von req, res oder Queue-Metadaten.
  • Rückgabe von Datenbankzeilen direkt aus Anwendungsfällen ohne Domänen-/Anwendungsmapping.
  • Adapter rufen einander direkt auf, anstatt über Use-Case-Ports zu fließen.
  • Abhängigkeitsverdrahtung über viele Dateien mit versteckten globalen Singletons verteilen.

Migrations-Playbook

  1. Wählen Sie einen vertikalen Ausschnitt (einzelner Endpunkt/Job) mit häufigem Änderungsaufwand.
  2. Extrahieren Sie eine Use-Case-Grenze mit expliziten Eingabe-/Ausgabetypen.
  3. Führen Sie ausgehende Ports um vorhandene Infrastrukturaufrufe ein.
  4. Verschieben Sie Orchestrierungslogik von Controllern/Diensten in den Anwendungsfall.
  5. Behalten Sie alte Adapter, lassen Sie sie jedoch an den neuen Anwendungsfall delegieren.
  6. Fügen Sie Tests um die neue Grenze hinzu (Komponente + Adapterintegration).
  7. Wiederholen Sie den Vorgang ausschnittsweise; vermeiden Sie vollständige Neuschreibungen.

Refactoring bestehender Systeme

  • Strangler-Ansatz: Behalten Sie aktuelle Endpunkte bei, leiten Sie einen Anwendungsfall nach dem anderen durch neue Ports/Adapter.
  • Keine Big-Bang-Neuschreibungen: Migrieren Sie pro Feature-Ausschnitt und bewahren Sie das Verhalten mit Charakterisierungstests.
  • Fassade zuerst: Wickeln Sie Legacy-Dienste hinter ausgehenden Ports ein, bevor Sie Interna ersetzen.
  • Kompositionsstopp: Zentralisieren Sie die Verdrahtung frühzeitig, damit neue Abhängigkeiten nicht in Domänen-/Use-Case-Schichten eindringen.
  • Auswahlregel für Ausschnitte: Priorisieren Sie zuerst Abläufe mit hoher Änderungsrate und geringem Schadensradius.
  • Rückfallpfad: Halten Sie einen umkehrbaren Schalter oder Routenwechsel pro migriertem Ausschnitt bereit, bis das Produktionsverhalten verifiziert ist.

Testrichtlinien (gleiche hexagonale Grenzen)

  • Domänentests: Testen Sie Entitäten/Wertobjekte als reine Geschäftsregeln (keine Mocks, kein Framework-Setup).
  • Use-Case-Komponententests: Testen Sie die Orchestrierung mit Fakes/Stubs für ausgehende Ports; überprüfen Sie Geschäftsergebnisse und Port-Interaktionen.
  • Ausgehende Adapter-Vertragstests: Definieren Sie gemeinsame Vertragssuiten auf Port-Ebene und führen Sie diese gegen jede Adapterimplementierung aus.
  • Eingehende Adaptertests: Überprüfen Sie das Protokoll-Mapping (HTTP/CLI/Queue-Payload zu Use-Case-Eingabe und Ausgabe-/Fehler-Mapping zurück zum Protokoll).
  • Adapter-Integrationstests: Testen Sie gegen reale Infrastruktur (DB/API/Queue) für Serialisierung, Schema-/Query-Verhalten, Wiederholungen und Timeouts.
  • End-to-End-Tests: Decken Sie kritische Benutzerreisen durch eingehenden Adapter -> Anwendungsfall -> ausgehenden Adapter ab.
  • Refactoring-Sicherheit: Fügen Sie Charakterisierungstests vor der Extraktion hinzu; behalten Sie diese bei, bis das neue Grenzverhalten stabil und gleichwertig ist.

Best Practices-Checkliste

  • Domänen- und Anwendungsschichten importieren nur interne Typen und Ports.
  • Jede externe Abhängigkeit wird durch einen ausgehenden Port repräsentiert.
  • Validierung erfolgt an den Grenzen (eingehender Adapter + Use-Case-Invarianten).
  • Verwenden Sie unveränderliche Transformationen (geben Sie neue Werte/Entitäten zurück, anstatt gemeinsamen Zustand zu mutieren).
  • Fehler werden über Grenzen hinweg übersetzt (Infrastrukturfehler -> Anwendungs-/Domänenfehler).
  • Der Composition Root ist explizit und leicht zu überprüfen.
  • Anwendungsfälle sind mit einfachen In-Memory-Fakes für Ports testbar.
  • Refactoring beginnt mit einem vertikalen Ausschnitt und verhaltenserhaltenden Tests.
  • Sprach-/Framework-Spezifika bleiben in Adaptern, niemals in Domänenregeln.

Individual skills in this repo

This repo contains 20 individual skills — each has its own dedicated page.

accessibility

Design, implement, and audit inclusive digital products using WCAG 2.2 Level AA. Use when building or auditing UI that must meet WCAG 2.2 Level AA, or when reviewing a change for keyboard, contrast, or screen-reader support.

affaan-m/claude-api

Anthropic Claude API patterns for Python and TypeScript. Covers Messages API, streaming, tool use, vision, extended thinking, batches, prompt caching, and Claude Agent SDK. Use when building applications with the Claude API or Anthropic SDKs.

affaan-m/everything-claude-code

End-to-end marketing campaign planning and execution. Covers audience research, positioning, campaign angle definition, landing page copy, email sequences, social posts, ad copy, short-form video scripts, and content calendars. Use as the orchestration layer for multi-channel product launches. Use when planning or executing a multi-channel product launch, or producing landing page, email, social, or ad copy.

affaan-m/everything-claude-code

Development conventions and patterns for everything-claude-code. JavaScript project with conventional commits.

affaan-m/everything-claude-code-conventions

Development conventions and patterns for everything-claude-code. JavaScript project with conventional commits.

affaan-m/frontend-design

Create distinctive, production-grade frontend interfaces with high design quality. Use when the user asks to build web components, pages, or applications and the visual direction matters as much as the code quality.

affaan-m/gget

gget CLI and Python workflow for quick genomic database queries, sequence lookup, BLAST-style searches, enrichment checks, and reproducible bioinformatics evidence logs.

affaan-m/literature-review

Systematic literature-review workflow for academic, biomedical, technical, and scientific topics, including search planning, source screening, synthesis, citation checks, and evidence logging.

affaan-m/motion-ui

Production-ready UI motion system for React/Next.js. Use when implementing animations, transitions, or motion patterns.

affaan-m/project-guidelines-example

Example project-specific skill template based on a real production application.

affaan-m/pubmed-database

Direct PubMed and NCBI E-utilities search workflows for biomedical literature, MeSH queries, PMID lookup, citation retrieval, and API-backed literature monitoring.

affaan-m/scholar-evaluation

Structured scholarly-work evaluation for papers, proposals, literature reviews, methods sections, evidence quality, citation support, and research-writing feedback.

affaan-m/uspto-database

USPTO patent and trademark data workflow for official record lookup, PatentSearch queries, TSDR checks, assignment data, and reproducible IP research logs.

agent-architecture-audit

Full-stack diagnostic for agent and LLM applications. Audits the 12-layer agent stack for wrapper regression, memory pollution, tool discipline failures, hidden repair loops, and rendering corruption. Produces severity-ranked findings with code-first fixes. Essential for developers building agent applications, autonomous loops, or any LLM-powered feature. Use when an agent or LLM feature misbehaves and the failing layer is unknown, or before shipping an agent stack.

agent-eval

Head-to-head comparison of coding agents (Claude Code, Aider, Codex, etc.) on custom tasks with pass rate, cost, time, and consistency metrics. Use when choosing between coding agents, or when a change to an agent setup needs measured pass rate, cost, and time rather than an impression.

agent-harness-construction

Design and optimize AI agent action spaces, tool definitions, and observation formatting for higher completion rates. Use when defining or revising an agent

agentic-engineering

Operate as an agentic engineer using eval-first execution, decomposition, and cost-aware model routing. Use when planning or executing engineering work that agents will carry out end to end.

agentic-os

Build persistent multi-agent operating systems on Claude Code. Covers kernel architecture, specialist agents, slash commands, file-based memory, scheduled automation, and state management without external databases. Use when building a persistent multi-agent system on Claude Code with its own memory, commands, and scheduling.

agent-introspection-debugging

Structured self-debugging workflow for AI agent failures using capture, diagnosis, contained recovery, and introspection reports. Use when an agent run fails and you need a reproducible diagnosis instead of a retry.

agent-payment-x402

Add x402 payment execution to AI agents with per-task budgets, spending controls, and non-custodial wallets. Supports Base through agentwallet-sdk and X Layer through OKX Payments / OKX Agent Payments Protocol. Use when an agent must pay for something itself and needs per-task budgets, spending controls, and a non-custodial wallet.

Verwandte Skills