PHPackages                             ahmedsaed/laravel-ddd - PHPackages - PHPackages  [Skip to content](#main-content)[PHPackages](/)[Directory](/)[Categories](/categories)[Trending](/trending)[Leaderboard](/leaderboard)[Changelog](/changelog)[Analyze](/analyze)[Collections](/collections)[Log in](/login)[Sign up](/register)

1. [Directory](/)
2. /
3. ahmedsaed/laravel-ddd

ActiveLibrary

ahmedsaed/laravel-ddd
=====================

Lightweight DDD structure and architecture tooling for Laravel

v0.1.0(today)00MITPHPPHP ^8.4CI passing

Since Jul 30Pushed todayCompare

[ Source](https://github.com/ahmedsaed27/laravel-ddd)[ Packagist](https://packagist.org/packages/ahmedsaed/laravel-ddd)[ Docs](https://github.com/ahmedsaed/laravel-ddd)[ GitHub Sponsors]()[ RSS](/packages/ahmedsaed-laravel-ddd/feed)WikiDiscussions main Synced today

READMEChangelog (1)Dependencies (14)Versions (2)Used By (0)

Laravel DDD
===========

[](#laravel-ddd)

Laravel DDD adds bounded-Context generators, architecture checks, and Context-aware database commands to a normal Laravel 11–13 application. It keeps Laravel’s container, events, queues, mail, routes, views, Eloquent, Pest, and PHPUnit as the integration APIs; it does not add wrapper frameworks around them.

Install
-------

[](#install)

```
composer require ahmedsaed/laravel-ddd
```

New applications use project-root `contexts/` and the `Contexts` namespace. Add the namespace to the application’s `composer.json`:

```
{
    "autoload": {
        "psr-4": {
            "App\\": "app/",
            "Contexts\\": "contexts/"
        }
    }
}
```

Then rebuild the autoloader:

```
composer dump-autoload
```

The path and namespace remain configurable in `config/laravel-ddd.php` for applications with an existing convention.

The generated Context
---------------------

[](#the-generated-context)

```
php artisan ddd:make-context Order
```

creates this case-sensitive structure:

```
contexts/Order/
├── Config/
│   └── config.php
├── Providers/
│   └── OrderServiceProvider.php
├── Domain/
│   ├── Aggregates/
│   ├── Entities/
│   ├── Events/
│   ├── Repositories/
│   └── ValueObjects/
├── Application/
│   ├── DTOs/
│   ├── Queries/
│   └── UseCases/
├── Infrastructure/
│   ├── Listeners/
│   ├── Jobs/
│   ├── Mail/
│   ├── Integrations/
│   ├── Persistence/
│   │   └── Eloquent/
│   │       ├── Models/
│   │       ├── Migrations/
│   │       ├── Seeders/
│   │       └── Factories/
│   └── Search/
├── Presentation/
│   ├── Console/
│   │   └── Commands/
│   └── Http/
│       ├── Controllers/
│       ├── Middleware/
│       ├── Requests/
│       ├── Resources/
│       └── Routes/
│           ├── api.php
│           └── web.php
├── resources/
│   ├── assets/
│   └── views/
└── tests/
    ├── Feature/
    └── Unit/

```

`Config` and `Providers` are uppercase. `resources` and `tests` are lowercase. This matters on Linux.

The command creates directories and framework bootstrap files, not fictional business classes or frontend entry files. It also creates the conventional empty `OrderDatabaseSeeder`.

### Optional Aggregate and Vite setup

[](#optional-aggregate-and-vite-setup)

```
php artisan ddd:make-context Order --with-aggregate
php artisan ddd:make-context Order --with-vite
php artisan ddd:make-context Order --with-aggregate --with-vite
```

`--with-aggregate` adds only:

```
Domain/Aggregates/Order.php
Domain/Repositories/OrderRepository.php

```

Both classes are intentionally empty. `--with-vite` adds only `contexts/Order/vite.config.js`. It uses the root application’s Node dependencies, a Context-specific hot file, and a Context-specific build directory. It does not create a Context `package.json` or pretend that nonexistent JavaScript/CSS entry files exist. Add real entries to the generated empty `input` array when the Context gains frontend assets.

`--force` repairs framework scaffolding and missing directories. It preserves the entry seeder and optional Aggregate/Repository business files.

A Laravel-oriented DDD vocabulary
---------------------------------

[](#a-laravel-oriented-ddd-vocabulary)

- An **Aggregate** protects a transactional business invariant.
- An **Entity** has identity and lifecycle inside a business model.
- A **Value Object** represents an immutable value such as money.
- A **Domain Event** records a business fact in `Domain/Events`.
- A **Repository interface** describes Aggregate persistence in Domain.
- A **Use Case** coordinates one application action in `Application/UseCases`.
- A **DTO** carries application input or output in `Application/DTOs`.
- A **Laravel Listener**, **Job**, or **Mailable** is an Infrastructure adapter.
- A **Form Request**, controller, middleware, or Resource is a Presentation adapter.
- The root Context Service Provider is the composition root.

Dependencies point inward:

```
Presentation → Application → Domain
Infrastructure → Application and Domain

```

Domain contains business rules and no Laravel/Symfony dependency, with one deliberate exception: a class in `Domain/Events` may use `Illuminate\Foundation\Events\Dispatchable`. This narrow compromise makes a Domain Event feel native in Laravel without allowing facades, helpers, Eloquent, queues, mail, HTTP, or the container into the rest of Domain.

Application owns DTOs, Queries, and Use Cases. It may directly dispatch an Aggregate’s recorded events after a successful save, but it does not use the database, Eloquent, cache, HTTP, mail, or queue adapters.

Infrastructure and Presentation are normal Laravel code. They may use the container, facades, and helpers appropriate to their boundaries.

Native Laravel events
---------------------

[](#native-laravel-events)

Generate a Domain Event and a typed Listener:

```
php artisan ddd:make-event Order:OrderPlaced
php artisan ddd:make-listener Order:SendOrderReceipt \
    --event='Contexts\Order\Domain\Events\OrderPlaced'
```

The event stub contains Laravel’s event `Dispatchable` trait, so all standard styles work:

```
event(new OrderPlaced($orderId));
Event::dispatch(new OrderPlaced($orderId));
OrderPlaced::dispatch($orderId);
```

Listeners live directly in `Infrastructure/Listeners`. The package adds `contexts/*/Infrastructure/Listeners` to Laravel’s supported event-discovery paths. Laravel infers the event from the first argument:

```
final readonly class SendOrderReceipt
{
    public function __construct(
        private PrepareOrderReceipt $prepareReceipt,
    ) {}

    public function handle(OrderPlaced $event): void
    {
        SendOrderReceiptJob::dispatch(
            $this->prepareReceipt->execute($event),
        );
    }
}
```

There is no provider event map. Do not bind the Listener or the concrete Use Case: Laravel resolves both through constructor injection. Repository interfaces and other replaceable ports still need explicit bindings.

Laravel’s operational commands work with Context listeners:

```
php artisan event:list
php artisan event:cache
php artisan event:clear
```

When dispatch happens after persistence, record events in the Aggregate, save it, then release and dispatch them:

```
$this->orders->save($order);

foreach ($order->releaseEvents() as $event) {
    event($event);
}
```

Production systems that need atomic database/event delivery should add an outbox rather than dispatch before the transaction commits.

Native Laravel helpers
----------------------

[](#native-laravel-helpers)

Context classes do not need package wrappers. In Infrastructure and Presentation, normal Laravel calls work:

```
event($event);
Event::dispatch($event);
OrderPlaced::dispatch($orderId);

dispatch(new SendOrderReceiptJob($orderId));
SendOrderReceiptJob::dispatch($orderId);
dispatch_sync(new RebuildOrderReadModel($orderId));

config('order.notification_recipient');
view('order::orders.show', ['order' => $order]);
route('order.orders.show', $order);
app(PrepareOrderReceipt::class);
cache()->put('order:last', $order->id);
logger()->info('Order placed');
response()->json($payload);
now();
```

Jobs may use the `sync` queue connection in local and test environments. The same dispatch APIs remain valid.

Context Service Provider
------------------------

[](#context-service-provider)

`Providers/OrderServiceProvider.php`:

- merges `Config/config.php` under the snake-case `order` key;
- loads `Presentation/Http/Routes/api.php` and `web.php`;
- loads Context migrations;
- registers `resources/views` as the `order` view namespace.

For example:

```
view('order::orders.show');
```

The provider’s `register()` method is where repository interfaces and other replaceable ports are bound:

```
$this->app->bind(
    OrderRepository::class,
    EloquentOrderRepository::class,
);
```

Providers are discovered from each direct child of `contexts/`. Explicit registration in `bootstrap/providers.php` is also supported. The former `Infrastructure/Providers` location remains a discovery-only compatibility fallback; if both files exist, only the root provider is registered.

Component commands
------------------

[](#component-commands)

All class commands use `Context:ClassName`, print their target, protect existing files unless `--force` is supplied, and reuse the same configured path/namespace system.

CommandGenerated location`ddd:make-aggregate``Domain/Aggregates``ddd:make-entity``Domain/Entities``ddd:make-value-object``Domain/ValueObjects``ddd:make-event``Domain/Events``ddd:make-repository``Domain/Repositories``ddd:make-dto``Application/DTOs``ddd:make-use-case``Application/UseCases``ddd:make-model``Infrastructure/Persistence/Eloquent/Models``ddd:make-migration``Infrastructure/Persistence/Eloquent/Migrations``ddd:make-seeder``Infrastructure/Persistence/Eloquent/Seeders``ddd:make-factory``Infrastructure/Persistence/Eloquent/Factories``ddd:make-listener``Infrastructure/Listeners``ddd:make-job``Infrastructure/Jobs``ddd:make-mail``Infrastructure/Mail``ddd:make-controller``Presentation/Http/Controllers``ddd:make-request``Presentation/Http/Requests``ddd:make-resource``Presentation/Http/Resources``ddd:make-middleware``Presentation/Http/Middleware``ddd:make-provider``Providers``ddd:make-config``Config/config.php`Repository generation can add an Eloquent adapter with `--eloquent` and a mapper with `--mapper`. Jobs are queued by default; `--sync` creates a synchronous dispatchable Job. Mailables accept either `--view` or `--markdown`. Listener `--event` values must be fully qualified, and `--queued` applies Laravel’s queued-listener convention.

Migration inference is conservative:

- `create_orders_table` uses `Schema::create` and drops the table in `down`;
- safely inferred add/change names use `Schema::table`;
- ambiguous names produce a valid neutral migration with no database work and no unused schema imports.

No generator invents fields, relationships, event properties, routes, or business methods.

Context databases
-----------------

[](#context-databases)

The package provides Context-aware migration and seeding commands:

```
php artisan ddd:migrate [Order]
php artisan ddd:migrate-status [Order]
php artisan ddd:migrate-rollback [Order]
php artisan ddd:migrate-reset [Order]
php artisan ddd:migrate-refresh [Order]
php artisan ddd:migrate-fresh [Order]
php artisan ddd:seed [Order]
```

Connection precedence is CLI option, Context config, then Laravel default. Rollback/reset select only migration rows owned by the chosen Context. `ddd:migrate-fresh` refuses a shared/default physical database and requires `database.dedicated=true`. Standard `php artisan migrate` and `php artisan db:seed` remain available.

See [Context databases](docs/context-databases.md) for the complete option and safety rules.

Context tests
-------------

[](#context-tests)

The generator creates lowercase `tests/Feature` and `tests/Unit` directories. PHPUnit only discovers directories declared in a test suite, so add the Context root explicitly instead of rewriting `phpunit.xml` from a generator:

```

    contexts

```

For Pest, apply the application Test Case only to Context Feature tests:

```
$contextFeatureTests = glob(
    dirname(__DIR__).'/contexts/*/tests/Feature',
) ?: [];

uses(Tests\TestCase::class)->in(...$contextFeatureTests);
```

The example includes an additive `phpunit.contexts.xml`, Pest setup, and Unit and Feature tests. This keeps application test configuration an explicit developer choice and avoids fragile XML string editing.

Architecture checks
-------------------

[](#architecture-checks)

```
php artisan ddd:check
```

The checker reports namespace/layer violations, outward Domain dependencies, database/cache/HTTP/mail/queue dependencies in Application, direct cross-Context Aggregate/Entity imports, and obvious misplaced Laravel adapters. Strict mode fails; non-strict mode warns.

It tokenizes PHP namespaces and imports. It cannot prove every dynamic container lookup, helper call, string class name, or runtime dependency, so the documented policy still matters.

Example
-------

[](#example)

[`examples/laravel-app`](examples/laravel-app) contains a readable Order/Catalog/Search application slice:

```
PlaceOrder Use Case
→ Order Aggregate records OrderPlaced
→ OrderRepository saves
→ event(...) dispatches
→ automatically discovered Laravel Listener
→ concrete notification preparation Use Case
→ Laravel Job
→ Mailable and Context view

```

Catalog owns SQL product writes. Search owns the eventually consistent product read model. `ProductCreated` is handled by Search’s automatically discovered Listener, which dispatches an indexing Job and delegates to the Search Application layer.

More documentation
------------------

[](#more-documentation)

- [Architecture and layer policy](docs/architecture.md)
- [Command reference](docs/commands.md)
- [Modular Contexts](docs/modular-contexts.md)
- [Example walkthrough](docs/examples.md)
- [Troubleshooting](docs/troubleshooting.md)

###  Health Score

40

—

FairBetter than 86% of packages

Maintenance100

Actively maintained with recent releases

Popularity0

Limited adoption so far

Community19

Small or concentrated contributor base

Maturity40

Maturing project, gaining track record

 Bus Factor1

Top contributor holds 59.7% of commits — single point of failure

How is this calculated?**Maintenance (25%)** — Last commit recency, latest release date, and issue-to-star ratio. Uses a 2-year decay window.

**Popularity (30%)** — Total and monthly downloads, GitHub stars, and forks. Logarithmic scaling prevents top-heavy scores.

**Community (15%)** — Contributors, dependents, forks, watchers, and maintainers. Measures real ecosystem engagement.

**Maturity (30%)** — Project age, version count, PHP version support, and release stability.

###  Release Activity

Cadence

Unknown

Total

1

Last Release

0d ago

### Community

Maintainers

![](https://www.gravatar.com/avatar/edee719b8dbb41a6394141900c76815f4675d2ea2fcc3db8933e81192d6c48f4?d=identicon)[ahmedsaed27](/maintainers/ahmedsaed27)

---

Top Contributors

[![freekmurze](https://avatars.githubusercontent.com/u/483853?v=4)](https://github.com/freekmurze "freekmurze (390 commits)")[![mvdnbrk](https://avatars.githubusercontent.com/u/802681?v=4)](https://github.com/mvdnbrk "mvdnbrk (46 commits)")[![dependabot[bot]](https://avatars.githubusercontent.com/in/29110?v=4)](https://github.com/dependabot[bot] "dependabot[bot] (35 commits)")[![Nielsvanpach](https://avatars.githubusercontent.com/u/10651054?v=4)](https://github.com/Nielsvanpach "Nielsvanpach (23 commits)")[![github-actions[bot]](https://avatars.githubusercontent.com/in/15368?v=4)](https://github.com/github-actions[bot] "github-actions[bot] (21 commits)")[![pforret](https://avatars.githubusercontent.com/u/474312?v=4)](https://github.com/pforret "pforret (16 commits)")[![sebastiandedeyne](https://avatars.githubusercontent.com/u/1561079?v=4)](https://github.com/sebastiandedeyne "sebastiandedeyne (14 commits)")[![AlexVanderbist](https://avatars.githubusercontent.com/u/6287961?v=4)](https://github.com/AlexVanderbist "AlexVanderbist (12 commits)")[![patinthehat](https://avatars.githubusercontent.com/u/5508707?v=4)](https://github.com/patinthehat "patinthehat (10 commits)")[![riasvdv](https://avatars.githubusercontent.com/u/3626559?v=4)](https://github.com/riasvdv "riasvdv (10 commits)")[![crynobone](https://avatars.githubusercontent.com/u/172966?v=4)](https://github.com/crynobone "crynobone (8 commits)")[![AdrianMrn](https://avatars.githubusercontent.com/u/12762044?v=4)](https://github.com/AdrianMrn "AdrianMrn (8 commits)")[![ahmedsaed27](https://avatars.githubusercontent.com/u/103289562?v=4)](https://github.com/ahmedsaed27 "ahmedsaed27 (7 commits)")[![thecaliskan](https://avatars.githubusercontent.com/u/13554944?v=4)](https://github.com/thecaliskan "thecaliskan (5 commits)")[![irfanm96](https://avatars.githubusercontent.com/u/42065936?v=4)](https://github.com/irfanm96 "irfanm96 (5 commits)")[![IGedeon](https://avatars.githubusercontent.com/u/694313?v=4)](https://github.com/IGedeon "IGedeon (4 commits)")[![ziming](https://avatars.githubusercontent.com/u/679513?v=4)](https://github.com/ziming "ziming (3 commits)")[![jessarcher](https://avatars.githubusercontent.com/u/4977161?v=4)](https://github.com/jessarcher "jessarcher (3 commits)")[![koossaayy](https://avatars.githubusercontent.com/u/6431084?v=4)](https://github.com/koossaayy "koossaayy (3 commits)")[![lloricode](https://avatars.githubusercontent.com/u/8251344?v=4)](https://github.com/lloricode "lloricode (3 commits)")

---

Tags

dddddd-architectureddd-patternslaravel-frameworklaravel-packagephplaravellaravel-dddAhmed Saed

###  Code Quality

TestsPest

Static AnalysisPHPStan

Code StyleLaravel Pint

### Embed Badge

![Health badge](/badges/ahmedsaed-laravel-ddd/health.svg)

```
[![Health](https://phpackages.com/badges/ahmedsaed-laravel-ddd/health.svg)](https://phpackages.com/packages/ahmedsaed-laravel-ddd)
```

###  Alternatives

[laravel/ai

The official AI SDK for Laravel.

1.0k3.2M256](/packages/laravel-ai)[illuminate/queue

The Illuminate Queue package.

20432.6M1.7k](/packages/illuminate-queue)[spatie/laravel-health

Monitor the health of a Laravel application

87912.0M177](/packages/spatie-laravel-health)[spatie/laravel-pdf

Create PDFs in Laravel apps

1.0k4.8M48](/packages/spatie-laravel-pdf)[rawilk/profile-filament-plugin

Profile &amp; MFA starter kit for filament.

3914.8k](/packages/rawilk-profile-filament-plugin)[masterix21/laravel-licensing

Laravel licensing package with polymorphic assignment to any model, activation keys, expirations/renewals, and seat control via LicenseUsage. Supports offline verification with public-key–signed tokens, a CLI to generate/rotate/revoke keys, and an extensible architecture via config and contracts.

1613.3k4](/packages/masterix21-laravel-licensing)

PHPackages © 2026

[Directory](/)[Categories](/categories)[Trending](/trending)[Changelog](/changelog)[Analyze](/analyze)
