Every Laravel application starts out tidy. Then it grows, and one day app/Http/Controllers has eighty files in it, billing sits beside the blog, the blog sits beside the support desk, and the only thing the files of a feature share is a prefix in their names.
Laravel sorts code by what it is: controllers here, models there, migrations somewhere else. That is perfect for a small application. For a large one you want to sort by what the code is for. Everything the blog needs, routes, controllers, models, migrations, views, tests, in one directory that you can read on its own, hand to a colleague, delete, or move into a package.
That is all a module is. And Laravel has every part you need to build one: a service provider can load routes, views, translations and migrations from any directory. A module system is mostly an agreement about where those calls are made. So the real question about a module package is how much machinery it puts around that agreement.
Here is what I wanted from one: Open one file and see which modules the application has, and in which order. Open one class and see everything a module contributes. Nothing to rebuild or clear when something does not show up. A module that is a directory of ordinary Laravel code, not a small package with a composer.json of its own. A module installed with Composer that works exactly like one in my own repository.
These expectations come from years of building products. Django, with its apps, is a great example of a modular architecture. Every time I started on a large product in anything, I ended up building something similar by hand. None of the Laravel packages I tried gave me all five points, so I wrote Laramod.
Two packages cover most of what people use today. Both are good work that the community has leaned on for years, and Laramod takes inspiration from them. They made choices that suit many teams, but I wanted something simpler: modules that plug in with one line, and code that is as easy to use from another module as from the application itself.
It has followed the framework from Laravel 5.4 to 13. Its documentation puts the idea in one sentence: "Each module is a mini Laravel application with a predictable structure."
And that is what you get. A generated module has a module.json, a composer.json, a vite.config.js and its own service providers, a BlogServiceProvider and a RouteServiceProvider among them. Modules are switched on and off with module:enable and module:disable, the state lives in modulesstatuses.json. Since v11 the modules are autoloaded through the Wikimedia Composer merge plugin, which merges the composer.json of every module into the application's. Files are generated with a command family of its own, module:make-model, module:make-controller and so on.
It is mature, it is documented, and nearly every question about it has been answered somewhere already. The price is weight. A module is a lot of files before you have written a line, and the truth about a module is spread over several of them: module.json, the status file, composer.json, the providers. When a route is missing, there are many places to look.
Modular is the lighter take, and I like it a lot. It does not invent a module system, it uses the two that are already there: a module is a real Composer package in app-modules/, pulled in through a path repository, and it is booted by Laravel's package discovery.
make:module writes the module, adds a path repository and a require to your composer.json, and reminds you to run composer update. From then on, conventions do the work: commands, migrations, factories, policies, Blade components and event listeners are discovered on their own, and modules:cache makes that discovery faster. Its best idea is that the stock generators take a --module= option, php artisan make:model Post --module=blog, instead of a second family of commands. I took that idea gladly.
The price here is different. Every module still has its own composer.json, a src/ directory and a service provider. A new module is a Composer operation. And what a module contributes is decided by convention, so the answer to "what does this module provide?" is its directory tree, plus a cache that may or may not be fresh.
That last point is what the two have in common. Both answer the question by looking at the file system. Laramod answers it by looking at a class.
That is the whole mechanism. There is no auto-discovery, no manifest, no status file, no cache and no composer.json per module. The order of the list is the order the modules are wired in. To switch a module off, you remove its line.
A module gains a capability by implementing a contract: ProvidesRoutes, ProvidesApiRoutes, ProvidesGlobalMiddlewares, ProvidesMigrations, ProvidesSeeders, ProvidesCommands, ProvidesViews, ProvidesTranslations, ProvidesConfig, ProvidesViteEntries, and Ordered for a module that has to come first or last.
Nothing is looked up on disk. A module without ProvidesViews has no views, whatever its directories contain. It sounds strict, and it is the point: the class line of a module is its table of contents, your editor can jump to every part of it, and "why is this not loaded?" has exactly one place to look.
Views, translations and Blade components use the module's name as their namespace, the way packages always have:
composer require protibimbok/laramod and php artisan laramod:init prepare the application: the Modules/ directory, one PSR-4 line in composer.json, the module test suites in phpunit.xml. php artisan make:module Blog writes a module and lists it.
