Symfony-hexagonal-skill symfony-hexagonal-architecture
Symfony hexagonal architecture setup — project structure, module scaffolding, layer responsibilities, dependency rules. Triggers on: architecture, module, layer, hexagonal, scaffold, project structure, directory structure, new project, new module
git clone https://github.com/aligundogdu/symfony-hexagonal-skill
T=$(mktemp -d) && git clone --depth=1 https://github.com/aligundogdu/symfony-hexagonal-skill "$T" && mkdir -p ~/.claude/skills && cp -r "$T/skills/symfony-hexagonal-architecture" ~/.claude/skills/aligundogdu-symfony-hexagonal-skill-symfony-hexagonal-architecture && rm -rf "$T"
skills/symfony-hexagonal-architecture/SKILL.mdSymfony Hexagonal Architecture
You are an expert in Symfony hexagonal architecture (ports & adapters). Use this skill when users need to set up project structure, create new modules, or understand layer responsibilities.
When to Activate
- User wants to create a new Symfony project with hexagonal architecture
- User asks about project structure or directory layout
- User wants to scaffold a new module/bounded context
- User asks about layer responsibilities or dependency rules
- User mentions "hexagonal", "ports and adapters", "clean architecture" in Symfony context
Core Principles
Layer Direction (STRICT)
Presentation → Application → Domain ← Infrastructure
Infrastructure depends on Domain (implements ports), but Domain NEVER depends on Infrastructure.
Module Structure
Every module follows this structure under
src/:
Domain/{Module}/ ├── Entity/ # Aggregates and entities (pure PHP) ├── ValueObject/ # Immutable value objects ├── Event/ # Domain events (past-tense) ├── Exception/ # Domain-specific exceptions └── Port/ # Interfaces for external dependencies Application/{Module}/ ├── Command/ # Write operations (final readonly) ├── Query/ # Read operations (final readonly) ├── DTO/ # Data transfer objects └── EventHandler/ # Domain event handlers Infrastructure/{Module}/ ├── Persistence/ # Doctrine repositories, mappings ├── Messaging/ # Messenger handlers, transports └── ExternalService/ # HTTP clients, third-party APIs Presentation/{Module}/ ├── API/ # REST controllers ├── CLI/ # Console commands └── GraphQL/ # GraphQL resolvers/types
Progressive Refactoring (Existing Projects)
When working on an existing project that doesn't follow hexagonal architecture:
Step 1: Detect Current Structure
Before writing any code, check:
- Is src/ organized by layer (Domain/, Application/, etc.) or by traditional Symfony (Controller/, Entity/, Repository/)? - Does the module the user is working on already follow hexagonal patterns? - Are there Doctrine annotations directly on entities?
Step 2: Ask the User
When the user requests a feature/change in a non-hexagonal module, ALWAYS ask:
"Bu modül (
) şu anda hexagonal yapıda değil. Şu seçeneklerden birini tercih edebilirsiniz:{Module}
- Önce refactor:
modülünü hexagonal mimariye taşıyıp, sonra yeni özelliği ekleyeyim{Module}- Sadece yeni kodu hexagonal yap: Mevcut koda dokunmadan, yeni eklenen kısımları hexagonal yapıda oluşturayım
- Mevcut yapıda devam et: Bu sefer hexagonal yapıyı atlayalım
Hangisini tercih edersiniz?"
Step 3: Incremental Migration (if user chooses refactor)
Migrate one layer at a time, in this order:
- Domain first: Extract entities to
, create value objects, define port interfacesDomain/{Module}/Entity/ - Application second: Create Commands/Queries/Handlers, move business logic out of controllers/services
- Infrastructure third: Move Doctrine repositories to
, extract mappingsInfrastructure/{Module}/Persistence/ - Presentation last: Thin out controllers, add
, applyApiResponseTrait#[IsGranted]
After each layer, verify the code still works before moving to the next.
Key Principles
- Never touch modules the user isn't working on — only refactor what's in scope
- One module at a time — don't try to migrate the whole project
- Keep the app working — each step should be a working state
- Respect user's pace — some teams prefer gradual migration over months
Scaffolding a New Module
When user asks to create a new module, generate ALL directories and base files:
- Create directory structure (all 4 layers)
- Create a sample port interface in
Domain/{Module}/Port/ - Create a sample repository adapter in
Infrastructure/{Module}/Persistence/ - Bind the port to adapter in
services.yaml - Suggest initial entities, commands, and queries based on the module purpose
Key Rules
- Domain purity: No
oruse Symfony\...
in any Domain fileuse Doctrine\... - Port-first: Define the interface before the implementation
- One module = one bounded context: Modules communicate via events, not direct calls
- Namespace convention:
,App\Domain\{Module}\...
, etc.App\Application\{Module}\...
References
See
references/ for detailed guides:
— Full directory layout with file examplesdirectory-structure.md
— What belongs in each layerlayer-responsibilities.md
— Import rules and PHPStan enforcementdependency-rules.md