START HERE FEATURE
Module Structure
Start with the smallest useful module shape, then expand the system around it instead of hiding the request path under the product shell.
Feature Guide
A quick orientation block that answers the essential questions: what this feature does, how it works, why it matters, and the key concepts behind it.
What this does
The minimal Semitexa module is a typed HTTP spine of payload, handler, resource, and template.
File tree: a contact form module example
Each file points to the page that explains that concern in more detail.
packages/semitexa-demo/src/Application/
├── Payload/
│ └── Request/
│ └── ContactFormPayload.php
├── Handler/
│ └── PayloadHandler/
│ └── ContactFormHandler.php
├── Resource/
│ └── Response/
│ └── ContactFormResource.php
└── View/
└── templates/
└── pages/
└── contact-form.html.twig
Read it as the smallest useful path: request boundary, use case, response, presentation.
Verified against Semitexa Ultimate 2026.09.19.1020
Module Structure
A Semitexa module begins with one minimal HTTP spine:
- payload
- handler
- resource
- template
Everything else extends that path. Nothing replaces it.
Responsibility split
Payload
Owns the route contract and inbound data boundary.
Handler
Owns the use case and orchestration.
Resource
Owns response data, metadata, and render context.
Template
Owns presentation only.
Why this matters
First-time readers should be able to explain a module in one sentence before they learn the whole catalog. The small typed spine keeps the request path legible while the product shell can grow around it.
Canonical folder layout
This is the canonical Semitexa module layout for request payloads, handlers, and response DTOs.
Payload: Application/Payload/{Type}/
| Subfolder | Purpose | Attribute / usage |
|---|---|---|
| Request | HTTP request DTOs (route + methods) | #[AsPublicPayload(path, methods, responseWith)]; require entry in src/registry/Payloads/ |
| Session | Session segment DTOs | #[SessionSegment('name')]; SessionInterface::getPayload() / setPayload() |
| Event | Event DTOs for dispatch | Used with EventDispatcher::create(EventClass::class, [...]) and dispatch() |
Namespaces: Semitexa\Modules\{Module}\Application\Payload\Request\, ...\Payload\Session\, ...\Payload\Event\.
Do not put these in Application/Session/ or other ad-hoc module-root folders. Use Application/Payload/Request/, Payload/Session/, Payload/Event/ only.
Request DTOs declare access through one of #[AsPublicPayload] / #[AsProtectedPayload] / #[AsServicePayload], and finer requirements through #[RequiresPermission('name')] or #[RequiresCapability('name')].
Handler: Application/Handler/{Type}/
| Subfolder | Purpose | Attribute |
|---|---|---|
| PayloadHandler | HTTP handlers (payload → resource) | #[AsPayloadHandler(payload: ..., resource: ...)] |
| System | Pipeline listeners (Auth/Access phases) | #[AsPipelineListener(phase: ..., priority: ...)] |
| Server | Swoole server lifecycle hooks including pre-fork bootstrap | #[AsServerLifecycleListener(phase: ..., priority: ...)] |
| DomainListener | Domain event listeners (sync/async/queued) | #[AsEventListener(event: ..., execution: ...)] |
Full layout
Application/
├── Payload/
│ ├── Request/ # HTTP request DTOs
│ ├── Session/ # Session segment DTOs
│ └── Event/ # Event DTOs
├── Handler/
│ ├── PayloadHandler/ # HTTP handlers
│ ├── System/ # Pipeline listeners
│ └── DomainListener/ # Domain event listeners
├── Server/ # Swoole server lifecycle listeners
├── Resource/ # Response DTOs
├── View/templates/
└── Service/ # optional