Skip to content

Beta 模块长期标准结构

适用范围:beta/app/Modules/*

目标:统一 beta 侧业务模块的目录结构、资源归属和加载方式,避免继续回到“代码在模块里、视图/路由散在全局目录”的平铺模式。


1. 总原则

1.1 模块内聚

业务模块自己的内容,原则上都放在:

text
beta/app/Modules/<ModuleName>/

包括但不限于:

  • Http/Controllers
  • Http/Requests
  • Http/Resources
  • Http/Middleware
  • Services
  • Repositories
  • Models
  • DTOs
  • Support
  • Extensions
  • views
  • config
  • module routes
  • 与模块相关但统一存放的 migrations

1.2 全局目录只留平台级共享能力

以下内容才应放在全局目录:

  • app/Foundation/*
  • 全局中间件
  • 全局异常处理
  • 全局布局(layout)
  • 跨模块共享组件
  • 平台级公共服务

换句话说:

模块专属的,尽量留在模块内;跨模块共享的,才放全局。

1.3 过渡结构允许存在,但不作为长期目标

下面这类目录可以在迁移过渡期保留:

  • beta/resources/views/modules/*
  • beta/routes/modules/*

但它们不应作为最终形态。长期应逐步收敛回模块目录。


2. 推荐标准结构

FileManagement 为例:

text
beta/
├── app/
│   ├── Foundation/
│   └── Modules/
│       └── FileManagement/
│           ├── src/
│           │   ├── Http/
│           │   │   ├── Controllers/
│           │   │   ├── Requests/
│           │   │   ├── Resources/
│           │   │   └── Middleware/
│           │   ├── Services/
│           │   ├── Repositories/
│           │   ├── Models/
│           │   ├── DTOs/
│           │   ├── Support/
│           │   ├── Extensions/
│           │   └── Providers/
│           │       └── FileManagementServiceProvider.php
│           ├── tests/
│           │   ├── Unit/
│           │   └── Feature/
│           ├── routes/
│           │   ├── web.php
│           │   └── api.php
│           ├── resources/
│           │   └── views/
│           │       ├── index.blade.php
│           │       ├── form.blade.php
│           │       ├── show.blade.php
│           │       ├── logs/
│           │       ├── exports/
│           │       └── partials/
│           ├── config/
│           │   └── filemanagement.php
│           └── README.md
├── database/
│   ├── migrations/
│   └── seeders/
├── resources/
│   └── views/
│       └── layouts/
└── routes/
    └── beta.php

3. 各目录职责

3.1 Http/Controllers

只负责:

  • 接收 HTTP 参数
  • 调用 Service
  • 返回 View / JSON / File Response

不要把复杂业务判断长期堆在 Controller 里。

3.2 Http/Requests

负责参数验证、格式约束、基本边界控制。

3.3 Services

负责业务编排,是模块核心逻辑层。

建议区分:

  • Query Service:查
  • Command Service:写
  • 场景型 Service:预览、导出、打印、日志等

3.4 Repositories

负责数据访问细节:

  • 查询条件拼装
  • Eloquent / Query Builder 查询
  • 数据持久化封装

3.5 Models

负责模块领域实体和表映射。

3.6 DTOs

用于明确 Controller、Service、Repository 之间的内部数据传输形状,避免复杂流程长期传递含义不清的散装数组。DTO 不负责定义对外 JSON 格式。

3.7 Http/Resources

用于定义稳定的 JSON 输出契约,包括字段选择、字段改名、嵌套关系和集合输出。简单的模块内部 AJAX 可以直接返回 JsonResponse,不要求为每个响应创建 Resource。

这里不能使用模块根目录的 Resources/:它会与存放视图和静态资源的 resources/ 在大小写不敏感文件系统上冲突。

3.8 Http/Middleware

只放模块专属 HTTP 中间件,并由模块 Provider 注册别名。跨模块公共中间件才放全局 app/Http/Middleware

3.9 Support

放模块内部通用辅助类,例如:

  • Breadcrumbs
  • PathResolver
  • Formatter
  • Mapper

3.10 Extensions

放扩展点、插件点和可替换策略接口,例如:

  • ActionRegistry
  • PreviewPolicyInterface
  • DownloadPolicyInterface

3.11 resources

当前模块目录只自动加载 Blade 视图:

  • resources/views

模块 JS/CSS 不会被 Vite 自动发现,应由 Beta 根目录 resources/jsresources/css 入口显式引入。语言文件在建立模块翻译加载约定前继续放根 lang,脚手架不预建无加载链路的目录。

3.12 config

模块配置分为三种:

  • features.php:可独立启停的完整业务能力。
  • settings.php:站点可维护的运行配置定义。
  • <module-key>.php:Laravel config() 运行配置,仅在模块确实需要时创建。

ModuleServiceProvider 默认不加载运行配置;只有真实存在 <module-key>.php 的模块才覆盖 moduleConfigKey()

3.13 database

数据库迁移、Seeder 和 Factory 统一放在 Beta 根目录 database,沿用 Laravel 默认加载链路。文件名和类名体现业务模块归属,不在各 Module 下另建一套未被加载的数据库目录。

3.14 分层使用边界

目录结构用于明确代码归属,不代表每个接口都必须使用完整分层。

  • 简单下拉、状态查询和模块内部 AJAX:Controller 可直接调用简单查询并返回 JsonResponse。
  • 普通增删改查:写入参数先校验;有业务规则或事务时再引入 Service。
  • DTO:字段较多、跨多层传递或数组含义不清时使用。
  • Repository:复杂查询需要复用,或需要隔离遗留数据访问细节时使用。
  • Resource:公开接口、分页列表或 JSON 契约需要长期稳定时使用。
  • 审批、委托生成任务等复杂流程:按需要组合 Request、DTO、Service、Repository 和 Resource。

禁止为了填满目录而创建只做一层转发的空类。新增抽象应能减少重复、固定边界或承载明确业务规则。


3.15 模块启用契约

模块是否启用,以 bootstrap/providers.php 中是否显式注册其 ModuleServiceProvider 为唯一事实来源。 app/Modules/* 下的目录本身不代表模块已启用。

Feature、Settings、权限配置和模块视图都只处理已注册 Provider 对应的模块;未注册目录不会被这些系统自动发现。 因此,临时目录或尚未完成的模块可以存在,但如果目录内已经出现 config/features.phpconfig/settings.phpconfig/permissions.php、模块路由或业务源码,必须同步注册 Provider,或者在 CI 中被报告为孤立模块。

脚手架会在创建目录的同时写入 Provider 注册。CI 应校验以下关系:

  • 已注册的模块 Provider 类必须存在,并且位于对应模块目录;
  • 模块 Provider 文件不得遗漏注册;
  • 模块目录、Provider、模块 key 和配置定义不能互相冲突。

各子系统不得自行扫描整个 app/Modules 目录;统一通过 App\Foundation\Module\ModuleRegistry 获取已启用模块。


4. 视图规范

4.1 视图应放模块内

长期推荐:

text
app/Modules/<Module>/resources/views/*

不推荐长期停留在:

text
resources/views/modules/<module>/*

4.2 视图命名空间

统一使用模块命名空间:

php
return view('filemanagement::show');

而不是:

php
return view('modules.filemanagement.show');

原因:

  • 更符合 Laravel 模块视图加载习惯
  • 更容易做自动注册
  • 模块迁移目录时,调用侧更稳定

4.3 全局 views 的职责

全局 resources/views 只建议保留:

  • layouts
  • 跨模块共享 partial
  • 平台级页面

5. 路由规范

5.1 模块路由放模块内

推荐:

text
app/Modules/<Module>/routes/web.php

过渡方案:

text
routes/modules/<module>.php

长期应回收进模块目录。

5.2 路由注册方式

推荐由模块 ServiceProvider 注册:

php
final class FileManagementServiceProvider extends ModuleServiceProvider
{
    // 只有存在 config/filemanagement.php 时才声明运行配置 key。
    protected function moduleConfigKey(): string
    {
        return 'filemanagement';
    }
}

其中 ModuleServiceProvider 负责标准样板:

  • config/<key>.php -> mergeConfigFrom(...)
  • routes/web.php -> loadRoutesFrom(...)

这样每个模块 Provider 只保留少量声明,不重复写样板代码。


6. 资源加载规范

6.1 视图自动注册

标准结构下:

text
app/Modules/*/resources/views

