# 模块抽象 (09_Module)

最后更新: 2024-09-20


# 📚 概述

DarkM 采用模块化架构,每个模块都是独立的单元,可以灵活组合和部署。模块抽象层定义了模块的标准接口和描述符。

源代码位置: DarkM/src/Framework/Module


# 🏗️ 模块架构

# 完整目录结构

Module/
├── Module.Abstractions/
│   ├── IModuleDescriptor.cs               # 模块描述符接口
│   ├── IModuleCollection.cs               # 模块集合接口
│   ├── IModuleAssemblyDescriptor.cs       # 模块程序集描述符接口
│   ├── IModuleServicesConfigurator.cs     # 模块服务配置器接口
│   ├── IModuleInitializer.cs              # 模块初始化器接口
│   ├── ModuleCollectionAbstract.cs        # 模块集合抽象基类
│   ├── ModuleInitDataScriptDescriptor.cs  # 模块初始化数据脚本描述符
│   └── ModuleEnumDescriptor.cs            # 模块枚举描述符
│
├── Module.AspNetCore/
│   ├── ModuleDescriptor.cs                # ASP.NET Core 模块描述符
│   ├── ModuleCollection.cs                # 模块集合
│   ├── ModuleAssemblyDescriptor.cs        # 模块程序集描述符
│   ├── ServiceCollectionExtensions.cs     # 服务注册扩展
│   └── ApplicationBuilderExtensions.cs    # 应用构建扩展
│
└── Module.GenericHost/
    ├── ModuleDescriptor.cs                # 通用主机模块描述符
    ├── ModuleCollection.cs                # 模块集合
    └── ModuleAssemblyDescriptor.cs        # 模块程序集描述符
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22

# 🏗️ 模块结构

# 标准模块项目结构

ModuleName/
├── build/                      # 编译配置
│   └── module.build.targets
│
├── Application/                # 应用服务层
│   ├── UserService/
│   │   ├── IUserService.cs
│   │   ├── UserService.cs
│   │   ├── ViewModels/
│   │   └── _MapperConfig.cs
│   └── ...
│
├── Domain/                     # 领域层
│   ├── UserModule/
│   │   ├── Models/
│   │   │   └── UserEntity.cs
│   │   ├── Repository/
│   │   │   └── IUserRepository.cs
│   │   └── QueryModels/
│   │       └── UserQueryModel.cs
│
├── Infrastructure/             # 基础设施层
│   ├── Repositories/
│   │   └── UserRepository.cs
│   └── ModuleDescriptor.cs
│
├── Quartz/                     # 任务调度层
│   └── Jobs/
│       └── CleanUserJob.cs
│
├── Web/                        # 应用层
│   ├── Controllers/
│   │   └── UserController.cs
│   ├── Validators/
│   │   └── UserAddModelValidator.cs
│   └── ModuleInitializer.cs
│
└── WebHost/                    # 启动器
    ├── appsettings.json
    └── Program.cs
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40

# 🔧 核心接口

# 1. IModuleDescriptor(模块描述符)

文件位置: Module.Abstractions/IModuleDescriptor.cs

public interface IModuleDescriptor
{
    /// <summary>
    /// 模块名称
    /// </summary>
    string Name { get; }
    
    /// <summary>
    /// 模块描述
    /// </summary>
    string Description { get; }
    
    /// <summary>
    /// 模块版本
    /// </summary>
    Version Version { get; }
    
    /// <summary>
    /// 模块依赖
    /// </summary>
    IEnumerable<string> Dependencies { get; }
    
    /// <summary>
    /// 模块程序集描述符
    /// </summary>
    IModuleAssemblyDescriptor AssemblyDescriptor { get; }
    
    /// <summary>
    /// 模块初始化器
    /// </summary>
    IModuleInitializer Initializer { get; }
    
    /// <summary>
    /// 配置服务
    /// </summary>
    void ConfigureServices(IServiceCollection services, IModuleCollection modules);
    
    /// <summary>
    /// 使用模块
    /// </summary>
    void UseModule(IApplicationBuilder app);
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42

# 2. IModuleCollection(模块集合)

文件位置: Module.Abstractions/IModuleCollection.cs

public interface IModuleCollection : IList<IModuleDescriptor>
{
    /// <summary>
    /// 获取模块
    /// </summary>
    IModuleDescriptor Get(string name);
    
