dem1-off/laravel-modular
Composer 安装命令:
composer require dem1-off/laravel-modular
包简介
Modular architecture tools for Laravel — DDD modules that promote to standalone packages with zero code churn
README 文档
README
Modular architecture tooling for Laravel. DDD modules (Domain / Application / Infrastructure) that are real Composer packages from day one, so any module can be promoted to a standalone package with zero code churn.
Highlights
- Attribute-driven wiring — declare bindings and listeners with
#[Bind]and#[Listen], or let an implementation auto-bind itself with#[Provides](Symfony-style autoconfigure for Laravel modules). Config, migrations, views and routes load by convention, so the common provider is empty. - Fast by design —
module:cachecompiles discovery and attributes into one PHP file (zero reflection, zero filesystem scanning at runtime), wired intophp artisan optimize. - A designed-in promotion path — module namespaces never change, so moving a module into its own repo is a Composer change, not a refactor.
- Familiar layout — works with the
Modules/directory,module.json,modules_statuses.json, andmodule_path()conventions, so existing projects interoperate.
A module at a glance
#[Bind(PostRepositoryInterface::class, EloquentPostRepository::class)] #[Listen(ChapterPublished::class, SendDigest::class)] final class BlogServiceProvider extends ModuleServiceProvider {}
Installation
composer require dem1-off/laravel-modular php artisan vendor:publish --tag=modules-config
config/modules.php uses conventional module config keys (namespace,
paths, statuses_file), so an existing modular project migrates without
editing modules.
Creating a module
php artisan make:module Blog
Produces a promotion-ready package:
Modules/Blog/
├── composer.json # type: laravel-module, PSR-4 Modules\Blog\
├── module.json # module manifest
├── config/blog.php
├── database/{migrations,factories,seeders}/
├── resources/views/
├── src/{Domain,Application,Infrastructure}/
│ └── Infrastructure/Providers/BlogServiceProvider.php
└── tests/
Configuring a module
Convention loads, attributes wire. Config, migrations, views and routes load automatically when their folders exist. Declare container bindings and listeners with attributes:
use Dem1Off\LaravelModular\Module\Attributes\Bind; use Dem1Off\LaravelModular\Module\Attributes\Listen; use Dem1Off\LaravelModular\Module\ModuleServiceProvider; #[Bind(PostRepositoryInterface::class, EloquentPostRepository::class)] #[Bind(FeedCache::class, RedisFeedCache::class, singleton: true)] #[Listen(ChapterPublished::class, SendDigest::class)] final class BlogServiceProvider extends ModuleServiceProvider {}
Need more than attributes? Override register()/boot() and call the parent —
it's a normal Laravel provider. See the
docs.
Performance
Attributes reflect in development. In production, php artisan module:cache
compiles discovery and attributes into one PHP file — a request does zero
reflection and zero filesystem scanning. It's wired into php artisan optimize.
Runtime API
use Dem1Off\LaravelModular\Facades\Modules; Modules::all(); // every module, keyed by name Modules::enabled(); // only enabled ones Modules::find('Blog'); // ModuleDescriptor|null Modules::isEnabled('Blog'); Modules::path('Blog'); // absolute path module_path('Blog', 'resources/views'); // path helper
Customising behaviour
A module provider is a normal Laravel ServiceProvider. For anything beyond
attributes, override register()/boot() and call the parent:
public function boot(): void { parent::boot(); Livewire::component('blog.feed', Feed::class); }
Keep anything proprietary (navigation, mailing, metrics, …) in your application,
invoked from the module's boot() — never inside this package.
Promoting a module to a standalone package
Because a module is already a Composer package and its namespace
(Modules\Blog\) never changes, promotion is mechanical:
-
Move the directory to its own git repo
git subtree split --prefix=Modules/Blog -b blog-module # push that branch to a new repo, or copy Modules/Blog out -
Point the app at it via Composer — swap the path entry for a VCS/version constraint in the root
composer.json:// before — local development "repositories": [{ "type": "path", "url": "Modules/*" }], "require": { "acme/blog-module": "*" } // after — promoted package "repositories": [{ "type": "vcs", "url": "git@github.com:acme/blog-module.git" }], "require": { "acme/blog-module": "^1.0" }
-
composer update acme/blog-module— done.
No namespace changes, no provider rewrites: Laravel package auto-discovery reads
extra.laravel.providers from the module's own composer.json, exactly as it
did in-app. Tests, static-analysis config, and the module_path() helper all
keep working because the module's internal layout is unchanged.
Tip: develop with
"type": "path"+"url": "Modules/*"and"symlink": trueso in-app modules and promoted packages behave identically during development.
License
MIT.
统计信息
- 总下载量: 0
- 月度下载量: 0
- 日度下载量: 0
- 收藏数: 0
- 点击次数: 3
- 依赖项目数: 0
- 推荐数: 0
其他信息
- 授权协议: MIT
- 更新时间: 2026-06-20