Module Guidelines

Module Guidelines

Module Structure

Every module lives in src/Module/{ModuleName}/ and follows this structure:

src/Module/{ModuleName}/
├── Domain/
│   ├── Model/
│   │   ├── {Aggregate}.php           # Aggregate root
│   │   ├── {ValueObject}.php         # Value objects
│   │   └── {Entity}.php              # Child entities
│   ├── Event/
│   │   ├── {Entity}Created.php       # Domain events
│   │   └── {Entity}Updated.php
│   ├── Repository/
│   │   └── {Aggregate}RepositoryInterface.php
│   └── Exception/
│       └── {Aggregate}NotFoundException.php
├── Application/
│   ├── Command/
│   │   ├── Create{Entity}Command.php
│   │   └── Create{Entity}CommandHandler.php
│   ├── Query/
│   │   ├── Get{Entity}Query.php
│   │   └── Get{Entity}QueryHandler.php
│   └── EventHandler/
│       └── On{ExternalEvent}Handler.php
├── Infrastructure/
│   ├── Persistence/
│   │   ├── Doctrine{Aggregate}Repository.php
│   │   └── mapping/
│   │       └── {Aggregate}.orm.xml
│   └── Service/
│       └── {ExternalServiceAdapter}.php
├── Presentation/
│   ├── Controller/
│   │   └── {Entity}Controller.php
│   └── DTO/
│       ├── {Entity}Request.php
│       └── {Entity}Response.php
├── Contract/
│   └── {Module}ProviderInterface.php  # Public API for other modules
└── config/
    ├── routes.yaml
    └── services.yaml

Creating a New Module — Checklist

  1. Create the directory structure above
  2. Define the aggregate root in Domain/Model/
  3. Define domain events in Domain/Event/
  4. Define repository interface in Domain/Repository/
  5. Implement command/query handlers in Application/
  6. Implement repository in Infrastructure/Persistence/
  7. Create Doctrine mapping in Infrastructure/Persistence/mapping/
  8. Create controller and DTOs in Presentation/
  9. Define contract interface in Contract/ (if other modules need access)
  10. Add routes in config/routes.yaml
  11. Add services in config/services.yaml
  12. Register module in deptrac.yaml
  13. Update .ai/context/MODULE_STATUS.md
  14. Add events to .ai/context/EVENT_CATALOG.md
  15. Write unit tests for domain, integration tests for API

Boundary Rules

  • Domain layer: zero framework dependencies
  • Application layer: depends on Domain only (+ framework interfaces for dispatching)
  • Infrastructure: depends on Domain + framework
  • Presentation: depends on Application + framework
  • Contract: pure PHP interfaces, depends on nothing
  • Cross-module: only via Contract interfaces or domain events

Module Registration

Each module’s config/services.yaml registers its own services:

services:
    App\Module\{ModuleName}\:
        resource: '../../src/Module/{ModuleName}/'

Each module’s config/routes.yaml defines its API routes.