跳到主要内容

新模块开发指南

本文以当前默认主宿主 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));
}
}

实际仓库中的 CmsKitModuleStartupIdentityModuleStartupPlatformModuleStartup 都遵循这一模式。

第 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.csModules 字典中注册

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>.Host
  • FreeKit.<ModuleName>.Client

文档站则建议补充:

  • 模块 README
  • 实体参考
  • API 或运维文档

模块禁用与约定补充

默认主宿主下,禁用模块最直接的方式就是从 E.Modules 中移除对应项。

另外遵循当前仓库已有约定:

  • 应用服务放在 Application/
  • DTO/契约放在 Contracts/
  • 尽量复用 BuildingBlocks 中已有能力
  • Manager/Domain Service 若采用约定式领域服务模式,不要重复手工注册
  • 表结构同步放在模块 Configure(...) 中,而不是散落到 Program.cs

约定总结

规则说明
命名<Entity>ServiceI<Entity>Service<Entity>Dto<Entity>Req<Entity>Query
启动类必须实现 IModuleStartup,命名为 <Module>ModuleStartup
建表Configure() 中调用 fsql.CodeFirst.SyncStructure(...)
DI 注册优先放在 ConfigureServices();主宿主只负责统一装载
配置使用 IOptions<T> 模式,Options 类命名为 <Feature>Option