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

Laravel Domain Oriented Laravel Package

adhenrique/laravel-domain-oriented

A Laravel package for organizing applications by domain rather than technical layers, helping group models, actions, and services into modular domain-oriented structures for clearer boundaries and more scalable codebases.

View on GitHub
Deep Wiki
Context7

Getting Started

This is a thin scaffolding convention, not a framework extension, so the fastest path to understanding it is reading src/ and stubs/ rather than expecting docs. The README is essentially a two-line description plus a changelog.

composer require adhenrique/laravel-domain-oriented
php artisan list | grep -i domain        # discover the generator commands
php artisan vendor:publish --tag=domain  # publish config/stubs if provided

Order of inspection after install:

  1. Config — find the published config (likely config/domain-oriented.php) and note the namespace root and folder layout it expects, e.g. App\Domains vs Domains. If it is not App\Domains\..., add a matching PSR-4 entry to composer.json and composer dump-autoload before scaffolding anything.
  2. Generator commands — the package's primary value. Look for something like make:domain, make:domain-controller, make:domain-service. Run one against a throwaway name and read what it emits; the generated files are the documentation for the intended base classes.
  3. Base classes in src/ — specifically ValidateService (getRules() became public in v2.1.1, which tells you the intended call site) and the Resource base (a field-name typo was fixed in v2.1.1 — check whether your generated stubs reference the old or new name).

First use case: scaffold exactly one thin vertical slice against a real endpoint — e.g. Billing/Invoices with a GET index. Generate the domain, put the route in the domain's routes file, write a single ValidateService rule set, and confirm the full request → Controller → Service → Resource path returns correct JSON. Do not generate a second domain until this one works end-to-end; a package this small is either obvious within one slice or not worth the integration cost.

Implementation Patterns

Generator-driven vertical slices. The day-to-day workflow is: make:domain X to create the bounded context, then per-artifact generators to add a controller, service, repository, model, event, or resource. Resist hand-writing files into the domain tree unless you have already matched the stub output — mismatched base classes are the most common way this convention degrades.

Domain owns its routes and providers. Each domain is self-contained: a routes file, a service provider, and its own migrations. Register the domain provider explicitly in config/app.php (or rely on auto-discovery if the package scans a configured directory). Keep the domain provider doing only two things — route registration and container bindings — so the domain stays removable.

// app/Domains/Billing/Providers/BillingServiceProvider.php
public function boot(): void
{
    $this->loadRoutesFrom(__DIR__.'/../Routes/api.php');
    $this->loadMigrationsFrom(__DIR__.'/../Database/Migrations');
}

Validation lives in the domain, not in FormRequests. Use the ValidateService base: extend it, override getRules(), and call it from the controller (or inject it). Since getRules() is public as of v2.1.1, you can also compose rule sets in tests or reuse them across two entry points.

class CreateInvoiceValidateService extends ValidateService
{
    public function getRules(): array
    {
        return [
            'customer_id' => ['required', 'uuid', 'exists:customers,id'],
            'amount'      => ['required', 'integer', 'min:1'],
        ];
    }
}

Cross-domain is service-to-service, never model-to-model. The whole point of the layout is bounded contexts. If Billing needs customer data, inject a Customers service (or listen to an event the Customers domain emits) rather than reaching into App\Domains\Customers\Models\Customer. This is the single discipline that makes the structure pay off; the package itself enforces nothing here.

Migrate opportunistically. When you touch an existing controller, move that slice into a domain rather than scheduling a restructure. Big-bang migration across a mature app/Http/Controllers tree is where these adoptions die.

Gotchas and Tips

  • Version ceiling is real. Last release January 2022 puts this in the Laravel 8/9, PHP 7.4–8.1 window. Check composer.json constraints first; on Laravel 10+ expect composer require to fail or the base classes to break. Do not start an upgrade to Laravel 10 while still consuming this package upstream.
  • Autoloader drift when you change the namespace. The config's namespace root and your composer.json PSR-4 map must agree. A generator that writes App\Domains\... into a project whose autoloader does not map it produces classes that resolve in dev (via classmap scan) and fail elsewhere. composer dump-autoload after every namespace/config change.
  • Loose semver. v2.1.1 was labelled a patch but renamed a Resource field and widened getRules() visibility. Pin the exact version ("2.1.1", not ^2.1) and avoid depending on Resource field names in serialized API output you cannot re-test.
  • Migration ordering across domains. Per-domain loadMigrationsFrom merges everything into one chronological run. Two domains adding migrations in the same minute can produce non-deterministic order on a fresh migrate. Keep foreign-key-creating domains' migrations named so timestamps don't collide, and prefer php artisan migrate --path=app/Domains/X/Database/Migrations when iterating on one domain.
  • No guardrails. Nothing stops a developer bypassing ValidateService or crossing domain boundaries. Pair adoption with deptrac or PHPStan layered rules, or the convention erodes within a quarter or two.
  • Explicit registration beats auto-discovery. If you fork or vendor the base classes, drop auto-discovery entirely and register the domain providers in config/app.php. Auditability is worth more than the convenience here.
  • Debugging routes: php artisan route:list --path=billing to confirm a domain's routes actually registered — a missing loadRoutesFrom call is silent. If routes vanish after a config change, run php artisan config:clear; the package's namespace config is often cached.
  • Extension point: vendor it. MIT-licensed, ~a few hundred lines. Copying the base classes and stubs into app/Domain/Support/ (with attribution) removes the dependency, lets you target your PHP version, and makes upgrades a non-event. For a package in this adoption state, vendoring is usually the correct first move rather than the fallback.
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.
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
coleus/apps
seampact/laravel
emmyconceptng/laravel-ng-postcode
jjsquady/round-robin