Weave Code
Weaver
Helps Laravel developers discover, compare, and choose open-source packages. See popularity, security, maintainers, and scores at a glance to make better decisions.
Feedback
Share your thoughts, report bugs, or suggest improvements.
Subject
Message

Ddd Laravel Package

theaddresstechnology/ddd

A Laravel package that brings Domain-Driven Design structure to your application, with practical building blocks for organising domain logic, separating concerns, and keeping business code clean and maintainable. It helps model bounded contexts and keep infrastructure details away from your core dom...

View on GitHub
Deep Wiki
Context7

Getting Started

Still a scaffolding tool with a narrow blast radius — but v2.1.0 is the first release with real documentation (docs/TESTING.md), so start there rather than in src/. Before installing anything, decide on two things: the root path for domain code (app/Domain vs src/Domain) and the namespace root that maps to it. Neither has changed.

  1. Verify the package is actually resolvable. The release notes state Packagist still showed v1.1.14 at publication time. Confirm before you write a composer.json constraint against ^2.1:

    composer show theaddresstechnology/ddd --all | head -20
    

    If the v2 line isn't listed, either wait for the maintainer to refresh the index or add the Git tag as a vcs repository and require 2.1.0 explicitly. Pin, then commit composer.json and composer.lock immediately.

  2. Check the version constraint against your framework. v2.1.0 was validated on PHP 8.4/8.5 × Laravel 12/13 only. If you are on Laravel 10 or 11, ^2.1 is not your package — install in a branch and run the suite before assuming anything.

  3. Publish and read the config. Behavior is still driven by one config file:

    php artisan vendor:publish --tag=ddd-config
    

    Look for path, namespace, stubs, layer-name overrides, and anything test-related added in v2. If a publish tag doesn't exist, check publishes() in vendor/theaddresstechnology/ddd/src/.

  4. Discover the commands. Now genuinely useful, because the docs cover testing rather than the whole surface:

    php artisan list | grep -E 'ddd|module'
    

    v2.1.0 introduces ddd:setup-tests, module:make-test, and a repurposed ddd:make Test. Read each $signature in src/Console/Commands for the option matrix — --unit, --feature, --both, --domain=, --name=, --legacy-phpunit.

  5. Scaffold one throwaway context end to end, tests included. Pick Billing:

    php artisan ddd:make:context Billing
    php artisan ddd:make Test --domain=Billing --name=OrderTest --both
    

    Open every generated file. Verify namespaces, composer dump-autoload, confirm classes resolve, then run the generated tests. They should be green and empty — v2.1.0 explicitly ships them as todos, not coverage.

  6. First production use case: one aggregate, one value object, one repository interface, a hand-written Eloquent implementation, a provider binding, and one real feature test fleshed out from the generated stub. If that loop feels natural, the package earns its place. If you spend more time fixing namespaces than writing code, copy the stubs and drop the dependency.

Implementation Patterns

Context-per-module generation. Still the intended workflow: generate Billing, wire it, ship it, then Shipping. v2's test generation makes this more valuable — each context lands with its own test scaffolding, so you get a reviewable per-context diff that includes tests.

Layer-aware generation, not layer-blind. The target shape is unchanged:

  • Domain/Billing/{Entities,ValueObjects,Aggregates,DomainEvents,Repositories} — pure PHP, no Eloquent.
  • Application/Billing/{Actions,DTOs,Queries} — orchestration, transactions, no persistence details.
  • Infrastructure/Billing/{Persistence,Migrations,Providers} — Eloquent models, repository implementations, mappings.

Use the generator for the first two layers and expect to hand-write most of the infrastructure.

Split tests along the same seam as the code. v2.1.0 encodes this: feature tests bind the application's Laravel TestCase, unit tests stay isolated. Lean on it deliberately:

# Domain logic: no container, no DB, fast
module:make-test OrderTest Billing --unit

# HTTP/application path: boots Laravel, uses the app TestCase
module:make-test PlaceOrderTest Billing --feature

Default to --both when you're unsure; it costs one extra empty file. Prefer --both on CRUD scaffolds too — v2 preserves existing CRUD tests rather than overwriting them, so re-running is comparatively safe.

Treat generated tests as a checklist, not a gate. The release notes are refreshingly honest: starter cases are todos. Do not let a green Pest run in CI create the illusion that a new context is tested. Wire the generator into your definition of done as "test files exist and are listed in the PR description as unimplemented" — then implement them in the same PR.

Run ddd:setup-tests once, then diff it. It discovers both tests/ and Tests/ layouts and is designed not to replace existing Pest/TestCase files or application PHPUnit settings. "Designed not to" is not "proven not to" in your repo. Run it on a clean tree:

git status --porcelain   # expect empty
php artisan ddd:setup-tests
git diff

Commit that diff as its own PR. Every subsequent context generator then has a sane target.

Customize the stubs before the codebase grows. Still the highest-leverage integration step, and now covers more surface: publish stubs into stubs/ddd/ and edit both the class stubs and the test stubs. Standardize declare(strict_types=1), final classes, readonly promotion, and your Pest conventions (it() vs test(), dataset usage, expectation style) in one sitting.

