Communitygithub.com

Arquitetura Hexagonal

Projete, implemente e refatore sistemas Ports & Adapters com limites de domínio claros, inversão de dependência e orquestração de casos de uso testável em serviços TypeScript, Java, Kotlin e Go. Use ao introduzir ou refatorar para Ports and Adapters, ou quando a lógica de domínio tiver se tornado emaranhada com E/S.

O que é Arquitetura Hexagonal?

A Arquitetura Hexagonal (Ports and Adapters) é um padrão arquitetural que isola a lógica de negócios central de detalhes de infraestrutura, como frameworks, bancos de dados e APIs externas. Este skill fornece um guia completo para projetar, implementar e refatorar sistemas usando esse padrão, com suporte para TypeScript, Java, Kotlin e Go. Ele descreve como modelar limites de casos de uso, definir portas de saída, implementar casos de uso com orquestração pura, construir adaptadores nas bordas e centralizar a fiação em uma raiz de composição. O skill inclui exemplos de código, diagramas de arquitetura, um layout de módulo sugerido e um playbook de migração para refatorar sistemas existentes. É particularmente útil ao construir novos recursos que exigem alta capacidade de manutenção e testabilidade, refatorar código acoplado a frameworks ou dar suporte a múltiplas interfaces para o mesmo caso de uso.

Funciona com~Claude Code~Codex CLI~Cursor
npx skills add https://github.com/affaan-m/ECC/tree/main/skills/hexagonal-architecture

Perguntar na sua IA favorita

Abre um novo chat com esta habilidade de agente já pré-carregada.

Documentação

O que Arquitetura Hexagonal faz?

A arquitetura hexagonal (Ports and Adapters) mantém a lógica de negócios independente de frameworks, transporte e detalhes de persistência. O aplicativo central depende de portas abstratas, e os adaptadores implementam essas portas nas bordas.

Quando Usar

  • Construir novos recursos onde a capacidade de manutenção e testabilidade a longo prazo são importantes.
  • Refatorar código em camadas ou com muitos frameworks onde a lógica de domínio está misturada com preocupações de E/S.
  • Suportar múltiplas interfaces para o mesmo caso de uso (HTTP, CLI, workers de fila, cron jobs).
  • Substituir infraestrutura (banco de dados, APIs externas, barramento de mensagens) sem reescrever regras de negócios.

Use este skill quando a solicitação envolver limites, design centrado em domínio, refatoração de serviços fortemente acoplados ou desacoplamento da lógica de aplicação de bibliotecas específicas.

Conceitos Principais

  • Modelo de domínio: Regras de negócios e entidades/objetos de valor. Sem imports de framework.
  • Casos de uso (camada de aplicação): Orquestram o comportamento do domínio e as etapas do fluxo de trabalho.
  • Portas de entrada: Contratos que descrevem o que a aplicação pode fazer (interfaces de comandos/consultas/casos de uso).
  • Portas de saída: Contratos para dependências que a aplicação necessita (repositórios, gateways, publicadores de eventos, relógio, UUID, etc.).
  • Adaptadores: Implementações de infraestrutura e entrega das portas (controladores HTTP, repositórios de banco de dados, consumidores de fila, wrappers de SDK).
  • Raiz de composição: Única localização de fiação onde adaptadores concretos são vinculados a casos de uso.

As interfaces de porta de saída geralmente vivem na camada de aplicação (ou apenas no domínio quando a abstração é verdadeiramente de nível de domínio), enquanto os adaptadores de infraestrutura as implementam.

A direção da dependência é sempre para dentro:

  • Adaptadores -> aplicação/domínio
  • Aplicação -> interfaces de porta (contratos de entrada/saída)
  • Domínio -> abstrações apenas de domínio (sem dependências de framework ou infraestrutura)
  • Domínio -> nada externo

Como Funciona

Etapa 1: Modele um limite de caso de uso

Defina um único caso de uso com um DTO de entrada e saída claro. Mantenha detalhes de transporte (Express req, GraphQL context, wrappers de payload de job) fora deste limite.

Etapa 2: Defina as portas de saída primeiro

Identifique cada efeito colateral como uma porta:

  • persistência (UserRepositoryPort)
  • chamadas externas (BillingGatewayPort)
  • transversais (LoggerPort, ClockPort)

As portas devem modelar capacidades, não tecnologias.

Etapa 3: Implemente o caso de uso com orquestração pura

A classe/função do caso de uso recebe as portas via construtor/argumentos. Valida invariantes de nível de aplicação, coordena regras de domínio e retorna estruturas de dados simples.

Etapa 4: Construa adaptadores na borda

  • Adaptador de entrada: converte a entrada do protocolo para a entrada do caso de uso.
  • Adaptador de saída: mapeia os contratos da aplicação para APIs/ORM/query builders concretos.
  • O mapeamento permanece nos adaptadores, não dentro dos casos de uso.

