$npx -y skills add aligundogdu/symfony-hexagonal-skill --skill symfony-hexagonal-architectureSymfony 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
| 1 | # Symfony Hexagonal Architecture |
| 2 | |
| 3 | 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. |
| 4 | |
| 5 | ## When to Activate |
| 6 | |
| 7 | - User wants to create a new Symfony project with hexagonal architecture |
| 8 | - User asks about project structure or directory layout |
| 9 | - User wants to scaffold a new module/bounded context |
| 10 | - User asks about layer responsibilities or dependency rules |
| 11 | - User mentions "hexagonal", "ports and adapters", "clean architecture" in Symfony context |
| 12 | |
| 13 | ## Core Principles |
| 14 | |
| 15 | ### Layer Direction (STRICT) |
| 16 | ``` |
| 17 | Presentation → Application → Domain ← Infrastructure |
| 18 | ``` |
| 19 | |
| 20 | Infrastructure depends on Domain (implements ports), but Domain NEVER depends on Infrastructure. |
| 21 | |
| 22 | ### Module Structure |
| 23 | |
| 24 | Every module follows this structure under `src/`: |
| 25 | |
| 26 | ``` |
| 27 | Domain/{Module}/ |
| 28 | ├── Entity/ # Aggregates and entities (pure PHP) |
| 29 | ├── ValueObject/ # Immutable value objects |
| 30 | ├── Event/ # Domain events (past-tense) |
| 31 | ├── Exception/ # Domain-specific exceptions |
| 32 | └── Port/ # Interfaces for external dependencies |
| 33 | |
| 34 | Application/{Module}/ |
| 35 | ├── Command/ # Write operations (final readonly) |
| 36 | ├── Query/ # Read operations (final readonly) |
| 37 | ├── DTO/ # Data transfer objects |
| 38 | └── EventHandler/ # Domain event handlers |
| 39 | |
| 40 | Infrastructure/{Module}/ |
| 41 | ├── Persistence/ # Doctrine repositories, mappings |
| 42 | ├── Messaging/ # Messenger handlers, transports |
| 43 | └── ExternalService/ # HTTP clients, third-party APIs |
| 44 | |
| 45 | Presentation/{Module}/ |
| 46 | ├── API/ # REST controllers |
| 47 | ├── CLI/ # Console commands |
| 48 | └── GraphQL/ # GraphQL resolvers/types |
| 49 | ``` |
| 50 | |
| 51 | ## Progressive Refactoring (Existing Projects) |
| 52 | |
| 53 | When working on an existing project that doesn't follow hexagonal architecture: |
| 54 | |
| 55 | ### Step 1: Detect Current Structure |
| 56 | Before writing any code, check: |
| 57 | ``` |
| 58 | - Is src/ organized by layer (Domain/, Application/, etc.) or by traditional Symfony (Controller/, Entity/, Repository/)? |
| 59 | - Does the module the user is working on already follow hexagonal patterns? |
| 60 | - Are there Doctrine annotations directly on entities? |
| 61 | ``` |
| 62 | |
| 63 | ### Step 2: Ask the User |
| 64 | When the user requests a feature/change in a non-hexagonal module, ALWAYS ask: |
| 65 | |
| 66 | > "Bu modül (`{Module}`) şu anda hexagonal yapıda değil. Şu seçeneklerden birini tercih edebilirsiniz: |
| 67 | > 1. **Önce refactor**: `{Module}` modülünü hexagonal mimariye taşıyıp, sonra yeni özelliği ekleyeyim |
| 68 | > 2. **Sadece yeni kodu hexagonal yap**: Mevcut koda dokunmadan, yeni eklenen kısımları hexagonal yapıda oluşturayım |
| 69 | > 3. **Mevcut yapıda devam et**: Bu sefer hexagonal yapıyı atlayalım |
| 70 | > |
| 71 | > Hangisini tercih edersiniz?" |
| 72 | |
| 73 | ### Step 3: Incremental Migration (if user chooses refactor) |
| 74 | Migrate one layer at a time, in this order: |
| 75 | |
| 76 | 1. **Domain first**: Extract entities to `Domain/{Module}/Entity/`, create value objects, define port interfaces |
| 77 | 2. **Application second**: Create Commands/Queries/Handlers, move business logic out of controllers/services |
| 78 | 3. **Infrastructure third**: Move Doctrine repositories to `Infrastructure/{Module}/Persistence/`, extract mappings |
| 79 | 4. **Presentation last**: Thin out controllers, add `ApiResponseTrait`, apply `#[IsGranted]` |
| 80 | |
| 81 | After each layer, verify the code still works before moving to the next. |
| 82 | |
| 83 | ### Key Principles |
| 84 | - **Never touch modules the user isn't working on** — only refactor what's in scope |
| 85 | - **One module at a time** — don't try to migrate the whole project |
| 86 | - **Keep the app working** — each step should be a working state |
| 87 | - **Respect user's pace** — some teams prefer gradual migration over months |
| 88 | |
| 89 | ## Scaffolding a New Module |
| 90 | |
| 91 | When user asks to create a new module, generate ALL directories and base files: |
| 92 | |
| 93 | 1. Create directory structure (all 4 layers) |
| 94 | 2. Create a sample port interface in `Domain/{Module}/Port/` |
| 95 | 3. Create a sample repository adapter in `Infrastructure/{Module}/Persistence/` |
| 96 | 4. Bind the port to adapter in `services.yaml` |
| 97 | 5. Suggest initial entities, commands, and queries based on the module purpose |
| 98 | |
| 99 | ## Key Rules |
| 100 | |
| 101 | 1. **Domain purity**: No `use Symfony\...` or `use Doctrine\...` in any Domain file |
| 102 | 2. **Port-first**: Define the interface before the implementation |
| 103 | 3. **One module = one bounded context**: Modules communicate via events, not direct calls |
| 104 | 4. **Namespace convention**: `App\Domain\{Module}\...`, `App\Application\{Module}\...`, etc. |
| 105 | |
| 106 | ## References |
| 107 | |
| 108 | See `references/` for detailed guides: |
| 109 | - `directory-structure.md` — Full directory layout with file examples |
| 110 | - `layer-responsibilities.md` — What belongs in each layer |
| 111 | - `dependency-rules.md` — Import rules and PHPStan enforcement |