模块化架构(DDD)
简介
用 Filament 构建大型应用时,你可能希望按领域驱动设计(DDD)组织代码,将应用拆成自包含模块。本指南说明如何将 Filament 与 InterNACHI/Modular 等模块化架构包集成。
模块化方式
在模块化架构中,应用的每个领域都是独立的 Composer 包,通常放在 app-modules/ 目录下。每个模块包含自己的:
- 模型与业务逻辑
- Filament resources、pages 与 widgets
- Service provider
- 路由、视图与配置
- 测试
这种方式有多项好处:
- 领域之间职责清晰分离
- 团队协作更容易(不同团队可负责不同模块)
- 更好的可测试性与可维护性
- 可在多个项目间复用模块
设置 InterNACHI/Modular
首先安装 modular 包:
composer require internachi/modular创建新模块:
php artisan make:module alerts这会搭建如下模块结构:
.
+-- app-modules
| +-- alerts
| | +-- composer.json
| | +-- src
| | | +-- Providers
| | | | +-- AlertsServiceProvider.php
| | +-- routes
| | +-- resources
| | +-- database
| | +-- tests配置模块的 composer.json
每个模块应依赖 filament/filament 并定义自己的 service provider:
{
"name": "my-app/alerts",
"type": "library",
"require": {
"filament/filament": "^5.0"
},
"autoload": {
"psr-4": {
"Modules\\Alerts\\": "src/"
}
},
"extra": {
"laravel": {
"providers": [
"Modules\\Alerts\\Providers\\AlertsServiceProvider"
]
}
}
}为模块创建 Filament 插件
每个模块应定义自己的 Filament 插件,用于注册其 resources、pages 与 widgets:
namespace Modules\Alerts;
use Filament\Contracts\Plugin;
use Filament\Panel;
class AlertsPlugin implements Plugin
{
public function getId(): string
{
return 'alerts';
}
public static function make(): static
{
return app(static::class);
}
public function register(Panel $panel): void
{
$panel
->discoverResources(
in: __DIR__ . '/Filament/Resources',
for: 'Modules\\Alerts\\Filament\\Resources',
)
->discoverPages(
in: __DIR__ . '/Filament/Pages',
for: 'Modules\\Alerts\\Filament\\Pages',
)
->discoverWidgets(
in: __DIR__ . '/Filament/Widgets',
for: 'Modules\\Alerts\\Filament\\Widgets',
);
}
public function boot(Panel $panel): void
{
//
}
}按面板有条件地注册插件
当你有多个面板(如 admin、app、portal)时,通常希望某些模块只对特定面板注册插件。可在模块的 service provider 中使用 Panel::configureUsing() 有条件地注册插件。
基础条件注册
要为除某一个之外的所有面板注册插件:
namespace Modules\Alerts\Providers;
use Filament\Panel;
use Illuminate\Support\ServiceProvider;
use Modules\Alerts\AlertsPlugin;
class AlertsServiceProvider extends ServiceProvider
{
public function register(): void
{
Panel::configureUsing(function (Panel $panel): void {
if ($panel->getId() !== 'admin') {
return;
}
$panel->plugin(AlertsPlugin::make());
});
}
}用 match 语句处理多个面板
若需为特定面板注册插件,或按面板做不同配置,可使用直接调用 $panel->plugin() 的 match 语句:
namespace Modules\Alerts\Providers;
use Filament\Panel;
use Illuminate\Support\ServiceProvider;
use Modules\Alerts\AlertsPlugin;
class AlertsServiceProvider extends ServiceProvider
{
public function register(): void
{
Panel::configureUsing(function (Panel $panel): void {
match ($panel->getId()) {
'admin' => $panel->plugin(
AlertsPlugin::make()->enableAdminFeatures(),
),
'staff' => $panel->plugin(
AlertsPlugin::make(),
),
default => null,
};
});
}
}这样可按面板为每个插件实例做不同配置;未匹配到的面板则不会收到该插件。
模块目录结构
组织良好、已集成 Filament 的模块可能如下所示:
.
+-- app-modules
| +-- alerts
| | +-- composer.json
| | +-- config
| | | +-- alerts.php
| | +-- database
| | | +-- factories
| | | +-- migrations
| | | +-- seeders
| | +-- resources
| | | +-- views
| | | | +-- filament
| | | | | +-- pages
| | +-- routes
| | | +-- web.php
| | +-- src
| | | +-- AlertsPlugin.php
| | | +-- Filament
| | | | +-- Pages
| | | | +-- Resources
| | | | | +-- Alerts
| | | | | | +-- AlertResource.php
| | | | | | +-- Pages
| | | | | | | +-- CreateAlert.php
| | | | | | | +-- EditAlert.php
| | | | | | | +-- ListAlerts.php
| | | | +-- Widgets
| | | +-- Models
| | | | +-- Alert.php
| | | +-- Providers
| | | | +-- AlertsServiceProvider.php
| | +-- tests在面板间共享 resources
有时希望同一 resource 出现在多个面板中,但配置不同。可通过 resource discovery 并结合按面板的自定义来实现:
namespace Modules\Users;
use Filament\Contracts\Plugin;
use Filament\Panel;
use Modules\Users\Filament\Resources\UserResource;
class UsersPlugin implements Plugin
{
protected bool $canManageRoles = false;
public function getId(): string
{
return 'users';
}
public static function make(): static
{
return app(static::class);
}
public function canManageRoles(bool $condition = true): static
{
$this->canManageRoles = $condition;
return $this;
}
public function hasRoleManagement(): bool
{
return $this->canManageRoles;
}
public function register(Panel $panel): void
{
$panel->resources([
UserResource::class,
]);
}
public function boot(Panel $panel): void
{
//
}
}然后按不同能力注册:
Panel::configureUsing(function (Panel $panel): void {
match ($panel->getId()) {
'admin' => $panel->plugin(
UsersPlugin::make()->canManageRoles(),
),
'staff' => $panel->plugin(
UsersPlugin::make(),
),
default => null,
};
});INFO
Panel::configureUsing() 很强大:模块可自行配置,无需改面板 provider 文件。添加或移除模块时,其 Filament 集成会自动处理。
从模块注册 Livewire 组件
若模块包含 Filament 使用的自定义 Livewire 组件(如自定义页面或 widgets),可在插件的 boot() 方法中注册:
use Livewire\Livewire;
use Modules\Alerts\Filament\Pages\AlertsDashboard;
public function boot(Panel $panel): void
{
Livewire::component('alerts-dashboard', AlertsDashboard::class);
}