Make
24 code generation commands that scaffold your entire application — domain, CQRS, messaging, HTTP, persistence, and authorization — from a single CLI.
Make
vortos-make is a code generator built into the framework. It understands Vortos architecture and generates files that already follow every golden rule — correct attribute usage, correct layer placement, correct naming. You describe what you want; it writes the boilerplate.
Not a Scaffold — a Starting Point
Generated files compile and run immediately. They do not contain // TODO markers. They contain real implementations with the correct structure, the correct imports, and the correct attribute configuration. You fill in the logic; the generator handles everything else.
Installation
composer require vortos/vortos-make --devThe package is auto-discovered. All 24 commands are immediately available in bin/console.
How It Works
Every vortos:make:* command does three things:
- Validates input — names, context folders, event class resolution, argument constraints
- Renders a stub — substitutes your values into the template for that file type
- Writes the file — creates the target directory if needed, skips if the file already exists
After writing, each command prints a next-step hint — the exact follow-up action required, such as registering in DI config or dispatching via the command bus. No need to memorise the wiring rules.
Files are always written to src/{Context}/{Layer}/{ClassName}.php. The generator never touches existing files — re-running a command is always safe.
Stub Resolution
Stubs live inside each module's Resources/stubs/ directory. When a command renders a stub it looks in two places, in order:
stubs/{stub-name}.stub ← your project override (wins if present)
↓ not found
{module}/Resources/stubs/{stub-name}.stub ← framework defaultPlacing a file in your project's stubs/ directory overrides any framework stub by that name. See Customizing Stubs for the full override guide.
Event Class Resolution
Several commands accept --event as either a short name (UserRegisteredEvent) or a full FQCN. When a short name is given, the generator scans src/ to find the class:
# Short name — generator scans src/ to resolve to FQCN
php bin/console vortos:make:consumer SendEmail \
--context=User \
--consumer=user.events \
--event=UserRegistered
# Full FQCN — used as-is, no scanning
php bin/console vortos:make:consumer SendEmail \
--context=User \
--consumer=user.events \
--event="App\\User\\Domain\\Event\\UserRegistered"If the short name matches zero classes, the command fails with a helpful error. If it matches more than one, it fails listing all ambiguous matches so you can use the full FQCN.
Available Commands
Domain
| Command | What it generates |
|---|---|
vortos:make:context | Full bounded context directory structure |
vortos:make:aggregate | Aggregate root + typed AggregateId (in ValueObject/) + repository interface, inside Domain/{Name}/ |
vortos:make:entity | Child entity inside Domain/{Aggregate}/Entity/ + typed EntityId in ValueObject/ — requires --aggregate |
vortos:make:value-object | Immutable value object — requires --aggregate or --shared |
vortos:make:domain-event | Domain event inside Domain/{Aggregate}/Event/ — requires --aggregate. No Event suffix — pass UserRegistered, get UserRegistered.php |
vortos:make:domain-error | Structured domain error — requires --aggregate or --shared |
vortos:make:domain-service | Stateless domain service with #[AsDomainService] — requires --aggregate or --shared |
CQRS
| Command | What it generates |
|---|---|
vortos:make:command | CQRS command + handler |
vortos:make:query | CQRS query + handler |
vortos:make:projection-handler | Kafka projection handler |
Messaging
| Command | What it generates |
|---|---|
vortos:make:consumer | Kafka event handler |
vortos:make:messaging-config | MessagingConfig class (transport + producer + consumer) |
vortos:make:middleware | Kafka consumer middleware |
vortos:make:hook | Messaging lifecycle hook |
HTTP
| Command | What it generates |
|---|---|
vortos:make:controller | API controller + request DTO |
Persistence
| Command | What it generates |
|---|---|
vortos:make:write-repository | PostgreSQL write repository + interface |
vortos:make:read-repository | MongoDB read repository |
Authorization
| Command | What it generates |
|---|---|
vortos:make:authorization-policy | Resource authorization policy |
vortos:make:ownership-policy | Ownership policy |
vortos:make:feature-policy | Feature access policy |
vortos:make:rate-limit-policy | Rate limit policy |
vortos:make:quota-policy | Quota policy |
vortos:make:quota-resolver | Quota subject resolver |
vortos:make:session-policy | Session limit policy |
Quick Start — Scaffolding a Feature End-to-End
The typical flow for adding a new feature to an existing bounded context:
# 1. Generate the aggregate root
php bin/console vortos:make:aggregate User --context=User
# 2. Generate the domain event the feature will emit
php bin/console vortos:make:domain-event UserRegistered --context=User --aggregate=User
# 3. Generate the command that triggers the action
php bin/console vortos:make:command RegisterUser --context=User
# 4. Generate the write repository for the aggregate
php bin/console vortos:make:write-repository User --context=User
# 5. Generate the HTTP controller to accept the request
php bin/console vortos:make:controller RegisterUser --context=User --route=/users --method=POST
# 6. Generate a Kafka consumer to react to the event
php bin/console vortos:make:consumer SendWelcomeEmail \
--context=User \
--consumer=user.events \
--event=UserRegisteredStarting a brand-new bounded context? Generate the directory tree first:
php bin/console vortos:make:context OrderDetailed Pages
Domain
context, aggregate, entity, value-object, domain-event, domain-error, domain-service.
CQRS
command, query, projection-handler.
Messaging
consumer, messaging-config, middleware, hook.
HTTP
controller and request DTO.
Persistence
write-repository and read-repository.
Authorization
All seven policy and resolver types.
Customizing Stubs
Override framework stubs with your own project templates.