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

install
source · Clone the upstream repo
git clone https://github.com/aligundogdu/symfony-hexagonal-skill
Claude Code · Install into ~/.claude/skills/
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"
manifest: skills/symfony-hexagonal-architecture/SKILL.md
source content

Symfony 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 (

{Module}
) şu anda hexagonal yapıda değil. Şu seçeneklerden birini tercih edebilirsiniz:

  1. Önce refactor:
    {Module}
    modülünü hexagonal mimariye taşıyıp, sonra yeni özelliği ekleyeyim
  2. Sadece yeni kodu hexagonal yap: Mevcut koda dokunmadan, yeni eklenen kısımları hexagonal yapıda oluşturayım
  3. 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:

  1. Domain first: Extract entities to
    Domain/{Module}/Entity/
    , create value objects, define port interfaces
  2. Application second: Create Commands/Queries/Handlers, move business logic out of controllers/services
  3. Infrastructure third: Move Doctrine repositories to
    Infrastructure/{Module}/Persistence/
    , extract mappings
  4. Presentation last: Thin out controllers, add
    ApiResponseTrait
    , apply
    #[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:

  1. Create directory structure (all 4 layers)
  2. Create a sample port interface in
    Domain/{Module}/Port/
  3. Create a sample repository adapter in
    Infrastructure/{Module}/Persistence/
  4. Bind the port to adapter in
    services.yaml
  5. Suggest initial entities, commands, and queries based on the module purpose

Key Rules

  1. Domain purity: No
    use Symfony\...
    or
    use Doctrine\...
    in any Domain file
  2. Port-first: Define the interface before the implementation
  3. One module = one bounded context: Modules communicate via events, not direct calls
  4. Namespace convention:
    App\Domain\{Module}\...
    ,
    App\Application\{Module}\...
    , etc.

References

See

references/
for detailed guides:

  • directory-structure.md
    — Full directory layout with file examples
  • layer-responsibilities.md
    — What belongs in each layer
  • dependency-rules.md
    — Import rules and PHPStan enforcement