👨‍💻 niltor    📆 2026-09-24 14:16

模块化开发

模块是相对独立的业务功能单元,通常根据业务领域进行划分。模块化开发可以提升代码的可维护性和复用性。

模块必须以Mod后缀命名。

管理模块

通过studio中的模块管理界面,可以非常方便地创建和删除模块,工具会自动跨程序集处理模块相关的依赖关系。

也可以在解决方案根目录下使用 CLI 创建模块或服务:

perigon add module <模块名称>
perigon add service <服务名称>

模块目录

模块自身在src/Modules中,是一个独立的项目。通常包括:

  • Models: Dto模型
  • Managers: 业务逻辑
  • Services: 仅用于该模块的服务
  • ModuleExtensions.cs:模块的扩展方法,用于注册模块相关的服务。

Tip

源代码生成器会在服务中自动注入模块的扩展方法,无需手动调用。

模块的实体在src/Definition/Entity项目中,通过目录名称(模块命名)进行区分。

模块的复用

模块可以跨解决方案进行复用,如客户管理模块,订单管理模块等。工具提供了模块的打包和安装命令,方便模块的分发和使用。

业务模块应保持独立,不应直接引用其他业务模块。需要跨模块复用时,通用类型、常量和工具等基础内容放在Share项目中;如果复用内容属于功能实现、不适合放入Share,则放入共享模块CommonMod,由需要它的业务模块引用。不要让一个业务模块依赖另一个具体业务模块。

打包模块

要进行打包,需要编辑ModuleExtensions.cs文件,该文件在创建模块时自动生成,如:

public static class ModuleExtensions
{
    [DisplayName("Perigon::XXX")]
    [Description("注册 XXX 模块服务,包括密钥管理与 OAuth 核心逻辑")]
    public static IHostApplicationBuilder AddXXXMod(this IHostApplicationBuilder builder)
    {
        builder.AddModServices();
        return builder;
    }
    private static IHostApplicationBuilder AddModServices(this IHostApplicationBuilder builder)
    {
        // ...模块相关服务注册,如添加后台服务
        return builder;
    }
}
  • [DisplayName]:用来表示作者名称和包的显示名,使用::分隔,如"Perigon::文件管理模块",如果没有发现::,则默认认为是作者名称,包显示名使用默认的模块名称。
  • [Description]:用来描述包的作用和特点。
  • AddXXXMod:模块的扩展方法,用来注册模块相关的服务,如果服务很多,可以拆分到私有方法AddModServices中。

Note

通常AddXXXMod只包含该模块特定的服务注册

进行打包的模块,要满足:

  • 业务模块之间不能直接互相引用;共享内容按上文放入Share或CommonMod。
  • 当前 CLI 的打包依赖校验会检查 C# using 指令中的模块命名空间:模块源码和实体目录允许引用本模块及Share;所选服务的控制器目录还允许引用CommonMod。因此,模块源码直接引用CommonMod目前会被module pack校验拦截。

在项目根目录下使用perigon module pack <模块名称> <服务名称>打包,生成的 zip 文件位于package_modules目录。包内包含模块元数据和模块、实体目录;控制器和前端文件按需加入。典型布局如下:

metadata.json
Modules/<ModuleName>/...
Entity/<ModuleName>/...
Controllers/<ModuleName>/...   # 可选
Frontend/<前端模块目录名>/...   # 指定 --front-path 时
Frontend/share/...             # 同级 share 目录存在时
  • metadata.json记录模块名、作者、显示名、描述和版本等信息;版本可通过-v/--version指定,省略时默认使用1.0.0。
  • 打包命令中的<服务名称>是控制器来源服务。CLI 只检查src/Services/<服务名称>/Controllers/<模块名称>;目录存在时递归打入该目录下的文件,目录不存在时不包含控制器。它不会扫描其他服务,也不会按 C# 控制器类型筛选。打包时会跳过bin和obj目录。
  • 需要打包前端模块时,使用--front-path指定前端模块目录;该目录及其同级的share目录会分别写入Frontend/<模块目录名>和Frontend/share。详细规则见命令行文档。

安装模块

请在项目根目录下执行:

perigon module install <模块包路径> <服务名称>

安装命令中的<服务名称>是安装目标服务。CLI 会把包中的模块文件复制到src/Modules/<模块名称>,实体文件复制到src/Definition/Entity/<模块名称>;如果包内含Controllers/<模块名称>,则复制到src/Services/<服务名称>/Controllers/<模块名称>。目标位置已有同路径文件时,模块、实体和控制器文件会被覆盖。

复制文件后,CLI 会将模块加入解决方案,为目标服务项目添加模块项目引用,并更新服务的GlobalUsings.cs;如果找到DefaultDbContext.cs,还会根据模块实体补充DbSet及对应的 Entity Framework 全局 using。安装前请确认目标服务目录存在。

如果包中包含前端文件,安装时可用--front-path指定前端项目根目录,文件会恢复到其src/app/modules下。模块目录中的同名文件会覆盖;share目录中已存在的同名文件会保留,只补充缺少的文件。安装后重新加载解决方案并检查模块是否可正常构建。详细前端规则见命令行文档。