hexagonal-architecture
Guide for structuring applications using Hexagonal architecture and DDD.
Install
mkdir -p .claude/skills/hexagonal-architecture-michalwelna0 && curl -L -o skill.zip "https://agentskills.codes/api/skills/download/15204" && unzip -o skill.zip -d .claude/skills/hexagonal-architecture-michalwelna0 && rm skill.zipInstalls to .claude/skills/hexagonal-architecture-michalwelna0
Activation
This is the description your AI agent reads to decide when to run this skill — the better it matches your request, the more reliably it fires.
Hexagonal architecture (ports/adapters) with DDD principles. Use when implementing features, refactoring code, organizing code structure, determining where code should be placed, creating adapters, services, ports, or when questions arise about architectural layers, dependency direction, adapter patterns, or service patterns.Key capabilities
- →Place code in the correct layer (domain, service, port, adapter, controller).
- →Define new ports or adapters with correct dependency direction.
- →Create or modify adapters, services, or controllers following layer-specific conventions.
- →Review imports and structure to enforce architectural boundaries.
- →Enforce business rules and invariants in domain models.
- →Orchestrate use cases using services.
How it works
The skill defines responsibilities for controllers, domain models, services, ports, and adapters, and specifies dependency rules to maintain architectural boundaries.
Inputs & outputs
When to use hexagonal-architecture
- →Refactoring code into domains and adapters
- →Defining new ports
- →Determining layer responsibilities
About this skill
Hexagonal Architecture + DDD
When to Use
- You are adding or refactoring features and need to place code in the correct layer (domain/service/port/adapter/controller).
- You are defining a new port or adapter and want to ensure dependency direction is correct.
- You are creating or modifying adapters, services, or controllers and need layer-specific conventions.
- You are reviewing imports/structure to enforce architectural boundaries.
Quick Reference
| Topic | File | When to Use |
|---|---|---|
| Project structure | project-structure.md | Canonical directory tree, code location mapping, layer responsibilities, naming conventions |
Controllers (Thin Layer)
Controllers are the entry points that connect external inputs to the application. Examples: a FastAPI app, a Click CLI, a Chainlit chat app. Keep them thin:
- Parse/validate inputs
- Call
sharedservices - Map errors to appropriate responses (e.g., HTTP status codes, CLI exit codes, UI messages)
- Serialize outputs via
model_dump(mode="json")or equivalent - No business rules in controllers
For FastAPI-specific conventions, see the fastapi skill.
For Click-specific conventions, see the cli-conventions skill.
Domain Models
- Put business rules/invariants in domain models (
shared/src/shared/domain/**) - Raise
ValueErrorwith clear messages for domain violations - Pydantic v2 is the sole allowed third-party dependency in domain models
For DDD building blocks (entities, value objects, aggregates), file organization, and the persistence contract, see the domain-models skill.
Services (Orchestration)
Services are use-case orchestrators in shared/src/shared/services/**.
For system health checks (technical, non-business concerns):
- Put domain models in
shared/src/shared/domain/platform_ops/health/ - Services should aggregate adapter results into a generic
SystemHealth(no per-adapter probe logic in services)
Inputs
- Fixed internal knobs (model IDs, temperatures, limits) live in a nested:
@dataclass(frozen=True, slots=True) class Hyperparams: ...- with
_HP: Final[Hyperparams] = Hyperparams()
- Controller-controlled parameters should be Pydantic v2 models named
*Params(typically inshared/src/shared/domain/**) and passed asparams=... - Direct inputs (what to act on) are primitive args alongside
params(e.g.,source_url: str,article_id: UUID)
Outputs
- Return domain models (Pydantic); let controllers serialize via
model_dump(mode="json") - If a workflow needs a composite result, prefer a dedicated domain response model over a raw
dict
Validation
- Validate service-level inputs early (bounds, URL format) using shared validators (e.g.,
validate_url) - Domain invariants live in domain models and raise
ValueError - Do not duplicate the same validation across service + domain layers
Resource Handling
- If a service receives closeable dependencies, the wiring layer (controller-side DI / setup) must own lifecycle and close them in
finally
Ports (Interfaces)
- Define boundaries as ports (
shared/src/shared/ports/**) usingtyping.Protocol - Minimal method surface; no side effects; no external library imports; return domain types
- Ports MUST be technology-agnostic: folder names describe business capabilities (e.g.,
ports/database/,ports/llm/), NOT technologies - Repository naming conventions:
- Single item:
get_{model}(...) -> Model | None(missing →None) - Lists:
list_{model}s(..., limit: int | None = None, offset: int = 0) -> list[Model](missing →[]) - Prefer one
list_*with multiple optional filters over specialized*_by_*methods
- Single item:
Adapters (Implementations)
Adapters live in shared/src/shared/adapters/** and are technology-specific (e.g., adapters/database/postgres/, adapters/llm/).
For adapters integrating external components (DB/LLM/HTTP/etc.), expose:
async def health_check(self) -> ComponentHealth- Use generic health VO models (
ComponentHealth,SystemHealth) and put component diagnostics indetails(e.g.db-version,base-url)
Validation
- Validate adapter inputs early and fail fast with
ValueError(non-empty strings, bounds, required IDs) - Avoid magic numbers: define timeouts/limits/constants at module top
Error Handling
- Wrap integration failures as
RuntimeErrorwith actionable messages - Keep original cause via
raise ... from e - Log failures at
errorlevel (no secrets in logs or exceptions)
Resource Cleanup
- If an adapter owns network/IO resources, implement
close()(async if needed) close()should be best-effort: log and continue on cleanup errors- The wiring layer (controller-side DI / setup) must always close adapters in
finally
Database (Repository) Adapters
Repository adapters follow the thin wrapper pattern:
- Delegate CRUD operations to generic helpers in
{db_engine}_aggregate_crud.py - Define
TABLE_NAMEandSTORED_MODEL_NAMEconstants at module top - Validate
dbconnection in__init__ - Wrap
asyncpg.PostgresErrorasRuntimeError(done in CRUD helpers, not repeated in adapters) - Return validated domain models (deserialized via
AggregateRoot.from_payload())
Dependency Direction
shared/src/shared/domain/**→ no dependencies on project code (standard library + Pydantic only)shared/src/shared/services/**andshared/src/shared/ports/**→ may depend only onshared/src/shared/domain/**shared/src/shared/adapters/**→ may depend onshared/src/shared/domain/**,shared/src/shared/services/**, andshared/src/shared/ports/**- Controller directories (e.g.,
api/,app/,cli/) → may depend onshared/(domain, services, ports, adapters), butshared/must not depend on controllers shared/src/shared/adapters/database/{db-engine}/migrations/**andshared/src/shared/config/**→ may depend on shared code but must not be imported from domain, services, or ports
Change Workflow Checklists
Adding New External Capability
- Add a new Port in
shared/src/shared/ports/ - Implement an Adapter in
shared/src/shared/adapters/.../ - Update/create a Service method in
shared/src/shared/services/to orchestrate via ports - Wire the adapter in the controller layer as needed (do not wire inside
shared)
Adding Database Read Model / Cross-Aggregate Lookup
- Add a QueryPort in
shared/src/shared/ports/database/queries/ - Implement the adapter in
shared/src/shared/adapters/database/.../queries/ - Use services to orchestrate; do not expose adapters directly to controllers
Adding a New Aggregate Repository
- Create a migration
- Add a repository port in
shared/src/shared/ports/database/repositories/ - Implement a repository adapter — thin wrapper delegating to
{db_engine}_aggregate_crudhelpers - Wire the adapter in the controller layer
Adding New Domain Rule
- Enforce in the domain model (entity/value object) with clear
ValueErrormessages - Add unit tests in
shared/tests/for both valid and invalid cases
Changing Error Message or Validation
- Update/extend tests to match the new behavior and message (tests commonly use
match=...)
Cross-References
- Project structure: See project-structure.md for canonical paths, directory tree, and layer responsibilities
- Domain models: See
domain-modelsskill for DDD building blocks - FastAPI: See
fastapiskill for API-specific conventions - CLI: See
cli-conventionsskill for Click-specific conventions - Python patterns: See
pythonskill for type hints and error handling
When not to use it
- →When the task is not related to architectural layers or dependency direction.
- →When the task is not about organizing code structure.
- →When the task is not about creating adapters, services, or ports.
Limitations
- →Domain models allow Pydantic v2 as the sole allowed third-party dependency.
- →Ports must be technology-agnostic.
- →Adapters must be technology-specific.
How it compares
This skill provides a structured approach to organizing code into distinct layers with clear dependency rules, unlike an unstructured approach that might lead to tangled dependencies.
Compared to similar skills
hexagonal-architecture side by side with the closest alternatives in the catalog.
| Skill | Installs | Updated | Safety | Difficulty |
|---|---|---|---|---|
| hexagonal-architecture (this skill) | 0 | 6mo | No flags | Advanced |
| software-architecture | 333 | 6mo | No flags | Intermediate |
| codex | 32 | 2mo | Review | Advanced |
| game-development | 70 | 6mo | No flags | Intermediate |
Try saying
Example prompts that trigger this skill in your AI assistant.
You might also like
software-architecture
davila7
Guide for quality focused software architecture. This skill should be used when users want to write code, design architecture, analyze code, in any case that relates to software development.
codex
Lucklyric
Invoke Codex CLI for complex coding tasks requiring high reasoning capabilities. This skill should be invoked when users explicitly mention "Codex", request complex implementation challenges, advanced reasoning, or need high-reasoning model assistance. Automatically triggers on codex-related requests and supports session continuation for iterative development.
game-development
davila7
Game development orchestrator. Routes to platform-specific skills based on project needs.
senior-fullstack
davila7
Comprehensive fullstack development skill for building complete web applications with React, Next.js, Node.js, GraphQL, and PostgreSQL. Includes project scaffolding, code quality analysis, architecture patterns, and complete tech stack guidance. Use when building new projects, analyzing code quality, implementing design patterns, or setting up development workflows.
command-name
anthropics
This skill should be used when the user asks to "create a plugin", "scaffold a plugin", "understand plugin structure", "organize plugin components", "set up plugin.json", "use ${CLAUDE_PLUGIN_ROOT}", "add commands/agents/skills/hooks", "configure auto-discovery", or needs guidance on plugin directory layout, manifest configuration, component organization, file naming conventions, or Claude Code plugin architecture best practices.
python-project-structure
wshobson
Python project organization, module architecture, and public API design. Use when setting up new projects, organizing modules, defining public interfaces with __all__, or planning directory layouts.