Skip to main content

hexagonal

Hexagonal Architecture (Ports and Adapters), Onion Architecture, and their relationship to Clean Architecture -- enabling technology-independent domain logic with high testability. USE FOR: hexagonal architecture, ports and adapters, onion architecture, driving/driven adapters, technology-independent domain design, adapter-based testability DO NOT USE FOR: clean architecture layers specifically (use dev/craftsmanship/clean-architecture), microservice boundaries (use microservices), domain model design (use domain-driven-design)

Ir para a instalação

Informações da origem

Repositório
Tyler-R-Kendrick/agent-skills
Última atividade na origem
11 de fevereiro de 2026 às 05:14
Idioma detectado do SKILL.md
inglês
Estrelas
11
Forks
4

Opções de instalação

Por padrão, está selecionado o prompt que primeiro revisa a origem. Você pode mudar para um comando direto ou baixar uma cópia local.

Revise os arquivos de origem

Leia o SKILL.md e os arquivos complementares exibidos pelo SkillsMP antes de decidir se vai instalar.

Explorador de arquivos
14 arquivos

Exibindo SKILL.md

SKILL.md
Instruções da origem · Visualização somente leitura
name
hexagonal
description
Hexagonal Architecture (Ports and Adapters), Onion Architecture, and their relationship to Clean Architecture -- enabling technology-independent domain logic with high testability. USE FOR: hexagonal architecture, ports and adapters, onion architecture, driving/driven adapters, technology-independent domain design, adapter-based testability DO NOT USE FOR: clean architecture layers specifically (use dev/craftsmanship/clean-architecture), microservice boundaries (use microservices), domain model design (use domain-driven-design)
license
MIT
metadata
{"displayName":"Hexagonal Architecture","author":"Tyler-R-Kendrick"}
compatibility
claude, copilot, cursor
references
[{"title":"Alistair Cockburn — Hexagonal Architecture (Ports and Adapters)","url":"https://alistair.cockburn.us/hexagonal-architecture/"},{"title":"Hexagonal Architecture — Wikipedia","url":"https://en.wikipedia.org/wiki/Hexagonal_architecture_(software)"}]
# Hexagonal Architecture (Ports and Adapters) ## Overview Hexagonal Architecture, introduced by Alistair Cockburn in 2005, organizes an application so that the core domain logic is isolated from external concerns (databases, APIs, UIs, message brokers) through **ports** (interfaces) and **adapters** (implementations). The goal is to make the application equally drivable by users, programs, automated tests, or batch scripts -- and equally connected to any external system. The architecture is also known as **Ports and Adapters**. It shares the same fundamental principle as Onion Architecture (Jeffrey Palermo, 2008) and Clean Architecture (Robert C. Martin): **dependencies point inward; the domain depends on nothing external.** ## The Hexagonal Diagram ``` Driving Side (Primary) (things that USE the app) REST CLI Tests Events │ │ │ │ ▼ ▼ ▼ ▼ ┌─────────────────────────────────┐ │ Primary Adapters │ │ (implement driving ports) │ │ │ ┌─────┤─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─┤─────┐ │ │ Primary Ports │ │ │ │ (interfaces the app exposes) │ │ │ │ │ │ │ │ ┌───────────────────┐ │ │ │ │ │ │ │ │ │ │ │ Domain Model │ │ │ │ │ │ (Pure Business │ │ │ │ │ │ Logic) │ │ │ │ │ │ │ │ │ │ │ └───────────────────┘ │ │ │ │ │ │ │ │ Secondary Ports │ │ │ │ (interfaces the app needs) │ │ └─────┤─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─┤─────┘ │ Secondary Adapters │ │ (implement driven ports) │ └─────────────────────────────────┘ │ │ │ │ ▼ ▼ ▼ ▼ Postgres Redis Stripe Kafka Driven Side (Secondary) (things the app USES) ``` ## Core Concepts ### Ports (Interfaces) Ports define the boundaries of the application. They are **interfaces** -- contracts that describe what the application can do (primary/driving) or what the application needs (secondary/driven). | Port Type | Also Called | Direction | Purpose | Example | |-----------|------------|-----------|---------|---------| | **Primary Port** | Driving Port | Inbound | Defines what the app **offers** to the outside world | `IOrderService.PlaceOrder(...)` | | **Secondary Port** | Driven Port | Outbound | Defines what the app **requires** from the outside world | `IOrderRepository.Save(...)`, `IPaymentGateway.Charge(...)` | ``` // Primary port — what the application offers public interface IOrderService { Task<OrderId> PlaceOrder(PlaceOrderCommand command); Task<OrderDto> GetOrder(OrderId id); Task CancelOrder(OrderId id); } // Secondary port — what the application needs public interface IOrderRepository { Task<Order?> FindById(OrderId id); Task Save(Order order); } // Secondary port — what the application needs public interface IPaymentGateway { Task<PaymentResult> Charge(Money amount, PaymentMethod method); Task Refund(PaymentId paymentId, Money amount); } ``` ### Adapters (Implementations) Adapters are concrete implementations that connect ports to specific technologies. They live **outside** the domain core. | Adapter Type | Also Called | Implements | Example | |-------------|------------|------------|---------| | **Primary Adapter** | Driving Adapter | Uses primary ports | REST controller, gRPC handler, CLI command, test harness | | **Secondary Adapter** | Driven Adapter | Implements secondary ports | PostgreSQL repository, Stripe payment adapter, Kafka publisher | ``` // Primary adapter — REST controller drives the application [ApiController] public class OrdersController : ControllerBase { private readonly IOrderService _orderService; // Primary port [HttpPost] public async Task<IActionResult> PlaceOrder(PlaceOrderRequest request) { var command = MapToCommand(request); var orderId = await _orderService.PlaceOrder(command); return Created($"/orders/{orderId}", new { orderId }); } } // Secondary adapter — PostgreSQL implements the repository port public class PostgresOrderRepository : IOrderRepository { private readonly DbContext _db; public async Task<Order?> FindById(OrderId id) { return await _db.Orders .Include(o => o.Lines) .FirstOrDefaultAsync(o => o.Id == id); } public async Task Save(Order order) { ... } } // Secondary adapter — Stripe implements the payment port public class StripePaymentGateway : IPaymentGateway { private readonly StripeClient _stripe; public async Task<PaymentResult> Charge(Money amount, PaymentMethod method) { var intent = await _stripe.PaymentIntents.CreateAsync(...); return MapToResult(intent); } } ``` ### Driving vs. Driven Side | Aspect | Driving (Primary) Side | Driven (Secondary) Side | |--------|----------------------|------------------------| | **Who initiates** | External actor drives the application | Application drives external systems | | **Port direction** | Inbound (app receives calls) | Outbound (app makes calls) | | **Adapter role** | Translates external input into domain calls | Translates domain calls into external system interactions | | **Dependency direction** | Adapter depends on port (calls it) | Adapter implements port (the domain defines the interface) | | **Examples** | HTTP controller, CLI, test, event consumer | Database, API client, message publisher, file system | ## Code Structure Example ``` src/ OrderService/ Domain/ # Pure domain model (no dependencies) Order.cs OrderLine.cs Money.cs OrderStatus.cs Ports/ Primary/ # What the app offers IOrderService.cs Commands/ PlaceOrderCommand.cs CancelOrderCommand.cs Queries/ GetOrderQuery.cs Secondary/ # What the app needs IOrderRepository.cs IPaymentGateway.cs IInventoryClient.cs IEventPublisher.cs Application/ # Use case orchestration OrderApplicationService.cs # Implements IOrderService Adapters/ Primary/ # Driving adapters Rest/ OrdersController.cs Grpc/ OrderGrpcService.cs Cli/ OrderCliCommand.cs Secondary/ # Driven adapters Persistence/ PostgresOrderRepository.cs Payment/ StripePaymentGateway.cs Messaging/ KafkaEventPublisher.cs Composition/ # Wires everything together (DI) ServiceRegistration.cs ``` ## The Dependency Rule The fundamental rule shared by Hexagonal, Onion, and Clean Architecture: ``` Dependencies point inward. Inner layers know nothing about outer layers. ┌─────────────────────────────────────────┐ │ Adapters (outermost) │ │ ┌─────────────────────────────────┐ │ │ │ Ports / Application │ │ │ │ ┌─────────────────────────┐ │ │ │ │ │ Domain Model │ │ │ │ │ │ (innermost, no deps) │ │ │ │ │ └─────────────────────────┘ │ │ │ └─────────────────────────────────┘ │ └─────────────────────────────────────────┘ Outer depends on inner. Never the reverse. ``` - The **domain** has zero external dependencies. No framework imports, no database references, no HTTP concepts. - **Ports** are defined by the domain/application layer using domain language. - **Adapters** depend on ports (and on external libraries), never the reverse. - **Composition root** (startup/DI configuration) wires adapters to ports. ## Comparison: Hexagonal vs. Onion vs. Clean Architecture | Aspect | Hexagonal (Cockburn) | Onion (Palermo) | Clean (Martin) | |--------|---------------------|-----------------|----------------| | **Core idea** | Ports and Adapters | Concentric layers | Dependency Rule | | **Visualization** | Hexagon with ports | Concentric circles | Concentric circles | | **Inner layer** | Domain Model | Domain Model | Entities | | **Boundary definition** | Ports (interfaces) | Layer interfaces | Use Case boundaries | | **Outer layer** | Adapters | Infrastructure | Frameworks & Drivers | | **Key emphasis** | Symmetry between driving/driven | Layer discipline | Use cases as central organizing concept | | **Dependency direction** | Inward | Inward | Inward | **They are the same fundamental idea expressed differently.** All three: - Isolate the domain from infrastructure. - Use interfaces (ports) at boundaries. - Enforce the dependency rule: inner layers never reference outer layers. - Enable technology swaps without changing business logic. The practical differences are in emphasis and vocabulary, not in principle. ### Onion Architecture Layers (Palermo) ``` ┌─────────────────────────────────────────┐ │ Infrastructure & UI (outermost) │ │ ┌─────────────────────────────────┐ │ │ │ Application Services │ │ │ │ ┌─────────────────────────┐ │ │ │ │ │ Domain Services │ │ │ │ │ │ ┌─────────────────┐ │ │ │ │ │ │ │ Domain Model │ │ │ │ │ │ │ │ (Entities, │ │ │ │ │ │ │ │ Value Objects)│ │ │ │ │ │ │ └─────────────────┘ │ │ │ │ │ └─────────────────────────┘ │ │ │ └─────────────────────────────────┘ │ └─────────────────────────────────────────┘ ``` ## Testability: The Primary Benefit The greatest practical benefit of hexagonal architecture is **testability**. Because the domain depends only on ports (interfaces), you can test business logic without any infrastructure. ``` // Test the domain using mock adapters — no database, no HTTP, no Stripe [Test] public async Task PlaceOrder_WithValidItems_ConfirmsOrder() { // Arrange — mock secondary ports var orderRepo = new InMemoryOrderRepository(); var paymentGateway = new FakePaymentGateway(alwaysSucceeds: true); var eventPublisher = new SpyEventPublisher(); // The application service uses ports, not concrete adapters var service = new OrderApplicationService( orderRepo, paymentGateway, eventPublisher); // Act — drive through primary port var orderId = await service.PlaceOrder(new PlaceOrderCommand { CustomerId = "cust-1", Items = new[] { new OrderItem("prod-1", 2, 25.00m) } }); // Assert — verify domain behavior var order = await orderRepo.FindById(orderId); Assert.Equal(OrderStatus.Confirmed, order.Status); Assert.Single(eventPublisher.PublishedEvents .OfType<OrderConfirmed>()); } ``` ### Testing Strategy by Layer | Layer | Test Type | What to Test | Infrastructure Needed | |-------|-----------|-------------|----------------------| | **Domain** | Unit tests | Business rules, invariants, calculations | None (pure logic) | | **Application** | Unit tests with mocks | Use case orchestration, event publishing | Mock adapters | | **Primary Adapters** | Integration tests | Request mapping, serialization, routing | HTTP test server | | **Secondary Adapters** | Integration tests | Database queries, API calls, serialization | Real or containerized infrastructure | | **Composition** | Smoke / E2E tests | Full system wiring, happy path | Full infrastructure | ## Common Mistakes | Mistake | Problem | Fix | |---------|---------|-----| | **Domain imports framework** | Domain coupled to infrastructure; hard to test | Remove all framework dependencies from domain layer | | **Adapter logic in domain** | Business logic leaks into controllers or repositories | Move logic to domain model or application service | | **Port too broad** | Interface with 20 methods; hard to mock, violates ISP | Split into focused interfaces (Interface Segregation Principle) | | **Skipping ports for "simplicity"** | Application calls database directly; loses swappability and testability | Always define a port even if you only have one adapter | | **Anemic domain + fat service** | Domain model is just data; all logic in application service | Enrich the domain model with behavior (see `dev/architecture/domain-driven-design`) | ## Best Practices - Keep the domain model completely free of infrastructure dependencies. No ORM attributes, no HTTP concepts, no serialization annotations in the domain layer. - Define ports using domain language, not technology language. `IOrderRepository.Save(Order)`, not `IDatabaseContext.ExecuteCommand(SQL)`. - Use dependency injection to wire adapters to ports at the composition root. - Write the majority of tests against ports (mock adapters), not against infrastructure. This gives you fast, reliable tests. - Start with one adapter per port. Add additional adapters when you actually need them (e.g., switching databases, adding a CLI interface). - Use the hexagonal structure to enable incremental migration: swap one adapter at a time without touching the domain. - Combine with DDD (see `dev/architecture/domain-driven-design`) for rich domain modeling inside the hexagon. - The hexagonal shape is a metaphor for symmetry -- there is no inherent "top" or "bottom." Any adapter on any side is equally first-class.
Ver no GitHub