模块化架构最佳实践 & 约定

解决方案结构

  • 推荐 在Visual Sudio中为每个模块创建一个单独的解决方案.
  • 推荐 将解决方案命名为CompanyName.ModuleName(对于ABP核心模块来说,它的命名方式是Volo.Abp.ModuleName).
  • 推荐 一个模块做为分层项目开发,因为它有几个包(项目)是相互关联的.
    • 每个包都有自己的模块定义文件,并显式声明所依赖的包/模块的依赖关系.

层(layers) & 包(packages)

下面展示了一个分层良好的模块中的包以及它们之间的依赖关系:

module-layers-and-packages

最终的目地是让应用程序以灵活的方式使用该模块. 示例应用程序:

  • A) 单体应用程序;
    • 添加对WebApplication包的引用.
    • 根据需要添加对EF CoreMongoDB 包的引用.
    • 效果;
      • 应用程序可以显示模块的UI.
      • 它在同一进程中托管应用层领域层 (这就是为什么它引用对数据库集成包).
      • 此应用程序还提供了模块的HTTP API(因为它通过Web包引用了HttpApi包).
  • B) 仅为微服务提供模块的应用程序;
    • 添加对HttpApiApplication包的引用.
    • 根据需要添加对EF CoreMongoDB 包的引用.
    • 效果;
      • 应用程序无法显示模块的UI, 因为它没有对Web包的引用.
      • 它在同一进程中托管应用层领域层 (这就是为什么它引用对数据库集成包).
      • 此应用程序提供了模块的HTTP API(它的主要目标).
  • C) 显示模块UI但是不托管应用层的应用程序(仅将其用作由应用程序A或B托管的远程服务)
    • 添加对WebHttpApi.Client包的引用.
    • 配置HttpApi.Client包的远程端点.
    • 效果;
      • 应用程序可以显示模块的UI.
      • 它不会在同一进程中托管模块的应用层和领域层. 而是将其用作远程服务.
      • 此应用程序还提供了模块的HTTP API(因为它通过Web包引用了HttpApi包).
  • D) 客户端应用程序 (或微服务) 只使用模块作为远程服务(由应用程序A,B或C托管);
    • 添加对HttpApi.Client包的引用.
    • 配置HttpApi.Client包的远程端点.
    • 效果;
      • 应用程序可以使用模块的所有功能作为远程客户端.
      • 应用程序只是一个客户端,无法提供模块的HTTP API.
      • 应用程序只是一个客户端,无法显示模块的UI.
  • E) 托管模块的HTTP API但只是将所有请求转发给另一个应用程序的代理应用程序 (由应用程序A, B或C托管);
    • 添加对HttpApiHttpApi.Client包的引用.
    • 配置HttpApi.Client包的远程端点.
    • 效果;
      • 应用程序可以将模块的所有功能用作远程客户端.
      • 应用程序也服务于模块的HTTP API, 但实际上它的工作方式与代理一样,将所有请求(模块)重定向到另一个远程服务器.

下一节将详细地介绍这些包.

领域层

  • 推荐 将领域层划分为两个项目:
    • Domain.Shared 包(项目) 命名为CompanyName.ModuleName.Domain.Shared,包含常量,枚举和其他类型, 它不能包含实体,存储库,域服务或任何其他业务对象. 可以安全地与模块中的所有层使用. 此包也可以与第三方客户端使用.
    • Domain 包(项目) 命名为CompanyName.ModuleName.Domain, 包含实体, 仓储接口,领域服务接口及其实现和其他领域对象.
      • Domain 包依赖于 Domain.Shared 包.

应用服务层

  • 推荐 将应用服务层划分为两个项目:
    • Application.Contracts 包(项目) 命名为*CompanyName.ModuleName.Application.Contracts,包含应用服务接口和相关的数据传输对象(DTO).
      • Application contract 包依赖于 Domain.Shared 包.
    • Application 包(项目)命名为CompanyName.ModuleName.Application,包含应用服务实现.
      • Application 包依赖于 Domain 包和 Application.Contracts 包.

基础设施层

  • 推荐 为每个orm/数据库集成创建一个独立的集成包, 比如Entity Framework Core 和 MongoDB.
    • 推荐 例如, 创建一个抽象Entity Framework Core集成的CompanyName.ModuleName.EntityFrameworkCore 包. ORM 集成包依赖于 Domain 包.
    • 不推荐 依赖于orm/数据库集成包中的其他层.
  • 推荐 为每个主要的库创建一个独立的集成包, 在不影响其他包的情况下可以被另一个库替换.

HTTP 层

  • 推荐 创建命名为CompanyName.ModuleName.HttpApiHTTP API包, 为模块开发REST风格的HTTP API.
    • HTTP API 包只依赖于 Application.Contracts 包. 不要依赖 Application 包.
    • 推荐 为每个应用服务创建一个Controller (通常通过实现其接口). 这些控制器使用应用服务接口来委托操作. 它根据需要配置路由, HTTP方法和其他与Web相关的东西.
  • 推荐 创建一个为HTTP API包提供客户端服务的HTTP API Client包, 它的命名为Companyname.ModuleName.HttpApi.Client. 这些客户端服务将应用服务接口实现远程端点的客户端.
    • HTTP API Client 包仅依赖于 Application.Contracts 包.
    • 推荐 使用ABP框架提供的动态代理HTTP C#客户端的功能.

Web 层

  • 推荐 创建命名为CompanyName.ModuleName.WebWeb包. 包含页面,视图,脚本,样式,图像和其他UI组件.
    • Web 包仅依赖于 HttpApi 包.