Vortos
Make

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 --dev

The package is auto-discovered. All 24 commands are immediately available in bin/console.

How It Works

Every vortos:make:* command does three things:

  1. Validates input — names, context folders, event class resolution, argument constraints
  2. Renders a stub — substitutes your values into the template for that file type
  3. 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 default

Placing 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

CommandWhat it generates
vortos:make:contextFull bounded context directory structure
vortos:make:aggregateAggregate root + typed AggregateId (in ValueObject/) + repository interface, inside Domain/{Name}/
vortos:make:entityChild entity inside Domain/{Aggregate}/Entity/ + typed EntityId in ValueObject/ — requires --aggregate
vortos:make:value-objectImmutable value object — requires --aggregate or --shared
vortos:make:domain-eventDomain event inside Domain/{Aggregate}/Event/ — requires --aggregate. No Event suffix — pass UserRegistered, get UserRegistered.php
vortos:make:domain-errorStructured domain error — requires --aggregate or --shared
vortos:make:domain-serviceStateless domain service with #[AsDomainService] — requires --aggregate or --shared

CQRS

CommandWhat it generates
vortos:make:commandCQRS command + handler
vortos:make:queryCQRS query + handler
vortos:make:projection-handlerKafka projection handler

Messaging

CommandWhat it generates
vortos:make:consumerKafka event handler
vortos:make:messaging-configMessagingConfig class (transport + producer + consumer)
vortos:make:middlewareKafka consumer middleware
vortos:make:hookMessaging lifecycle hook

HTTP

CommandWhat it generates
vortos:make:controllerAPI controller + request DTO

Persistence

CommandWhat it generates
vortos:make:write-repositoryPostgreSQL write repository + interface
vortos:make:read-repositoryMongoDB read repository

Authorization

CommandWhat it generates
vortos:make:authorization-policyResource authorization policy
vortos:make:ownership-policyOwnership policy
vortos:make:feature-policyFeature access policy
vortos:make:rate-limit-policyRate limit policy
vortos:make:quota-policyQuota policy
vortos:make:quota-resolverQuota subject resolver
vortos:make:session-policySession 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=UserRegistered

Starting a brand-new bounded context? Generate the directory tree first:

php bin/console vortos:make:context Order

Detailed Pages

On this page