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 Service Modules Laravel Package

shreifelagamy/laravel-service-modules

Laravel Service Modules helps organize Laravel applications into self-contained service modules, keeping code modular, maintainable, and easy to scale. It provides a structured way to group related functionality and register module components within a Laravel app.

View on GitHub
Deep Wiki
Context7

Getting Started

shreifelagamy/laravel-service-modules is a small, opinionated scaffolding package: it gives you an Artisan-driven way to carve a Laravel app into self-contained service modules (each with its own routes, migrations, views, providers) instead of letting everything pile into app/.

Install (v2.x):

composer require shreifelagamy/laravel-service-modules
php artisan vendor:publish --provider="Shreifelagamy\ServiceModules\ServiceModulesServiceProvider"

Where to look first, in order:

  1. composer.json of the package — confirm the illuminate/support constraint matches your Laravel version. v2.0.0 is the Laravel 12-compatible line; if you're on 10 or 11, stay on ^1.2.
  2. The published config file (config/service-modules.php or similar) — this is where the module base path and namespace are defined. Everything else derives from it.
  3. php artisan list filtered to the package's namespace — the generator commands are the whole product surface; the runtime footprint is thin.
  4. The src/ directory: read the service provider and the generator commands. Both are short, and reading them is faster than guessing at conventions from the README.

First use case — the one that justifies the install: pick a genuinely separable slice of your app (billing, notifications, an internal admin panel) and scaffold it as a module rather than a app/Services/Billing folder. If you can't name two or three such slices, you don't need this package yet.

Implementation Patterns

Module-per-bounded-context. The value shows up when a module owns all of its layers — controllers, models, migrations, routes, views, tests — under one directory. Half-migrating (module has controllers, models stay in app/Models) is the failure mode that makes these packages feel like overhead.

Register providers, don't hardcode paths. Each module should expose its own service provider, registered from the package's config or a bootstrap/providers.php entry. This is what lets you delete or disable a module cleanly:

// config/service-modules.php
'modules' => [
    'Billing'      => ['enabled' => true],
    'Notifications'=> ['enabled' => env('MODULE_NOTIFICATIONS', true)],
],

Feature-flagging modules per environment is the highest-leverage pattern here — it's how you keep a half-finished module deployed but inert.

Route and migration isolation. Point each module's provider at its own routes/web.php and database/migrations directory with a distinct migration path. Load migrations with loadMigrationsFrom() and name them with a module prefix (2024_01_01_000000_billing_create_invoices_table.php) so php artisan migrate output stays readable and rollbacks are traceable.

Cross-module communication via events, not calls. The temptation is app(NotificationsService::class) from inside Billing. Resolve to firing a domain event and letting the other module subscribe. This keeps modules deletable and is the difference between "modular" as a folder convention and modular as an architectural property.

Composer autoload for module namespaces. Add a PSR-4 mapping so module classes resolve without per-module requires:

"autoload": { "psr-4": { "Modules\\": "modules/" } }

Then composer dump-autoload after adding a module. Wire this into a post-autoload-dump script if you scaffold modules often.

Pair with a test suite per module. Modules that can't be tested in isolation aren't modules. Put modules/Billing/tests/ in phpunit.xml's testsuite list; Pest users can add it as a separate suite with its own TestCase.

Gotchas and Tips

v2.0.0 is a major bump for a reason. The changelog lists only "add support for laravel 12," but the 1.x → 2.x jump almost certainly drops Laravel 10 (and possibly 11) from the illuminate/* constraints. Before upgrading, check the actual composer.json diff on the tag — a semver-major for a support-matrix change means your CI matrix is the thing that breaks, not your application code. If you're mid-cycle on Laravel 11, pin ^1.2 and revisit at your next upgrade window.

Config cache is the classic footgun. Module paths and enabled-flags read from config will be stale after deploy if you don't php artisan config:cache (or, conversely, will ignore your local edits until you config:clear). Same for route caching with per-module route files — route:cache needs the module providers to have registered first, so verify provider order in bootstrap/providers.php if routes 404 in production but work locally.

Namespace mismatches after renaming a module. Generators bake the module name into namespaces, provider class names, and sometimes migration filenames. Renaming a directory by hand leaves dangling references. Prefer deleting and re-scaffolding, or grep for the old namespace across modules/, config/, and composer.json — all three, not just the first.

Migration ordering across modules. Filesystem ordering means modules migrate in directory-name alphabetical order. If Billing has a foreign key into Accounts' table, that will fail on a fresh database. Resolve it by (a) naming modules so dependency order matches alphabetical, (b) merging cross-module FKs into a later migration, or (c) using Schema::withoutForeignKeyConstraints(). Document the order — it's invisible until someone runs migrate:fresh on a clean machine.

Generator output is a starting point, not gospel. These packages emit boilerplate that reflects the author's conventions. Read the stubs once, then publish/override them if the package allows — otherwise every new module ships with a directory structure or namespace style you'll fight later. If stubs aren't publishable, that's a legitimate reason to fork or wrap the commands.

Don't over-modularize on day one. The overhead (extra providers, autoload entries, cross-module imports) is real and only pays off past a certain size. Start with one module you'd genuinely extract, prove the ergonomics, then expand. Retrofitting an existing app wholesale is the highest-risk way to adopt this.

Small package, small bus factor. Single-maintainer projects with infrequent releases deserve a pinned version in composer.json ("^2.0" is fine, but avoid dev-main) and a note in your upgrade runbook. The runtime surface is thin enough that forking is cheap if maintenance stalls — keep that in mind before letting module structure become load-bearing across a large codebase.

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.
novasa/laravel-language-center
opencage/geocode
adolfoholzer/parameters
livenetworks/ln-starter
shitoudev/permissionmanager
johannesclimacus/laravel-tui
hocvt/seotools
vitalybaev/sypexgeo
amphibee/themosis-ignition
gito-me/fast-excel
frankyso/laravel-ipaymu
geolize-studio/laravel-chile
boomerangui/chat
cgv/localized-resource
noith/settings
kda/laravel-package
kda/filament-dynamic-navigation
bypersoft/signbox
pairus/product-data
fayne/laravel-filament-cms-core