Use cases and DTOs
Naming conventions
| Component | Convention | Example |
|---|---|---|
| Use Case | [Name]UseCase | PlaceOrderUseCase |
| Application DTO | [Name]DTO | PlaceOrderDTO |
| Domain DTO | [Name]DTO | OrderLineDTO |
| Port | [Name]…Interface | OrderRepositoryInterface |
| Entity | [Name]Entity | OrderEntity |
Use Case suffix and folder depth
The UseCase suffix is governed by folder depth under Application/UseCases/.
| Location | Suffix required? | Example |
|---|---|---|
Directly inside UseCases/{Capability}/ | Yes — *UseCase | PlaceOrderUseCase.php |
Inside any nested subfolder under {Capability}/ | No | OrderProcessor.php |
Mirror the same {Capability}/ folders for:
Domain/Ports/{Capability}/Application/DTO/{Capability}/- Outbound ACL ports →
Domain/Ports/Acl/ - Module façades →
Domain/Ports/Module/
{Capability}is a documentation placeholder — never a literalFeature/directory.
Application/UseCases/
└── Order/
├── PlaceOrderUseCase.php ✅ UseCase suffix
├── CancelOrderUseCase.php ✅ UseCase suffix
└── Processing/ nested — no suffix required
├── OrderProcessor.php
└── Payment/
└── CreditCardPaymentHandler.php
Entry-point boundary (strict)
- Only first-level
*UseCaseclasses may be invoked from UI, CLI, jobs, or admin actions in the same module. - Nested helpers are internal. UI must never call them.
- Other modules must never call this module’s Use Cases. Cross-module work uses ACL or Events only.
Entry method: __invoke
Every first-level Use Case exposes exactly one public entry: __invoke.
| Rule | Detail |
|---|---|
| Method name | Always __invoke — not execute() / handle() |
| Invocation | ($useCase)($dto) or $useCase->__invoke($dto) |
| Nested helpers | Not Use Cases; no __invoke requirement |
class PlaceOrderUseCase
{
public function __construct(
private OrderRepositoryInterface $orders,
private WarehouseAvailabilityPortInterface $warehouse,
) {}
public function __invoke(PlaceOrderDTO $dto): OrderEntity
{
// orchestrate Domain + ports
}
}
Application DTO vs Domain DTO
| Layer | Path | Purpose | Typical flow |
|---|---|---|---|
| Application | Application/DTO/ | Carry validated UI input into a Use Case | Controller → Use Case |
| Domain | Domain/DTO/ | Structured data inside the module | Use Case ↔ Entity / Port |
- Application DTOs receive external input. Controllers map validated request data into them.
- Domain DTOs never cross the HTTP/UI boundary as the primary API contract.
Illustrative Ordering examples
Application/DTO/Order/PlaceOrderDTO.php— API input when placing an order.Domain/DTO/OrderLineDTO.php— line data while building the order.Domain/DTO/TotalsBreakdownDTO.php— totals consumed by another Use Case in the same module.
Composition inside Application
A first-level Use Case may delegate to nested helpers in the same feature tree. Helpers receive already-injected ports (or are resolved with the same port interfaces). They must not import ORM types or become a second UI entry point.
Next: ports & persistence.