模块化架构最佳实践 & 约定
解决方案结构
- 推荐 在Visual Sudio中为每个模块创建一个单独的解决方案.
- 推荐 将解决方案命名为CompanyName.ModuleName(对于ABP核心模块来说,它的命名方式是Volo.Abp.ModuleName).
- 推荐 一个模块做为分层项目开发,因为它有几个包(项目)是相互关联的.
- 每个包都有自己的模块定义文件,并显式声明所依赖的包/模块的依赖关系.
层(layers) & 包(packages)
下面展示了一个分层良好的模块中的包以及它们之间的依赖关系:
最终的目地是让应用程序以灵活的方式使用该模块. 示例应用程序:
- A) 单体应用程序;
- 添加对Web和Application包的引用.
- 根据需要添加对EF Core 或 MongoDB 包的引用.
- 效果;
- 应用程序可以显示模块的UI.
- 它在同一进程中托管应用层和领域层 (这就是为什么它引用对数据库集成包).
- 此应用程序还提供了模块的HTTP API(因为它通过Web包引用了HttpApi包).
- B) 仅为微服务提供模块的应用程序;
- 添加对HttpApi和Application包的引用.
- 根据需要添加对EF Core 或 MongoDB 包的引用.
- 效果;
- 应用程序无法显示模块的UI, 因为它没有对Web包的引用.
- 它在同一进程中托管应用层和领域层 (这就是为什么它引用对数据库集成包).
- 此应用程序提供了模块的HTTP API(它的主要目标).
- C) 显示模块UI但是不托管应用层的应用程序(仅将其用作由应用程序A或B托管的远程服务)
- 添加对Web和HttpApi.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托管);
- 添加对HttpApi和HttpApi.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 包.
- Application.Contracts 包(项目) 命名为*CompanyName.ModuleName.Application.Contracts,包含应用服务接口和相关的数据传输对象(DTO).
基础设施层
- 推荐 为每个orm/数据库集成创建一个独立的集成包, 比如Entity Framework Core 和 MongoDB.
- 推荐 例如, 创建一个抽象Entity Framework Core集成的CompanyName.ModuleName.EntityFrameworkCore 包. ORM 集成包依赖于 Domain 包.
- 不推荐 依赖于orm/数据库集成包中的其他层.
- 推荐 为每个主要的库创建一个独立的集成包, 在不影响其他包的情况下可以被另一个库替换.
HTTP 层
- 推荐 创建命名为CompanyName.ModuleName.HttpApi的HTTP 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.Web的 Web包. 包含页面,视图,脚本,样式,图像和其他UI组件.
- Web 包仅依赖于 HttpApi 包.