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

Modular Laravel Package

savannabits/modular

Savannabits Modular is a Laravel package for modular application development. It helps organize code into self-contained modules with their own routes, migrations, views, and service providers, making large projects easier to structure and maintain.

View on GitHub
Deep Wiki
Context7

Getting Started

modular is a module-system package for Laravel (from the Savannabits team, the same author as filament-modules), so the first thing to internalise is that it is a structure + autoloading package, not a framework of its own. It gives you a Modules/ (configurable) directory, a generator, and lifecycle commands so that each module owns its own routes, migrations, views, providers and translations.

Minimal setup:

composer require savannabits/modular
php artisan vendor:publish --tag=modular-config   # verify the tag name in the README

Then look at three things before writing any code:

  1. config/modular.php — this is the source of truth for where modules live, what paths get auto-registered (routes, migrations, views, translations), and the namespace prefix used for generated classes. Read it end to end; almost every "why isn't my route registered" question is answered here.
  2. The generated Modules/ directory after your first module — comparing a generated skeleton against the config is the fastest way to learn the conventions.
  3. The README's command list — run php artisan list and grep for the module namespace, since command names are the main API surface and are still shifting in the 0.1.x line.

First use case: generate a module, confirm the service provider is auto-discovered, add a route, and hit it.

php artisan module:make Blog        # confirm exact signature with `php artisan help`
php artisan module:list
composer dump-autoload

If the module's route responds, your wiring is correct and everything after that is conventional Laravel work inside a namespaced folder.

One caveat up front: 0.1.1 is docs and Pint formatting only — no functional change from 0.1.0. But the version number itself is the signal: this is pre-1.0 software. Treat the public API (command names, config keys, generated stub namespaces) as unstable and pin the version in composer.json rather than tracking ^0.1.

Implementation Patterns

Vertical slices per module. The productive pattern is to put a whole feature — model, migration, policy, controller, route file, Blade/Livewire or Filament resource, tests — inside one module, and keep app/ for genuinely shared concerns (base User model, service providers, cross-cutting middleware). Resist putting business logic in app/ and only controllers in modules; you lose the main benefit.

Autoloading is the integration point. The package registers each module's providers, route files, migrations, views and translations based on config. That means most integration "just works" and the failure mode when it doesn't is almost always a stale autoloader or a namespace mismatch. After every module:make, run composer dump-autoload — bake it into your muscle memory or a Composer script.

Cross-module communication: contracts and events, not direct model references. Two modules referencing each other's Eloquent models creates a dependency graph that defeats the purpose. Instead:

  • Define an interface in the consumer or in a small shared module, bind the implementation in the provider module's service provider.
  • Use Laravel events/listeners for "something happened in module A, module B cares" — the event class can live in the shared/shared-kernel module.
  • For read-only cross-module queries, expose a small query/service class from the owning module rather than handing out the model.

Migrations. Each module keeps its own migrations directory. Prefer running module:migrate per module during development so failures are attributable, and be deliberate about ordering — cross-module foreign keys are the classic modular-architecture trap. Either keep FKs within a module, or use a shared "core" module that migrates first. In CI, migrate everything in dependency order and assert it works from a clean database.

Per-module testing. Give each module its own tests/ directory with its own phpunit.xml (or a testsuite entry in the root config) so a module can be tested in isolation and, ideally, extracted later. This is the single highest-value habit in a modular codebase and the one most teams skip.

Filament / admin panels. If you're building admin UI, pair this with savannabits/filament-modules — it's from the same author and designed to discover Filament resources and pages inside modules. Keep panel registration in the module's provider so a module can be enabled/disabled without editing central config.

Enable/disable as a deployment lever. The lifecycle commands are useful beyond local dev: gating a module behind an enable flag lets you ship code dark and turn features on per environment. If you use this, make sure your migrations and config are safe to run while a module is disabled — a disabled module whose migrations ran is a subtle and confusing state.

Code style. 0.1.1 applied Pint to the package's own source. Mirror that in your app: run Pint across Modules/ too (it's easy to exclude that path by accident when your pint.json scopes to app/). Add Modules to your Pint paths explicitly.

Gotchas and Tips

  • "My module isn't loading." Work the checklist in order: (1) composer dump-autoload; (2) namespace in the generated class matches config/modular.php; (3) provider listed/discovered correctly; (4) php artisan config:clear — a cached config with a different modules path is a genuinely nasty one; (5) module:list to confirm the package sees the module at all.
  • Config caching in production. Because paths and namespaces come from config, any deploy that caches config must cache it after the module directory exists and is correct. Building a container image with config:cache baked in and then mounting modules elsewhere will silently break discovery.
  • Namespace collisions with App\. Generated modules use their own root namespace (e.g. Modules\Blog\...). Don't half-migrate a class into a module — a model split across App\Models and Modules\Blog\Models with the same table name is a debugging afternoon you don't want.
  • Route caching. route:cache requires all module route files to be resolvable and closure-free. Test php artisan route:cache && php artisan route:clear in CI; modular route registration is a common place for this to break only in production.
  • Migration ordering across modules. There's no global ordering guarantee you should rely on. If module B needs module A's table, express it via a shared core module or a documented boot order, and add an integration test that migrates from scratch.
  • Version instability. 0.1.x means the generator stubs, command signatures and config keys can change between patch releases. Pin exactly ("savannabits/modular": "0.1.1") and read the diff of the package source on upgrade, not just the release notes — 0.1.1's notes ("README, Pint") say nothing about stub or config changes, so verify rather than assume.
  • Debugging discovery. When you need to see what the package actually resolved, dump the container bindings or the registered providers (php artisan about is a good first stop in recent Laravel versions) rather than guessing from the config file. The gap between "what config says" and "what got registered" is where the bug lives.
  • Extension points worth knowing. The config file itself (paths, namespaces, auto-registration toggles) is the primary extension surface; published generator stubs are the second — customise stubs once so every new module starts compliant with your team's conventions (strict types, readonly DTOs, a tests/ folder, an Actions/ directory, whatever your house style is). Doing this at the start of a project is far cheaper than retrofitting fifty modules later.
  • Don't modularise prematurely. A three-module app that could have been a well-organised app/ is a net cost. Adopt this when you have a genuine boundary (a domain you might extract, a team boundary, an optional/pluggable feature), not because the word "modular" sounds like architecture.
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.
jinas/dhivatar
store-doc/laravel-storage-driver
plus-it/laravel-translation-manager
diephp/perhaps
diephp/telegraph
osmaviation/spreadsheet
microsoft/fluentui-system-icons
mmccook/php-json-canonicalization-scheme
middlewares/firewall
qubiqx/montapacking
nyoncode/laravel-package-console
pnscripts/filament-broadquill-lite
pnscripts/laravel-sentfold-lite
vleap/mx-sdk-laravel-internal
twidpay/utils
htetmyathlaing/scout-elasticsearch-driver
artisan-toolbox/front-awesome
ls/client-assistant
users-au/laravel-client
cgv/localized-resource