    /// <summary>
    /// 尝试获取模块
    /// </summary>
    bool TryGet(string name, out IModuleDescriptor module);
    
    /// <summary>
    /// 是否包含模块
    /// </summary>
    bool Contains(string name);
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17

# 3. IModuleAssemblyDescriptor(模块程序集描述符)

文件位置: Module.Abstractions/IModuleAssemblyDescriptor.cs

public interface IModuleAssemblyDescriptor
{
    /// <summary>
    /// 应用层程序集
    /// </summary>
    Assembly Application { get; }
    
    /// <summary>
    /// 领域层程序集
    /// </summary>
    Assembly Domain { get; }
    
    /// <summary>
    /// 基础设施层程序集
    /// </summary>
    Assembly Infrastructure { get; }
    
    /// <summary>
    /// Web 层程序集
    /// </summary>
    Assembly Web { get; }
    
    /// <summary>
    /// 任务调度层程序集
    /// </summary>
    Assembly Quartz { get; }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27

# 4. IModuleInitializer(模块初始化器)

文件位置: Module.AspNetCore/IModuleInitializer.cs

public interface IModuleInitializer
{
    /// <summary>
    /// 初始化模块
    /// </summary>
    /// <param name="serviceProvider">服务提供器</param>
    void Initialize(IServiceProvider serviceProvider);
}
1
2
3
4
5
6
7
8

# 💡 使用示例

# 1. 定义模块描述符

文件位置: Module.User/ModuleDescriptor.cs

using DarkM.Lib.Module.Abstractions;
using DarkM.Lib.Module.AspNetCore;
using Microsoft.Extensions.DependencyInjection;
using System.Reflection;

namespace DarkM.Module.User
{
    /// <summary>
    /// 用户模块描述符
    /// </summary>
    public class ModuleDescriptor : ModuleDescriptorAbstract
    {
        public override string Name => "User";
        
        public override string Description => "用户管理模块";
        
        public override Version Version => new Version(1, 0, 0);
        
        /// <summary>
        /// 声明模块依赖
        /// </summary>
        public override IEnumerable<string> Dependencies => new[] { "Admin" };
        
        /// <summary>
        /// 配置服务
        /// </summary>
        public override void ConfigureServices(IServiceCollection services, IModuleCollection modules)
        {
            // 注册服务
            services.AddSingleton<IUserService, UserService>();
            services.AddSingleton<IUserRepository, UserRepository>();
            
            // 注册验证器
            services.AddValidatorsFromAssembly(Assembly.GetExecutingAssembly());
            
            // 注册任务
            services.AddSingleton<ITask, CleanUserJob>();
        }
        
        /// <summary>
        /// 使用模块
        /// </summary>
        public override void UseModule(IApplicationBuilder app)
        {
            // 模块初始化逻辑
            var logger = app.ApplicationServices.GetRequiredService<ILogger<ModuleDescriptor>>();
            logger.LogInformation("User module initialized");
        }
    }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50

# 2. 声明模块依赖

public class ModuleDescriptor : ModuleDescriptorAbstract
{
    public override string Name => "Order";
    
    /// <summary>
    /// 声明依赖
    /// </summary>
    public override IEnumerable<string> Dependencies => new[] { "Admin", "User", "Product" };
}
1
2
3
4
5
6
7
8
9

# 3. 模块初始化

文件位置: Module.User.Web/ModuleInitializer.cs

using DarkM.Lib.Module.AspNetCore;

namespace DarkM.Module.User.Web
{
    /// <summary>
    /// 用户模块初始化器
    /// </summary>
    public class ModuleInitializer : IModuleInitializer
    {
        public void Initialize(IServiceProvider serviceProvider)
        {
            // 模块初始化逻辑
            var logger = serviceProvider.GetRequiredService<ILogger<ModuleInitializer>>();
            logger.LogInformation("User module initialized");
            
            // 初始化数据
            InitializeData(serviceProvider);
        }
        
