internachi/modular
internachi/modular is a module system for Laravel applications. It lets you create modules in an app-modules/ directory using Composer path repositories for autoloading and Laravel package discovery for initialization, plus minimal tooling to fill the gaps.
composer require internachi/modular. Laravel package discovery registers the service provider automatically; there is no manual provider wiring step.php artisan vendor:publish --tag=modular-config. The single most important setting is the module namespace. The default Modules\ works, but choose your organization namespace (e.g. Acme\) before you create real modules — retrofitting namespaces across dozens of modules later is painful.php artisan make:module billing. This creates app-modules/billing/ with composer.json, src/, tests/, routes/, resources/, and database/, and edits the root composer.json to add a path repository plus a modules/billing:* requirement.composer update modules/billing as prompted. The module is not autoloadable until this runs — this is the step people forget.php artisan modules:sync once to wire up phpunit.xml (adds a Modules test suite) and PhpStorm config. It is idempotent and safe to re-run, so add it to post-autoload-dump in the root composer.json.composer.json (namespace + dependency contract) and its service provider under src/. Then php artisan modules:list to confirm what the package actually sees.app/ — Billing, Notifications, Reporting — and move its models, migrations, factories, policies, listeners, and routes into a module. Because the module is a normal Composer path repository and a normal Laravel package, everything inside keeps using stock Laravel conventions. The value is organizational, not a new runtime paradigm.app/. Use the --module= flag on the familiar generators: make:model Invoice --module=billing, make:controller InvoiceController --module=billing, make:migration, make:factory, make:policy, make:listener, make:command, make:test, etc. Custom stubs still apply, so the generated code matches your house style. This is the primary day-to-day workflow — you rarely hand-create files.php artisan migrate; factory() resolves module factories; policies attach to module models. Treat the module's service provider as the place for the few things that genuinely need explicit registration (routes, views, custom bindings).<x-billing::invoice-card />, <x-billing::nested.one />, and anonymous components mirror their file paths. Translations use __('billing::messages.welcome'). This keeps cross-module template references explicit and prevents accidental coupling.php artisan db:seed --module=billing targets Acme\Billing\Database\Seeders\DatabaseSeeder; adding --class=InvoiceSeeder --module=billing resolves inside the module's Database\Seeders namespace. Use this in local setup and test fixtures rather than reaching into module internals from the root seeder.app-modules/billing/tests/ and run under the Modules suite added by modules:sync. Because Composer does not support autoload-dev for non-root packages, tests are autoloaded from the module's main autoload block — structure test namespaces accordingly and don't expect autoload-dev semantics.modules/core in Billing's composer.json and import the class. Do not reach across module directories with relative includes. Keeping dependencies explicit in each module's composer.json is what makes future extraction to a standalone package realistic.app-modules.php) and edit the stubs with the provided placeholders (StubModuleNamespace, StubClassNamePrefix, StubMigrationPrefix, StubFullyQualifiedTestCaseBase, etc.). Teams that skip this end up hand-fixing every generated file; teams that do it get consistent modules for free.php artisan modules:cache during deploy to cache module discovery, and php artisan modules:clear when adding or removing modules locally. Pair with modules:sync in post-autoload-dump so phpunit.xml and IDE config never drift.app/, and let both coexist. Modules are just path-repository packages; there is no all-or-nothing commitment.make:module is a two-step operation. It edits the root composer.json but does not run Composer. Until you run composer update modules/<name>, autoloading fails with confusing class-not-found errors. Any CI or onboarding script that scaffolds modules must run the update.composer.json, every namespace declaration, and the root path require entries. Decide the organization namespace on day one.modules:clear (or re-run modules:cache) if discovery looks stale. If modules:list disagrees with the filesystem, clear the cache and composer dump-autoload before debugging anything else.modules:sync is additive and idempotent. It will add missing phpunit.xml suite entries and PhpStorm config, and it now marks modules as excluded paths in PhpStorm to avoid double-registration in the IDE. Re-run it after changing editor or test configuration; it won't clobber existing settings.make:model --all is only partially supported. Don't assume it generates the full Laravel set (migration, factory, seeder, controller, policy) inside a module. Generate the pieces explicitly with separate --module= calls when correctness matters.db:seed --module expects the module's seeder namespace. Passing a bare class name resolves under Acme\<Module>\Database\Seeders. If seeding silently fails, check the namespace assumption first.routes/, but routes are not in the documented auto-discovery list. Verify the generated service provider and register route files explicitly if needed — this is the most common gap when a new module's endpoints 404.StubMigrationPrefix to keep names distinguishable; be careful when a module migration alters a table created by another module, since ordering is timestamp-based.nwidart/laravel-modules is the better fit; choose Modular for organization.composer.json, confirm modules/<name> is required, run composer update modules/<name>, run modules:clear, run composer dump-autoload, then modules:list. That sequence resolves nearly every discovery issue.composer.json before chasing anything else.composer.json as a contract. If you hope to extract a module into a standalone package later, keep its dependencies complete and avoid referencing app-specific helpers. The organization namespace plus an honest dependency list is the whole extraction strategy — the package's conventions do the rest.How can I help you explore Laravel packages today?