Laravel DDD adds a small set of commands for organizing an app into DDD-style layers, instead of the default app/ structure.
Layers are defined in config('ddd.layers'). Out of the box you get six:
| Layer | Namespace | Path |
|---|---|---|
Domain |
Domain\ |
src/Domain |
Modules |
Modules\ |
src/Modules |
Foundation |
Foundation\ |
src/Foundation |
Support |
Support\ |
src/Support |
Infrastructure |
Infrastructure\ |
src/Infrastructure |
Integrations |
Integrations\ |
src/Integrations |
Publish the config file to add, rename, or remove layers, or to point an existing layer back at App\ if you'd rather keep it under app/.
Infrastructure holds concrete adapters to external systems, like repositories, storage, and queues. Integrations holds third-party service integrations, like payments and notifications. Remove either one from the published config if your app doesn't need the distinction.
php artisan ddd:install
This registers each layer's namespace in your composer.json's autoload.psr-4 map, creates the layer directories, and rebuilds the autoloader. Run it once, right after installing the package.
php artisan ddd:install --force # overwrite namespaces that already point elsewhere
php artisan ddd:install --no-dump-autoload # skip regenerating the autoloader
ddd:install is just a convenience wrapper around a few file edits, so you can skip it and reproduce the same result by hand. Add the default layers, plus Database\Factories\ and Database\Seeders\, to autoload.psr-4, and add the Foundation layer's Helpers.php to autoload.files:
{
"autoload": {
"psr-4": {
"Domain\\": "src/Domain/",
"Modules\\": "src/Modules/",
"Foundation\\": "src/Foundation/",
"Support\\": "src/Support/",
"Infrastructure\\": "src/Infrastructure/",
"Integrations\\": "src/Integrations/",
"Database\\Factories\\": "database/factories/",
"Database\\Seeders\\": "database/seeders/"
},
"files": [
"src/Foundation/Helpers.php"
]
}
}
If you removed the Infrastructure or Integrations layers from config/ddd.php, drop them from autoload.psr-4 the same way.
Then create the layer directories and src/Foundation/Helpers.php from the package's helpers.ddd.stub (see Customizing Stubs to change its default content), and run composer dump-autoload to pick up the new mappings.
ddd:make generates a class into a layer, similar to Laravel's own make:model and other generator commands:
php artisan ddd:make CreateInvoice --type=action
# Domain\Invoice\Actions\CreateInvoice
The domain is guessed from the class name (Invoice here) unless you pass --domain:
php artisan ddd:make Actions/CreateInvoice --type=action --domain=Billing
Each layer also has its own shortcut command, so you don't need to pass --layer:
php artisan ddd:make-domain Invoice --type=model
php artisan ddd:make-module InvoiceController --type=controller
php artisan ddd:make-foundation AppServiceProvider --type=provider
php artisan ddd:make-support Money --type=value_object
--type picks both the stub and the subfolder the class is generated into:
action cast channel class collection
command contract controller data dto
enum event exception factory filter
job listener mail middleware migration
model notification observer pipe policy
provider query_builder request resource rule
scope seeder service setting state
trait value_object view_model
Publish the stubs if you want to change them for the whole app:
php artisan vendor:publish --tag=ddd-stubs
This copies every *.ddd.stub file into stubs/; delete the ones you don't want to override. To publish a single stub instead:
mkdir -p stubs && cp vendor/foxws/laravel-ddd/stubs/action.ddd.stub stubs/action.ddd.stub
Or point a type at any file via config:
// config/ddd.php
'stubs' => [
'action' => 'stubs/ddd/custom-action.stub',
],
Each type maps to a subfolder under the domain (action → Actions, model → Models, and so on). Override or add entries via config:
// config/ddd.php
'substitutions' => [
'action' => 'UseCases', // Domain\Invoice\Actions\... becomes Domain\Invoice\UseCases\...
'query_builder' => 'Eloquent\Builders', // nest under an extra namespace segment
],
A helper resolves the path for each default layer, the same way the generators do:
domain_path(); // base_path('src/Domain')
domain_path('Invoice/Actions'); // base_path('src/Domain/Invoice/Actions')
modules_path(); // base_path('src/Modules')
modules_path('Web/Controllers'); // base_path('src/Modules/Web/Controllers')
foundation_path(); // base_path('src/Foundation')
foundation_path('Providers'); // base_path('src/Foundation/Providers')
support_path(); // base_path('src/Support')
support_path('Money'); // base_path('src/Support/Money')
infrastructure_path(); // base_path('src/Infrastructure')
infrastructure_path('Storage'); // base_path('src/Infrastructure/Storage')
integrations_path(); // base_path('src/Integrations')
integrations_path('Stripe'); // base_path('src/Integrations/Stripe')
For a custom or renamed layer, use layer_path() directly:
layer_path('Billing', 'Invoices'); // base_path('src/Billing/Invoices')