Etapa 5: Conecte tudo em uma raiz de composição

Instancie adaptadores e, em seguida, injete-os nos casos de uso. Mantenha essa fiação centralizada para evitar comportamento oculto de localizador de serviços.

Etapa 6: Teste por limite

  • Teste unitário de casos de uso com portas falsas (fakes).
  • Teste de integração de adaptadores com dependências de infraestrutura reais.
  • Teste E2E de fluxos voltados ao usuário através de adaptadores de entrada.

Diagrama de Arquitetura

flowchart LR
  Client["Cliente (HTTP/CLI/Worker)"] --> InboundAdapter["Adaptador de Entrada"]
  InboundAdapter -->|"chama"| UseCase["Caso de Uso (Camada de Aplicação)"]
  UseCase -->|"usa"| OutboundPort["Porta de Saída (Interface)"]
  OutboundAdapter["Adaptador de Saída"] -->|"implementa"| OutboundPort
  OutboundAdapter --> ExternalSystem["BD/API/Fila"]
  UseCase --> DomainModel["Modelo de Domínio"]

Layout de Módulo Sugerido

Use organização por funcionalidade com limites explícitos:

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

Exemplo em TypeScript

Definições de Porta

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 }>;
}

Caso de Uso

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 retorna uma nova instância de Order; não sofre mutação no local.
    const authorizedOrder = order.markAuthorized(auth.authorizationId);
    await this.orderRepository.save(authorizedOrder);

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

Adaptador de Saída

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;
  }
}

Raiz de Composição

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);
};

Mapeamento Multi-Linguagem

