跳到主要内容
版本:Next

核心架构

引言

CMS.Plugin.MyPluginName 是一个基于领域驱动设计(DDD)原则构建的模块化插件,旨在为CMS系统提供可扩展的功能。该插件采用分层架构模式,通过清晰的职责划分实现高内聚低耦合的设计目标。本文档详细阐述了插件的核心架构,包括各模块之间的协作机制、ABP框架的应用以及模块化设计的实现方式。

项目结构与分层架构

本插件遵循典型的DDD分层架构,包含Application、Domain、EntityFrameworkCore、Abstractions等核心模块。这种分层设计确保了业务逻辑与基础设施的分离,提高了代码的可维护性和可测试性。

图示来源

模块职责划分与依赖关系

各模块通过明确的依赖关系协同工作,形成稳定的架构体系:

  • Abstractions:定义插件的核心抽象,包括服务接口和领域活动,为其他模块提供契约
  • Domain:包含领域模型、聚合根、领域服务和仓储接口,实现核心业务逻辑
  • Application:提供应用服务,协调领域对象完成用例,实现DTO与领域对象的映射
  • EntityFrameworkCore:实现数据访问,提供具体的仓储实现和数据库上下文管理

图示来源

领域驱动设计(DDD)实现

插件严格遵循DDD原则,通过聚合根、实体、值对象和领域服务等模式构建领域模型。

聚合根与实体

MyEntityName 类作为聚合根,继承自 FullAuditedAggregateRoot<Guid>,封装了业务规则和不变性:

public class MyEntityName : FullAuditedAggregateRoot<Guid>
{
public virtual string Code { get; protected set; }
public virtual string Name { get; protected set; }
public virtual int Sort { get; protected set; }
public virtual string Remark { get; protected set; }

public virtual void Update(string code, string name, string remark = null)
{
// 业务规则验证
Code = Check.NotNullOrWhiteSpace(code, "编号", MyEntityNameConsts.MaxCodeLength);
Name = Check.NotNullOrWhiteSpace(name, "名称", MyEntityNameConsts.MaxNameLength);
Remark = Check.Length(remark, "备注", MyEntityNameConsts.MaxRemarkLength);
}
}

仓储模式

通过 IMyEntityNameRepository 接口定义仓储契约,EfCoreMyEntityNameRepository 提供具体实现,实现了领域层与基础设施层的解耦。

模块注册与生命周期管理

CMSPluginEntry 类作为插件入口点,负责模块的注册和生命周期管理。

模块注册

通过 Register 方法注册服务和配置:

public override void Register(IRegisteContext context)
{
// 注册HTTP API客户端
context.Services.AddHttpApi<IMyPluginNameExternalApi>();

// 注册服务
context.Services.TryAddMyPluginName();
context.Services.AddScoped<IProjectRuntimeMigrator, CMSPluginRuntimeMigrator>();
context.Services.AddSingleton<IProjectService, MyPluginNameProjectService>();

// 配置数据库提供程序
_service.AddApplication<CMSPluginModule>(options =>
{
var databaseType = configuration[SectionName.DatabaseType] ?? "mysql";
if (KnowsDbCode.IsMysql(databaseType))
{
options.PlugInSources.Add(new TypePlugInSource(
typeof(MySQL.CMSPluginMySQLModule)
));
}
// 其他数据库类型...
});
}

生命周期管理

插件通过重写生命周期方法实现不同阶段的初始化:

public override async Task ReadyAsync(IReadyContext context)
{
context.Provider.GetRequiredService<ObjectAccessor<IApplicationBuilder>>().Value = context.Features.GetApplicationBuilder();
var app = context.Features.GetApplicationBuilder();
await app.InitializeApplicationAsync();
}

public override async Task AfterReadyAsync(IReadyContext context)
{
await context.GetRequiredService<IBackgroundWorkerManager>().AddAsync(context.GetRequiredService<MyPluginNameWorker>());
}

ABP框架在模块化设计中的作用

ABP框架为插件提供了强大的模块化支持,通过 AbpModule 特性实现模块依赖管理。

模块依赖

CMSPluginModule 通过 DependsOn 特性声明对其他模块的依赖:

[DependsOn(
typeof(CMSPluginAbpModule),
typeof(CMSPluginAbpAspNetCoreModule),
typeof(CMSPluginApplicationModule),
typeof(CMSPluginEntityFrameworkCoreModule)
)]
public class CMSPluginModule : AbpStartupModule

应用服务配置

CMSPluginApplicationModule 配置应用层服务:

[DependsOn(
typeof(CMSPluginDomainModule),
typeof(CMSPluginApplicationContractsModule),
typeof(AbpAutoMapperModule),
typeof(AbpEventBusModule),
typeof(AbpDddApplicationModule)
)]
public class CMSPluginApplicationModule : AbpModule
{
public override void ConfigureServices(ServiceConfigurationContext context)
{
context.Services.AddAutoMapperObjectMapper<CMSPluginApplicationModule>();
Configure<AbpAutoMapperOptions>(options =>
{
options.AddMaps<CMSPluginApplicationModule>(validate: true);
});
}
}

系统上下文与组件交互

系统上下文图

图示来源

组件交互序列图

图示来源

服务注册与依赖注入配置

插件通过依赖注入容器管理服务生命周期,确保松耦合和可测试性。

服务注册示例

// 在CMSPluginEntry中注册服务
context.Services.AddScoped<IProjectRuntimeMigrator, CMSPluginRuntimeMigrator>();
context.Services.AddSingleton<IProjectService, MyPluginNameProjectService>();

// 配置EF数据提供程序
context.Services.AddScoped<IEFDataProvider>(p =>
{
var cfg = p.GetRequiredService<IDataRuntimeConfig>();
return new DefaultEFDataProvider(CMSPluginMesSuiteOptions.DataProviderName,
CMSPluginMesSuiteOptions.GetDatabaseSuffix(CMSPluginDbProperties.MigrationsHistoryTable),
CMSPluginDbProperties.MigrationsHistoryTable, cfg);
});

依赖注入配置

// 在ConfigureContainer中填充服务
public override void ConfigureContainer(ContainerBuilder builder)
{
builder.Populate(_service);
base.ConfigureContainer(builder);
}

技术选型与架构权衡

分层架构优势

采用分层架构而非单体模式的主要原因包括:

  1. 关注点分离:各层职责明确,便于团队协作和代码维护
  2. 可测试性:通过接口抽象,可以独立测试各层逻辑
  3. 可扩展性:新增功能或修改实现时影响范围可控
  4. 技术独立性:领域层不依赖具体技术栈,便于未来技术演进

DDD实践价值

  • 领域模型清晰:通过聚合根、实体等概念准确表达业务语义
  • 业务规则集中:将业务规则封装在领域对象中,避免贫血模型
  • 可维护性高:代码结构符合业务逻辑,易于理解和修改

模块化设计收益

  • 插件化支持:通过ABP模块系统实现功能的动态加载和卸载
  • 数据库适配:支持MySQL、SqlServer、PostgreSql等多种数据库
  • 运行时配置:根据配置动态选择数据库模块,提高部署灵活性

结论

CMS.Plugin.MyPluginName通过采用领域驱动设计和分层架构,构建了一个高内聚、低耦合、易于维护和扩展的插件系统。ABP框架提供了强大的模块化支持,使得插件能够灵活地集成到主系统中。清晰的职责划分和依赖管理确保了系统的稳定性和可预测性,为未来的功能扩展奠定了坚实的基础。