orebarranco/filament-ddd-starter
A Laravel starter package that pairs Filament admin panel scaffolding with Domain-Driven Design conventions, helping you organize domain, application, and infrastructure layers into maintainable, scalable back-office projects. Useful for bootstrapping DDD-style Filament resources and reducing boiler...
This is a starter kit, not a library — you consume it once via composer create-project, then you own the code. There is nothing to composer require later, and upstream updates are not pulled in automatically.
composer create-project orebarranco/filament-ddd-starter my-app
cd my-app
cp .env.example .env && php artisan key:generate
php artisan migrate --seed
php artisan make:filament-user # or use the seeded admin
php artisan serve
First places to look, in this order:
composer.json — confirm the pinned Filament version (v1.0.3 ships Filament 5.10.2) and the PHP/Laravel constraints before you start writing code.app/Domain, app/Application, app/Infrastructure — exact names vary; run tree -L 3 app to confirm) to see how bounded contexts are separated and where the repository interfaces live.AppServiceProvider / any DomainServiceProvider — this is where interface→implementation bindings are declared and where you will spend most of your wiring time.First use case: add one trivial CRUD aggregate (e.g. Tag) by copying an existing slice rather than inventing structure. If you can add a domain entity + value object + repository interface + Eloquent repository + Action + Filament Resource without touching framework files outside the provider, the kit is doing its job.
Follow the slice, not the framework. The kit's whole premise is that Laravel's default app/Models + fat controllers is replaced by per-context layers. Practical rules that keep it coherent:
->save() on an Eloquent model inside a Resource, or business rules in ->rules([...]) beyond shape validation, the boundary is already leaking.__invoke() or execute(), one transaction boundary. Call them from Filament Resource pages (CreateRecord, EditRecord, custom Action closures), from queued jobs, and from artisan commands — the same Action should serve all three, which is the actual payoff of the layering.Illuminate\Database\Eloquent\Builder out of the domain. If a Filament table needs complex filtering, do it in an Infrastructure query object and expose a read DTO rather than reaching into the domain.string $email travel through three layers.Filament integration tips:
->discoverResources(in: app_path('Presentation/Filament/Resources'), for: 'App\\Presentation\\Filament\\Resources') — otherwise new Resources in a non-default namespace silently do not appear.discoverResources path and its own auth guard rather than one panel with canAccessPanel() branching everywhere.->using() on page classes to point at your Action instead of overriding handleRecordCreation() with glue code.Testing pattern: assert at the Action level with an in-memory or SQLite repository, and keep a thin set of Filament/Livewire tests for the UI. The layering only pays off if the domain tests do not boot the panel.
Versioning / Packagist history — read this before pinning. v1.0.2 was tagged onto v1.0.1's commit and Packagist never served it correctly; it cannot be fixed retroactively. v1.0.3 is the corrected release. Consequences:
1.0.2 in any scaffold script, CI matrix, or internal template — it is unresolvable or resolves to the wrong tree.https://github.com/orebarranco/filament-ddd-starter/compare/v1.0.1...v1.0.3.create-project, Composer's version constraint only affects the initial download. Record the starter version in your README; you will not get upgrade prompts.MFA password confirmation (Filament 5.10.2). Managing app-based multi-factor authentication now requires re-entering the password. Practical fallout:
auth.password_confirmed_at is stale get bounced to the password-confirmation screen mid-flow. That is intended, not a bug — do not "fix" it by removing the middleware.actingAs($user) alone is not enough for MFA-management routes. Seed the confirmation window:
$this->actingAs($user)->withSession(['auth.password_confirmed_at' => time()]);
DDD-specific pitfalls:
->toArray(), ->fresh(), ->load() in domain code is the most common regression. Grep for Illuminate\Database outside Infrastructure periodically.php artisan migrate ordering stays predictable and rollbacks are scoped.Debugging:
php artisan about plus the Filament version check first — mismatched Filament/Laravel versions after a manual composer update cause the majority of "panel won't render" reports in starter kits.php artisan filament:clear-cached-components && php artisan optimize:clear.canViewAny() before hunting routing.Extension points worth knowing: panel providers, the AppServiceProvider bindings block, Filament's ->using() on pages, custom Field/Column classes for VO casting, and domain event → listener registration. Those five cover most day-to-day customisation without forking the kit's structure.
How can I help you explore Laravel packages today?