Use as mesmas regras de limite em diferentes ecossistemas; apenas a sintaxe e o estilo de fiação mudam.

  • TypeScript/JavaScript
    • Portas: application/ports/* como interfaces/types.
    • Casos de uso: classes/funções com injeção por construtor/argumento.
    • Adaptadores: adapters/inbound/*, adapters/outbound/*.
    • Composição: módulo factory/container explícito (sem globais ocultas).
  • Java
    • Pacotes: domain, application.port.in, application.port.out, application.usecase, adapter.in, adapter.out.
    • Portas: interfaces em application.port.*.
    • Casos de uso: classes simples (Spring @Service é opcional, não obrigatório).
    • Composição: configuração Spring ou classe de fiação manual; mantenha a fiação fora das classes de domínio/caso de uso.
  • Kotlin
    • Módulos/pacotes espelham a divisão do Java (domain, application.port, application.usecase, adapter).
    • Portas: interfaces Kotlin.
    • Casos de uso: classes com injeção por construtor (Koin/Dagger/Spring/manual).
    • Composição: definições de módulo ou funções de composição dedicadas; evite padrões de localizador de serviços.
  • Go
    • Pacotes: internal/<feature>/domain, application, ports, adapters/inbound, adapters/outbound.
    • Portas: interfaces pequenas de propriedade do pacote da aplicação consumidora.
    • Casos de uso: structs com campos de interface mais construtores New... explícitos.
    • Composição: fie em cmd/<app>/main.go (ou pacote de fiação dedicado), mantenha construtores explícitos.

Anti-Padrões a Evitar

  • Entidades de domínio importando modelos ORM, tipos de framework web ou clientes SDK.
  • Casos de uso lendo diretamente de req, res ou metadados de fila.
  • Retornar linhas de banco de dados diretamente dos casos de uso sem mapeamento de domínio/aplicação.
  • Permitir que adaptadores chamem uns aos outros diretamente em vez de fluir através das portas do caso de uso.
  • Espalhar a fiação de dependências por muitos arquivos com singletons globais ocultos.

Playbook de Migração

  1. Escolha uma fatia vertical (um endpoint/job) com dor de mudança frequente.
  2. Extraia um limite de caso de uso com tipos de entrada/saída explícitos.
  3. Introduza portas de saída em torno de chamadas de infraestrutura existentes.
  4. Mova a lógica de orquestração de controladores/serviços para o caso de uso.
  5. Mantenha os adaptadores antigos, mas faça-os delegar ao novo caso de uso.
  6. Adicione testes em torno do novo limite (unitário + integração de adaptador).
  7. Repita fatia por fatia; evite reescritas completas.

Refatoração de Sistemas Existentes

  • Abordagem Strangler: mantenha os endpoints atuais, direcione um caso de uso por vez através de novas portas/adaptadores.
  • Sem reescritas big-bang: migre por fatia de funcionalidade e preserve o comportamento com testes de caracterização.
  • Facade primeiro: envolva serviços legados por trás de portas de saída antes de substituir os internos.
  • Congelamento da composição: centralize a fiação desde o início para que novas dependências não vazem para as camadas de domínio/caso de uso.
  • Regra de seleção de fatia: priorize fluxos de alta rotatividade e baixo raio de explosão primeiro.
  • Caminho de rollback: mantenha um toggle reversível ou chave de rota por fatia migrada até que o comportamento em produção seja verificado.

Orientação de Teste (Mesmos Limites Hexagonais)

  • Testes de domínio: teste entidades/objetos de valor como regras de negócios puras (sem mocks, sem configuração de framework).
  • Testes unitários de caso de uso: teste a orquestração com fakes/stubs para portas de saída; afirme resultados de negócios e interações de porta.
  • Testes de contrato de adaptador de saída: defina suítes de contrato compartilhadas no nível da porta e execute-as contra cada implementação de adaptador.
  • Testes de adaptador de entrada: verifique o mapeamento de protocolo (HTTP/CLI/payload de fila para entrada do caso de uso e saída/erro de volta para o protocolo).
  • Testes de integração de adaptador: execute contra infraestrutura real (BD/API/fila) para serialização, comportamento de esquema/consulta, retentativas e timeouts.
  • Testes ponta a ponta: cubra jornadas críticas do usuário através de adaptador de entrada -> caso de uso -> adaptador de saída.
  • Segurança de refatoração: adicione testes de caracterização antes da extração; mantenha-os até que o comportamento do novo limite esteja estável e equivalente.

Lista de Verificação de Melhores Práticas

  • As camadas de domínio e caso de uso importam apenas tipos internos e portas.
  • Toda dependência externa é representada por uma porta de saída.
  • A validação ocorre nos limites (adaptador de entrada + invariantes do caso de uso).
  • Use transformações imutáveis (retorne novos valores/entidades em vez de sofrer mutação em estado compartilhado).
  • Erros são traduzidos entre limites (erros de infraestrutura -> erros de aplicação/domínio).
  • A raiz de composição é explícita e fácil de auditar.
  • Casos de uso são testáveis com fakes simples em memória para portas.
  • A refatoração começa a partir de uma fatia vertical com testes de preservação de comportamento.
  • Especificidades de linguagem/framework permanecem nos adaptadores, nunca nas regras de domínio.

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/content-engine

Create platform-native content systems for X, LinkedIn, TikTok, YouTube, newsletters, and repurposed multi-platform campaigns. Use when the user wants social posts, threads, scripts, content calendars, or one source asset adapted cleanly across platforms.

affaan-m/ECC

Multi-platform content distribution across X, LinkedIn, Threads, and Bluesky. Adapts content per platform using content-engine patterns. Never posts identical content cross-platform. Use when the user wants to distribute content across social platforms.

affaan-m/fal-ai-media

Unified media generation via fal.ai MCP — image, video, and audio. Covers text-to-image (Nano Banana), text/image-to-video (Seedance, Kling, Veo 3), text-to-speech (CSM-1B), and video-to-audio (ThinkSound). Use when the user wants to generate images, videos, or audio with AI.

affaan-m/manim-video

日本語翻訳:このファイルは manim-video 用の日本語翻訳が必要です

affaan-m/remotion-video-creation

Remotion のベストプラクティス - React で動画を作成する。3D、アニメーション、音声、字幕、チャート、トランジションなどをカバーするドメイン固有の29のルール。

affaan-m/video-editing

AI-assisted video editing workflows for cutting, structuring, and augmenting real footage. Covers the full pipeline from raw capture through FFmpeg, Remotion, ElevenLabs, fal.ai, and final polish in Descript or CapCut. Use when the user wants to edit video, cut footage, create vlogs, or build video content.

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.

agent-self-evaluation

Use after completing any non-trivial task. The agent self-rates its output on 5 axes — accuracy, completeness, clarity, actionability, conciseness — with concrete evidence per criterion. Produces a structured 1-5 scorecard with specific improvement suggestions.

agent-sort

Build an evidence-backed ECC install plan for a specific repo by sorting skills, commands, rules, hooks, and extras into DAILY vs LIBRARY buckets using parallel repo-aware review passes. Use when ECC should be trimmed to what a project actually needs instead of loading the full bundle.

ai-first-engineering

Engineering operating model for teams where AI agents generate a large share of implementation output. Use when setting team process, review gates, or ownership rules for a codebase largely written by agents.

ai-regression-testing

Regression testing strategies for AI-assisted development. Sandbox-mode API testing without database dependencies, automated bug-check workflows, and patterns to catch AI blind spots where the same model writes and reviews code. Use when adding regression coverage to AI-assisted code, or when the same model both wrote and reviewed a change.

android-clean-architecture

Clean Architecture patterns for Android and Kotlin Multiplatform projects — module structure, dependency rules, UseCases, Repositories, and data layer patterns. Use when structuring modules, layers, or data flow in an Android or KMP project.

angular-developer

Generates Angular code and provides architectural guidance. Trigger when creating projects, components, or services, or for best practices on reactivity (signals, linkedSignal, resource), forms, dependency injection, routing, SSR, accessibility (ARIA), animations, styling (component styles, Tailwind CSS), testing, or CLI tooling.

Habilidades Relacionadas