Pair with native Artisan, don't replace it. Use ddd: / module: commands for domain primitives and generated Pest tests; keep make:controller, make:request, make:middleware for the HTTP edge.

Standardize the invocation. The command count grew in v2; wrap the handful you actually use:

"scripts": {
  "ddd:context": "php artisan ddd:make:context",
  "ddd:aggregate": "php artisan ddd:make:aggregate",
  "ddd:test": "php artisan ddd:make Test",
  "ddd:test:module": "php artisan module:make-test"
}

Keep Pest a dev dependency, and prove it. The notes claim a --no-dev install generates tests without Pest present. Mirror that in CI with a cheap job: composer install --no-dev in a scratch project, run one generator, assert exit code 0. It takes seconds and catches a stray use Pest\ in a non-test stub.

Mapping discipline. If the generator puts Eloquent models in the Domain layer, write explicit mappers. Keep domain entities free of Model inheritance; otherwise the abstraction is cosmetic and you inherit the query builder in places it doesn't belong.

Gotchas and Tips

Packagist index lag is the immediate blocker. A published Git tag is not an installable package. Until the maintainer refreshes the index, composer require theaddresstechnology/ddd:^2.1 will fail or silently resolve to v1. Check composer show --all first, and if you must move now, use a vcs repository pinned to the tag rather than a dev- constraint.

ddd:make Test changed behavior. It now emits Pest. The reflection-based PHPUnit generator is behind --legacy-phpunit. If you have scripts, docs, or CI steps calling ddd:make Test from v1, they will write different files after the upgrade. Grep your repo and your runbooks before bumping.

Laravel 13 + Pest 5, Laravel 12 + Pest 4. Mixing them is the first thing that will break. Check composer.json constraints before any Laravel minor upgrade — the previous warning stands, just shifted: v2.1.0's tested matrix is PHP 8.4/8.5 × Laravel 12/13, and nothing older is claimed.

Namespace/path drift is still the number-one failure mode. ddd.path and ddd.namespace are read independently. Set path to app/Domain with namespace Src\Domain and generated classes — now including test classes — silently fail to autoload. After every config change: composer dump-autoload -o and resolve one generated class in php artisan tinker.

Confirm PSR-4 coverage for both source and test roots. Generated directories must fall under an existing autoload.psr-4 (or autoload-dev.psr-4) entry. If you generate into src/ or Tests/, you need the mapping — the package will not add it.

Generated tests are probably invisible to coverage. The old warning now matters more: phpunit.xml source includes frequently miss generated directories. Add app/Domain, app/Application, and the generated test roots explicitly, or your best-tested code reports 0% and your worst-tested code reports 100%.

Commands are not uniformly idempotent. v2 hardens the test path — duplicate test outputs, invalid paths, symbolic-link escapes, unknown modules, and conflicting options all fail safely. That is not a blanket guarantee for the class/context generators, which have historically appended provider registrations and clobbered files. Always generate on a clean tree, then git diff. If something is clobbered, delete the directory and regenerate rather than patching in place.

Published stubs go stale — and now there are more of them. Once stubs live in your repo, package updates no longer reach them. That's fine, arguably desirable, but note it in your upgrade checklist and pin the version so upstream stub changes don't surprise you.

Symlink and nested-path rejections are a feature, not a bug. If module:make-test refuses a nested name or a path that resolves outside the module, it is enforcing a sandbox. Don't work around it with ../ — fix the module name or the config.

Debugging a no-op command. If a ddd: command exits silently, verify registration in the package's service provider and clear a stale provider manifest: php artisan optimize:clear.

You are still the QA, but less alone. 83 tests / 496 assertions across two framework/Pest combinations, plus integration tests that execute generated fixtures with the real Pest runner, is a meaningful step up from zero dependents and no matrix. It is not a community. Keep the habit of installing new minors in a branch and running your own suite.

Escape hatch: copy the stubs and drop the dependency. Still cheap, and v2 makes it slightly cheaper — the test stubs are now templates you could carry yourself, and ddd:setup-tests is a one-time config step you can replicate with a pest.php and a TestCase. Don't import package classes into application code; keep the exit free.

Don't let the tool dictate architecture. Inspect what lands in the Domain layer. If the generator emits validation rules, HTTP concerns, or Illuminate\Database\Eloquent\Model subclasses there — or a Pest test that boots the Laravel container inside app/Domain — fix the stubs before you have twenty contexts to refactor.

Extension points are still just config and stubs. If you need different command behavior (say, a test naming convention the options don't support), fork and reference via a Composer path/vcs repository. Never edit vendor/.

Weaver

How can I help you explore Laravel packages today?

Conversation history is not saved when not logged in.
Prompt
Add packages to context
No packages found.
coleus/frame
thtg88/journalism
filagora/filagora-ai-forums
rlacerda83/query-parser
socialdept/atp-orm
cboxdk/laravel-assistant-client
socialdept/atp-resolver
socialdept/tenant-domains
tautvydasr/blade-class-names
socialdept/color-scales
maherelgamil/controller
coleus/widgets
coleus/table
coleus/users
coleus/music
coleus/health
coleus/calendar
coleus/notes
coleus/support
coleus/settings