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

Laravel9 Ddd Laravel Package

hibit-dev/laravel9-ddd

Laravel 9 DDD is a starter package for building Domain-Driven Design applications in Laravel. It offers a modular structure that separates domain, application, and infrastructure layers, helping organize repositories, actions, and business logic with clean architecture principles.

View on GitHub
Deep Wiki
Context7

Getting Started

This is a reference application, not a drop-in Composer dependency. There is no Hibit\Ddd\ServiceProvider you require into an existing app — you adopt it by cloning the repo (or composer create-project hibit-dev/laravel9-ddd my-app) and treating the layered src/ tree as your project skeleton. Everything below assumes that model.

Minimal steps:

  • git clone / template the repo, then composer install, copy .env.example to .env, php artisan key:generate.
  • Bring up the database (the repo ships a Docker setup in most branches) and run php artisan migrate --seed.
  • Confirm the wiring: open composer.json and note the PSR-4 root mapped to src/ (e.g. "Src\\": "src/"). Then walk src/Domain → src/Application → src/Infrastructure and finally the provider in app/Providers (or src/Infrastructure/Providers) that binds repository interfaces to Eloquent implementations.
  • Hit one route end-to-end in the browser/Postman to prove the request path works.

Where to look first, in order:

  1. routes/ — the HTTP entry points.
  2. The controller for any route — it should show you the Application-layer service it calls.
  3. That service — it shows the Domain repository interface it depends on and the DTO it returns.
  4. The Infrastructure implementation of that interface — the only place Eloquent should appear.

First use case: trace a single existing flow (e.g. "create a resource") from route to database and back, drawing the four-layer chain on paper. Then reproduce that chain for your own first bounded context. That trace is the onboarding — the value of the repo is the convention, not the features.

Implementation Patterns

Organize by bounded context, then by layer. Rather than one giant Domain/, Application/ pair, nest per context (src/Domain/Billing, src/Application/Billing, …). The repo's flat layering is the template; keep it scoped as the app grows so contexts don't leak into each other.

Interface in Domain, implementation in Infrastructure. The single most important pattern to preserve:

// src/Domain/Order/Repositories/OrderRepositoryInterface.php
interface OrderRepositoryInterface {
    public function findById(OrderId $id): ?Order;
    public function save(Order $order): void;
}

// src/Application/Order/Services/CreateOrderService.php
final class CreateOrderService {
    public function __construct(private OrderRepositoryInterface $orders) {}
    public function execute(CreateOrderDto $dto): OrderDto { /* ... */ }
}

// bind in a provider
$this->app->bind(OrderRepositoryInterface::class, EloquentOrderRepository::class);

Controllers depend on Application services; Application services depend on Domain interfaces; only Infrastructure knows about Eloquent, HTTP, queues, or mail.

Keep Eloquent models and Domain entities separate. The Eloquent model (src/Infrastructure/.../Models/Order.php) is a persistence detail. It hydrates a Domain entity through a small mapper. This is the pattern developers drop first under deadline pressure and regret — once controllers accept an Eloquent model, the Domain layer is decorative.

Application services own transactions and orchestration. Wrap multi-repository writes in DB::transaction() at the service layer, dispatch domain events after commit (DB::afterCommit / Event::dispatch outside the closure), and return DTOs, never entities with lazy relations attached.

Validate at the boundary, enforce invariants in the entity. A FormRequest validates shape and authorization; value objects and entity constructors throw Domain exceptions for business rules. Map those exceptions to HTTP responses in a single listener/exception handler rather than try/catching in every controller.

Add a context with a checklist, so nothing is forgotten: Domain entity + value objects + repository interface + exceptions → Application DTOs + service → Infrastructure Eloquent model, mapper, repository impl, provider binding, migration → Presentation controller + request + resource → feature test.

Testing split: pure PHPUnit unit tests for Domain (no Illuminate\*, no DB — they should run in milliseconds); RefreshDatabase integration tests for Infrastructure repositories; HTTP feature tests for Presentation. That triad is the practical payoff of the layering, and it's worth enforcing in CI early.

Gotchas and Tips

  • It is a template with no upgrade path. There's no semver, no changelog you can rely on, and no upgrade tooling. Once you fork, you own the code. Expect to port the patterns to Laravel 10/11 yourself rather than merge anything upstream. The last commit is April 2024 and the repo targets Laravel 9, which is past security support — plan for that on day one, not month six.
  • Namespace drift when you scaffold. php artisan make:model puts files in app/Models with App\ namespaces. If you then move them under src/Infrastructure, update the namespace line and run composer dump-autoload -o. Half of all "class not found" issues in this layout are stale autoload maps.
  • Migrations vs. moved models. If the Eloquent model lives outside app/, set protected $table explicitly and make sure database/migrations still resolves. Migrations referencing App\Models\... after a move will break silently on migrate:fresh.
  • Binding scope. bind gives a fresh instance per resolution; singleton in a long-running worker (Octane, queue worker) will serve stale state — including cached aggregates. Use scoped for request/job-lifetime collaborators and audit any repository that caches internally.
  • Eloquent leakage is gradual. Watch for Illuminate\Support\Collection in Domain signatures, ->load(), ->toArray(), and Carbon in entities. If Domain code uses anything from Illuminate\, you've lost the testability argument. A one-line architectural test (grep for Illuminate\\ under src/Domain) in CI is cheap insurance.
  • N+1 through the mapper. A repository returning a list of entities will hydrate each one individually if the mapper touches relations. Eager-load in the query inside the repository, not in the domain object; the mapper should receive already-complete rows.
  • Domain exceptions need an HTTP story. Without a handler mapping, business-rule violations surface as 500s. Register one renderable/exception handler entry early.
  • Don't over-apply it. A settings table with two columns does not need an entity, a value object, a repository interface, and a mapper. Reserve the full stack for contexts with real invariants; use plain Eloquent elsewhere and say so in the PR.
  • Where to extend safely: the service provider bindings, a base AbstractEloquentRepository for shared CRUD plumbing, and an app/Support folder for cross-cutting helpers (transaction wrapper, exception mapper, DTO serializer). Those three give you leverage without compromising the layering.
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.
pharaonic/laravel-notification-dependencies
beautinow/translug
dev-brainiac/bot-cloaked
octoberrain/html
octoberrain/extension
octoberrain/exception
octoberrain/contracts
frankyso/ipaymu
3mdikram/worldmoneywords
snowsoft/config
itmosoft/laravel-subscriptions
larva/laravel-ai-qwen
adachsoft/api-version-bundle
cgv/localized-resource
noith/settings
kda/laravel-package
kda/filament-dynamic-navigation
bypersoft/signbox
pairus/product-data
fayne/laravel-filament-cms-core