新模块开发指南
本文以当前默认主宿主 src\Services\Host\FreeKit.Host 与模块注册中心 src\Services\Host\FreeKit.DI\E.cs 为准,说明如何接入一个新的业务模块。
目录结构约定
当前仓库常见模式是“目录 + 模块项目 + 可选宿主/客户端”:
src\Services\<ModuleGroup>\
├── FreeKit.<ModuleName>\ # 模块主项目
├── FreeKit.<ModuleName>.Host\ # 可选:独立宿主
└── FreeKit.<ModuleName>.Client\ # 可选:客户端 SDK
模块主项目一般包含:
FreeKit.<ModuleName>\
├── Application\
├── Contracts\
├── Controllers\
├── Domain\
├── Models\
└── <ModuleName>ModuleStartup.cs
第 1 步 — 创建项目
cd src/Services
mkdir Notification
cd Notification
dotnet new classlib -n FreeKit.Notification --framework net10.0
按需引用已有基建项目,优先复用 BuildingBlocks 而不是重复造轮子:
<ItemGroup>
<ProjectReference Include="..\..\..\BuildingBlocks\IGeekFan.FreeKit.Infrastructure\IGeekFan.FreeKit.Infrastructure.csproj" />
</ItemGroup>
第 2 步 — 实现 IModuleStartup
每个模块必须实现 IGeekFan.FreeKit.Modularity.IModuleStartup:
using IGeekFan.FreeKit.Modularity;
namespace FreeKit.Notification;
public class NotificationModuleStartup : IModuleStartup
{
public void ConfigureServices(IServiceCollection services, IConfiguration c)
{
services.AddAutoMapper(typeof(NotificationModuleStartup).Assembly);
services.Configure<NotificationOption>(c.GetSection("Notification"));
services.AddScoped<INotificationService, NotificationService>();
}
public void Configure(WebApplication app, IWebHostEnvironment env)
{
IFreeSql fsql = app.Services.GetRequiredService<IFreeSql>();
fsql.CodeFirst.SyncStructure(typeof(NotificationRecord));
}
}
实际仓库中的 CmsKitModuleStartup、IdentityModuleStartup、PlatformModuleStartup 都遵循这一模式。
第 3 步 — 定义实体与配置
当前项目主要使用 FreeSql CodeFirst。实体可直接使用 [Table] / [Column],也可以继承已有审计基类:
using FreeSql.DataAnnotations;
namespace FreeKit.Notification.Domain;
[Table(Name = "notification_record")]
public class NotificationRecord
{
[Column(IsPrimary = true, IsIdentity = true)]
public long Id { get; set; }
public string Title { get; set; } = string.Empty;
public string Content { get; set; } = string.Empty;
}
如果模块存在较多实体,推荐像现有模块一样按 Domain/ 与 Models/ 分开组织。
第 4 步 — 编写应用服务与控制器
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc;
namespace FreeKit.Notification.Controllers;
[ApiController]
[Route("api/notification/messages")]
[Authorize]
public class NotificationController : ControllerBase
{
private readonly INotificationService _service;
public NotificationController(INotificationService service) => _service = service;
[HttpGet]
public async Task<IActionResult> GetListAsync()
{
var result = await _service.GetListAsync();
return Ok(result);
}
}
路由前缀建议参考现有模块风格,例如:
api/cms/*api/identity/*api/im/*api/plat/*api/v1/*(ToDo、Holiday、部分工具接口)
第 5 步 — 注册到主宿主
默认主宿主通过 FreeKit.DI\E.cs 装载模块,需要同时做两件事。
5.1 在 FreeKit.DI.csproj 中添加项目引用
<ItemGroup>
<ProjectReference Include="..\..\Notification\FreeKit.Notification\FreeKit.Notification.csproj" />
</ItemGroup>
5.2 在 E.cs 的 Modules 字典中注册
using FreeKit.Notification;
public static readonly Dictionary<string, Type> Modules = new(StringComparer.OrdinalIgnoreCase)
{
{ "identity", typeof(IdentityModuleStartup) },
{ "notification", typeof(NotificationModuleStartup) },
};
主宿主 Program.cs 中已经使用:
builder.UseFreeKit(
moduleTypeMap: E.Modules,
extraAssemblies: new[] { typeof(Program).Assembly }
);
因此只要 E.Modules 注册完成,模块就会被自动装载。
第 6 步 — 添加配置(可选)
在 appsettings.Development.json 中添加模块配置段:
{
"Notification": {
"MaxRetryCount": 3,
"DefaultExpireDays": 30
}
}
对应的 Options 类:
public class NotificationOption
{
public int MaxRetryCount { get; set; } = 3;
public int DefaultExpireDays { get; set; } = 30;
}
第 7 步 — 根据需要接入独立宿主或文档
如果模块需要独立运行,可额外创建:
FreeKit.<ModuleName>.HostFreeKit.<ModuleName>.Client
文档站则建议补充:
- 模块 README
- 实体参考
- API 或运维文档
模块禁用与约定补充
默认主宿主下,禁用模块最直接的方式就是从 E.Modules 中移除对应项。
另外遵循当前仓库已有约定:
- 应用服务放在
Application/ - DTO/契约放在
Contracts/ - 尽量复用
BuildingBlocks中已有能力 - Manager/Domain Service 若采用约定式领域服务模式,不要重复手工注册
- 表结构同步放在模块
Configure(...)中,而不是散落到Program.cs
约定总结
| 规则 | 说明 |
|---|---|
| 命名 | <Entity>Service、I<Entity>Service、<Entity>Dto、<Entity>Req、<Entity>Query |
| 启动类 | 必须实现 IModuleStartup,命名为 <Module>ModuleStartup |
| 建表 | 在 Configure() 中调用 fsql.CodeFirst.SyncStructure(...) |
| DI 注册 | 优先放在 ConfigureServices();主宿主只负责统一装载 |
| 配置 | 使用 IOptions<T> 模式,Options 类命名为 <Feature>Option |