会由 AppServiceProvider 通过 ModuleRegistry,按“模块目录名小写”注册视图命名空间。

例如:

text
app/Modules/FileManagement/resources/views/form.blade.php

调用方式:

php
view('filemanagement::form');

因此模块 Provider 不需要再重复写

php
$this->loadViewsFrom(...);

6.2 兼容旧目录

过渡期仍兼容:

text
resources/views/modules/*

但仅作为历史兼容层,不再作为长期标准。

如果模块内目录和旧目录存在同名 namespace,优先使用模块内目录。

6.3 后台模块路由必须显式进入 web

对于 beta 后台模块,模块路由文件内必须显式挂上:

Route::middleware(['web', 'legacy.auth']) ->group(function (): void { Route::prefix('filemanagement') ->name('filemanagement.') ->middleware('filemanagement.permission:view') ->group(function (): void { // ... }); });


原因:

- `LegacySessionBridge` 挂在 `web` 组上
- `legacy.auth` 只校验桥接结果,不负责桥接本身
- 漏掉 `web` 时,模块页面和写接口会直接 302 到 `/login.php`

这是 beta 后台模块迁移时的硬约束。

---

## 7. 配置读取规范

模块配置优先统一走自己的顶级 key,例如:

```php
config('filemanagement.storage_root')

不建议长期混用多套路径,例如:

  • modules.filemanagement.*
  • lims.filemanagement.*

兼容读取可以暂时保留,但长期应收敛成一套正式 key。


8. 新模块落地规则

从本规范生效起:

  1. 新迁移的业务模块,优先按本标准新建目录。
  2. 新增 views 不再优先放 resources/views/modules/*
  3. 新增 module routes 不再优先放 routes/modules/*
  4. 模块 views 和 config 随模块落地;前端 assets、语言文件、迁移与 Seeder 沿用各自现有的根目录加载链路。
  5. beta 后台模块路由必须显式进入 ['web', 'legacy.auth'] 链路。
  6. 视图统一走 <模块目录名小写>::* 命名空间。
  7. 模块 Provider 优先继承 App\Foundation\Module\ModuleServiceProvider

8.1 使用脚手架命令

新模块使用 Artisan 命令生成最小骨架:

bash
php artisan lims:make-module Customer --key=customer --name="客户与委托"

正式生成前可先预览文件清单:

bash
php artisan lims:make-module Customer --key=customer --name="客户与委托" --dry-run

命令约束:

  • 默认拒绝处理已经存在的模块;需要补齐缺失骨架时使用 --missing-only
  • --missing-only 只创建不存在的文件,绝不覆盖现有 Provider、配置、路由或 README。
  • 会预建 Http/ControllersHttp/RequestsServicesModels 四个基础目录;Repository、DTO、Resource、Middleware 等目录在出现真实用途时再创建。
  • 生成过程中任一步失败都会回滚本次新建文件,避免留下半套模块。
  • migrations、Seeder 和 Factory 继续统一放在根目录 database/,不在模块下建立 database 目录。
  • 生成模块后,必须在 composer.json 添加 App\Modules\<Module>\app/Modules/<Module>/src/ 的 PSR-4 映射;模块存在测试时,还要添加 Tests\Modules\<Module>\app/Modules/<Module>/tests/ 的开发映射。随后执行 composer dump-autoload,不能只依赖 app/Modules/autoload.php 的兼容兜底。

9. 对现有 FileManagement 的落地建议

9.1 当前状态

FileManagement 现在已作为标准结构参考实现:

  • 代码主目录:beta/app/Modules/FileManagement/src/
  • 视图目录:beta/app/Modules/FileManagement/resources/views/
  • 路由目录:beta/app/Modules/FileManagement/routes/web.php
  • 配置目录:beta/app/Modules/FileManagement/config/filemanagement.php
  • 视图调用:filemanagement::...

9.2 建议的下一步

后续新模块或迁移模块,优先直接按 FileManagement 这套骨架落地,不再新增模块外平铺资源。

9.3 原则

迁移时只做“目录内聚”和“注册收敛”,不要顺手扩大业务改动范围。


10. 一句话规则

业务模块内聚:代码、视图、路由、配置、资源尽量都放 app/Modules/<Module> 内;全局目录只保留平台级共享能力。

状态建模、数据范围、Laravel 特性和前端技术选型以代码规范为准;涉及具体模块时再结合模块 README 和对应专项文档确认。