        private void InitializeData(IServiceProvider serviceProvider)
        {
            // 初始化模块数据
        }
    }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25

# 4. 模块配置

appsettings.json:

{
  "Db": {
    "Modules": [
      {
        // 模块名称
        "Name": "Admin",
        // 表前缀
        "Prefix": "",
        // 数据库名称
        "Database": "Nm_Admin",
        // 自定义连接字符串
        "ConnectionString": "",
        // 自定义版本号
        "Version": ""
      },
      {
        "Name": "User",
        "Database": "Nm_User"
      }
    ]
  }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22

# 5. 模块依赖排序

框架会自动根据依赖关系对模块进行排序:

// 模块 A 依赖模块 B
public class ModuleADescriptor : ModuleDescriptorAbstract
{
    public override string Name => "ModuleA";
    public override IEnumerable<string> Dependencies => new[] { "ModuleB" };
}

// 模块 B 无依赖
public class ModuleBDescriptor : ModuleDescriptorAbstract
{
    public override string Name => "ModuleB";
    public override IEnumerable<string> Dependencies => Array.Empty<string>();
}

// 框架会自动排序:ModuleB -> ModuleA
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15

# 📚 最佳实践

# 1. 模块命名

Module 结尾,使用 PascalCase 命名:

// ✅ 推荐
DarkM.Module.User
DarkM.Module.Order

// ❌ 不推荐
DarkM.UserModule
darkm.module.user
1
2
3
4
5
6
7

# 2. 分层清晰

  • Application 层:业务逻辑、服务接口
  • Domain 层:实体、仓储接口、查询模型
  • Infrastructure 层:仓储实现、外部服务
  • Web 层:控制器、验证器、过滤器

# 3. 依赖倒置

通过接口依赖,不要直接依赖具体实现:

// ✅ 推荐
private readonly IUserRepository _repository;

// ❌ 不推荐
private readonly UserRepository _repository;
1
2
3
4
5

# 4. 模块独立

模块之间尽量低耦合,通过事件或接口通信:

// 使用观察者模式
await _observerHandler.Add<UserEntity>(userId);
1
2

# 5. 模块版本管理

使用语义化版本号:

public override Version Version => new Version(1, 0, 0);  // Major.Minor.Patch
1

# 6. 模块文档

每个模块应包含 README.md 文档:

# User Module

用户管理模块

## 功能

- 用户注册
- 用户登录
- 用户管理

## 依赖

- Admin 模块

## 配置

```json
{
  "Db": {
    "Modules": [
      {
        "Name": "User",
        "Database": "Nm_User"
      }
    ]
  }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27

---

## 🔍 常见问题

### Q1: 模块未加载?

**检查清单:**
1. ✅ 模块名称是否正确
2. ✅ 模块描述符是否实现
3. ✅ 模块程序集是否被扫描
4. ✅ 模块依赖是否满足

**解决方案:**
```csharp
// 检查模块是否被加载
var module = modules.Get("User");
if (module == null)
{
    logger.LogError("User module not loaded");
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21

# Q2: 模块依赖循环?

问题: 模块 A 依赖模块 B,模块 B 依赖模块 A

解决方案:

  1. 提取公共模块
  2. 使用事件解耦
  3. 使用接口解耦
// 使用事件解耦
await _eventBus.PublishAsync(new UserCreatedEvent(userId));
1
2

# Q3: 模块服务未注册?

检查:

  1. ✅ ConfigureServices 是否调用
  2. ✅ 服务注册代码是否正确
  3. ✅ 程序集是否被扫描

解决方案:

public override void ConfigureServices(IServiceCollection services, IModuleCollection modules)
{
    // 确保服务被注册
    services.AddSingleton<IUserService, UserService>();
}
1
2
3
4
5

# Q4: 模块初始化顺序?

框架会自动根据依赖关系排序:

// 依赖关系:Admin -> User -> Order
// 初始化顺序:Admin -> User -> Order
1
2

# Q5: 如何卸载模块?

解决方案:

  1. 从配置中移除模块
  2. 重启应用
  3. 清理模块数据
{
  "Db": {
    "Modules": [
      // 移除要卸载的模块
      // { "Name": "OldModule" }
    ]
  }
}
1
2
3
4
5
6
7
8

# 📚 相关文档


# 🔗 参考链接


最后更新: 2024-09-20