
云筑网技术团队
助推建筑产业互联网
前言
子曰:工欲善其事,必先利其器。作为 新生代农民工
的我们,手中的这把“锄头”必须要十分趁手才能在挖墙脚
比赛中获得胜利。
什么是
Abp vNext
框架?Abp vNext
框架提供了什么能力?如何基于
Abp vNext
框架构建一个微服务项目?使用
Abp vNext
框架构建项目时需要注意什么?从菜鸟到成神学习路线
Abp vNext的”生辰八字”
Abp vNext框架的“身世”,我们先讲讲ASP.NET Boilerplate这个项目。
ASP.NET Boilerplate中文译作
ASP.NET样板项目,项目创建者是一名土耳其架构师Halil İbrahim Kalkan,江湖人称“土牛”,
ASP.NET Boilerplate自2013年创建以来,截至目前在Github上斩获9.8K+的Star,当前最新版本为6.4,项目同时支持
.Net Framework和
.Net Core,
ASP.NET Boilerplate自5.0版本后底层框架全面采用
.Net Core 3.x,但是目前项目处于比较缓慢的更新状态,因为
ABP团队把大部分精力放到了今天的主角身上。
ASP.NET Boilerplate是一个用最佳实践和流行技术开发的现代WEB应用,它旨在成为一个通用的WEB应用程序框架和项目模板。但是
ASP.NET Boilerplate本身是不对微服务架构做支持的,在目前微服务技术大背景下,难免有点"不合群",但是在单体应用中
ASP.NET Boilerplate可谓是一把非常趁手的"锄头",国内
52ABP项目也在其基础上做了很多补充,有兴趣的朋友可以去了解一下。因此在这样的背景下,
Abp vNext诞生了!

Abp vNext是
ABP团队从2017年开始基于
.Net Core对
ASP.NET Boilerplate的重写版本,并且正式命名为
Abp Framework,也称
Abp vNext或者
ABP,目前已经更新到4.4.2版本,并且全面兼容
.Net5,同时在微软正式发布
.Net6以后,
Abp vNext也将会快速支持,现目前的
Abp vNext可谓正值当打之年!
Abp vNext的:
ABP Framework is a complete infrastructure based on the ASP.NET Core to create modern web applications and APIs by following the software development best practices and the latest technologies.
Abp vNext提供了一个完整的、模块化和分层的软件架构,基于领域驱动设计原则和模式,它还提供了一些列必要的基础设施以支撑这一架构。
Abp vNext框架适用于微服务解决方案,同时也适用于单体应用,它通过遵循软件开发的最佳实践和最新技术来创建现代网络应用程序和API。
麻雀虽小,五脏俱全
Abp vNext框架提供了很多基础功能,如DDD基础设施、分布式事件总线、多租户、审计日志、BLOB存储、认证与授权、数据过滤等,做到了开箱即用而开发者无需关注具体细节问题,让我们更加专注业务的实现!

模块化
Abp vNext
框架被设计用来支持建立完全模块化的应用程序和系统,每个模块都可能有实体、服务、数据库集成、API、UI组件等等。
Abp vNext推荐将你的应用程序进行模块化拆分,一个模块对应一个业务的封装,这样实现了松耦合的代码组织方式,我们在开发应用程序时就像搭积木一样,可以快速地构建出一个应用。
Abp vNext中模块分为两种类型:框架模块和应用程序模块,它们没有任何结构差异,只是按功能和用途进行分类:
框架模块:这些是框架的核心模块,如缓存、电子邮件、主题化、安全、序列化、验证、EF Core集成、MongoDB集成等。它们没有业务功能,但通过提供通用的基础设施、集成和抽象,使你的日常开发更容易。
应用程序模块:这些模块实现了特定的业务功能,如博客、文件管理、身份管理、租户管理等。它们通常有自己的实体、服务、API和UI组件。
领域驱动设计基础设施
领域驱动设计(Domain Driven Design),简称DDD, Abp vNext
框架给我们提供了完善的领域驱动设计基础架构,包括了仓储、领域服务、实体与聚合、值对象等基础设施,以便我们在实践DDD时开发更易于实现。
Abp vNext框架中严格遵循DDD原则和模式去实现分层应用程序模型,该模型由四个基本层组成:
表示层:为用户提供接口,同时结合应用层实现与用户交互。
应用层:表示层与领域层的中介,主要负责业务编排、领域对象执行特定操作的应用程序服务。
领域层:包含业务对象以及领域业务逻辑。主要构成包括实体、值对象、聚合和聚合根、仓储、领域服务、规约和领域事件等,是应用程序的核心。
基础设施层:提供通用的技术(主要是第三方类库)功能,同时贯穿整个应用,以向各个层提供通用功能。

微服务架构
微服务是一种软件开发技术,是面向服务架构(SOA)架构风格的一个变种,它将一个应用程序构造成松散耦合的服务集合。 Abp vNext
提供了便捷的基础设施以便我们能够快速地创建微服务解决方案。
Abp vNext框架提供了
MicroserviceDemo、
eShopOnAbp两个微服务演示方案,其中
eShopOnAbp的目标是创建一个功能齐全的云原生微服务参考应用程序,目前项目还处于起步阶段,还没有任何业务实现,只是提供了租户管理、身份服务器等基础模块,项目整体架构图如下:

MicroserviceDemo提供了一个简单而完整的微服务解决方案:
拥有多个独立的、可自我部署的微服务。
拥有多个WEB应用程序,每个都使用不同的API网关。
使用
Ocelot
库开发的多个API网关。拥有一个使用
IdentityServer4
框架开发的认证服务。它也是一个具有UI的SSO(单点登录)应用程序。拥有多个数据库。一些微服务有自己的数据库,而一些服务/应用则共享一个数据库。
拥有不同类型的数据库。SQL Server(带有Entity Framework Core ORM)和MongoDB。
有一个控制台应用程序,展示通过认证使用服务的最简单方法。
使用
Redis
实现分布式缓存功能。使用
RabbitMQ
进行服务对服务的信息传递。使用
Docker
和Kubernetes
来部署和运行所有服务和应用程序。使用
Elasticsearch
和Kibana
来存储和可视化日志。

多租户
多租户指的是一种软件架构,这种架构可以支持一个软件实例在服务器上运行并为多个租户服务。 Abp vNext
框架不仅支持开发多租户应用程序,而且开发者几乎无需知道多租户底层是如何构建的。
Abp vNext框架提供了开箱即用的基础应用模块,只需要简单的几个步骤就能快速地集成它,提供了以下基础功能:
可以自动解析租户(框架默认提供了
CurrentUserTenantResolveContributor
、QueryStringTenantResolveContributor
、FormTenantResolveContributor
、RouteTenantResolveContributor
、HeaderTenantResolveContributor
、CookieTenantResolveContributor
以及自定义租户解析器),将不同租户的数据相互隔离。支持单一数据库、每个租户独立数据库或者混合方式部署数据库服务。

上手玩一玩
光说不练假把式,接下来我们用一个简单的虚拟项目来演示如何挥动 Abp vNext
这把“锄头”,来快速构建一个满足微服务架构的应用程序。
Abp vNext框架为我们提供了两种方式来快速使用官方模版创建一个解决方案,直接下载的方式不做演示,请各位朋友自行前往官网下载,这里我们使用官方推荐的命令工具来初始化我们的项目:
使用命令行窗口安装
ABP CLI
dotnet tool install -g Volo.Abp.Cli
dotnet tool update -g Volo.Abp.Cli
创建解决方案
abp new Yzw.BookStore -t module

清理模板项目

.Application.Contracts项目的应用服务接口实现。
EntityFramework Core项目,它定义了业务
DbContext并实现了
.Domain项目中定义的仓储接口。

初始化基础数据库
Yzw.BookStore.IdentityServer项目为启动项目,并打开包管理器键入以下命令:
PM> Update-Database -Verbose -Context IdentityServerHostMigrationsDbContext

创建Book实体
Yzw.BookStore.Domain项目中创建一个
Books的文件夹并创建一个名为
Book的类,然后继承自
AuditedAggregateRoot<TKey>审计聚合根基类:
public class Book : AuditedAggregateRoot<Guid>
{
public string Name { get; set; }
public DateTime PublishDate { get; set; }
public float Price { get; set; }
}
此处为了方便快速演示,实体属性设置为public的get/set,标准做法请参考DDD最佳实践指南。
将Book添加到BookStoreDbContext中
public class BookStoreDbContext : AbpDbContext<BookStoreDbContext>, IBookStoreDbContext
{
public DbSet<Book> Books { get; set; }
...
}
将Book实体映射到数据库表
public static void ConfigureBookStore(
this ModelBuilder builder,
Action<BookStoreModelBuilderConfigurationOptions> optionsAction = null)
{
...
builder.Entity<Book>(b =>
{
b.ToTable(BookStoreDbProperties.DbTablePrefix + "Books", BookStoreDbProperties.DbSchema);
b.ConfigureByConvention();
b.Property(x => x.Name).IsRequired().HasMaxLength(128);
});
}
添加数据库迁移脚本
Yzw.BookStore.HttpApi.Host项目为启动项目,并打开包管理器键入以下命令:
PM> Add-Migration Add_Book_Entity -Context BookStoreHttpApiHostMigrationsDbContext
PM> Update-Database -Verbose -Context BookStoreHttpApiHostMigrationsDbContext

创建Book服务接口
Yzw.BookStore.Application.Contracts项目中创建一个
Books的文件夹并创建一个名为
IBookAppService的类,然后继承自
ICrudAppService<>基类接口:
public interface IBookAppService : ICrudAppService<
BookDto,
Guid,
PagedAndSortedResultRequestDto,
BookDto>
{
}
Yzw.BookStore.Application.Contracts项目
Books文件夹中创建
BookDto数据传输对象,有关DTO最佳实践请参考官方文档:
public class BookDto : AuditedEntityDto<Guid>
{
public string Name { get; set; }
public DateTime PublishDate { get; set; }
public float Price { get; set; }
}
DTO对象,
Abp vNext框架集成了
AutoMapper,方便我们快速处理实体之间的转换动作。因此需要我们在
Yzw.BookStore.Application项目的
BookStoreApplicationAutoMapperProfile类中定义映射关系:
public class BookStoreApplicationAutoMapperProfile : Profile
{
public BookStoreApplicationAutoMapperProfile()
{
CreateMap<Book, BookDto>();
CreateMap<BookDto, Book>();
}
}
实现Book服务接口
Yzw.BookStore.Application项目中创建一个
Books的文件夹并创建一个名为
BookAppService的类,然后继承自
CrudAppService<>基类以及
IBookAppService接口:
public class BookAppService : CrudAppService<
Book,
BookDto,
Guid,
PagedAndSortedResultRequestDto,
BookDto>, IBookAppService
{
public BookAppService(IRepository<Book, Guid> repository) : base(repository)
{
}
}
创建Book控制器
Yzw.BookStore.HttpApi项目中创建一个
Books的文件夹并创建一个名为
BookController的类,然后继承自
IBookAppService接口以及
BookStoreController基类:
[RemoteService]
[AllowAnonymous]
[Route("api/services/book")]
public class BookController : BookStoreController, IBookAppService
{
private readonly IBookAppService _bookAppService;
public BookController(IBookAppService bookAppService)
{
this._bookAppService = bookAppService;
}
[HttpPost, Route("create")]
public async Task<BookDto> CreateAsync(BookDto input)
{
return await this._bookAppService.CreateAsync(input);
}
[HttpDelete, Route("delete")]
public async Task DeleteAsync(Guid id)
{
await this._bookAppService.DeleteAsync(id);
}
[HttpGet, Route("get")]
public async Task<BookDto> GetAsync(Guid id)
{
return await this._bookAppService.GetAsync(id);
}
[HttpGet, Route("get-all")]
public async Task<PagedResultDto<BookDto>> GetListAsync(PagedAndSortedResultRequestDto input)
{
return await this._bookAppService.GetListAsync(input);
}
[HttpPut, Route("update")]
public async Task<BookDto> UpdateAsync(Guid id, BookDto input)
{
return await this._bookAppService.UpdateAsync(id, input);
}
}
启动项目
Redis服务正常启动,然后分别启动
Yzw.BookStore.IdentityServer以及
Yzw.BookStore.HttpApi.Host项目,项目启动成功后会自动打开一个
Swagger文档,以方便我们调试接口:

curl -X 'POST' \
'https://localhost:44331/api/services/book/create' \
-H 'accept: text/plain' \
-H 'Content-Type: application/json' \
-H 'RequestVerificationToken: CfDJ8Gu1l20x7E1CjHStaI519uv2xPgbDRSF5QnsVIo8UzN8ttam7eWUGFsCU_EwhbzNfWycY6ucz1Es0lpc3atYOzy14PMg-WjiVdQqZx9fHfGmYx3W41vVXckwMzxQWAR-FoS4oJeI-9dLcbwdoIUD8dI' \
-d '{
"id": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
"name": "三体-刘慈欣",
"publishDate": "2021-09-17T09:49:57.367Z",
"price": 55.5
}'
Abp vNext框架构建一个项目全过程,看似繁琐的步骤其实是井井有条、环环相扣,示例项目还未演示诸如领域服务、工作单元等功能,大家可以参考官方文档以进一步了解。
一些问题,一些建议
尽可能手动构建你的解决方案,因为官方模板包含很多几乎不会使用的模块,因此没必要包含到你的项目中,建议按需安装
Abp vNext
提供的模块;建议不启动
Abp vNext
提供的自动生成API服务功能,所有控制器由自己手动构建,这样做的好处就是一切都能自己掌控,而不是按照框架规则来管控你的API;建议不使用
Abp vNext
仓储中提供的批量操作方法,因为官方默认实现采用的是遍历操作,大集合会出现严重的性能问题,建议自行实现;不建议将后台任务和后台工作者寄宿在一个WEB站点下,
Abp vNext
提供了BackgroundJobs
和BackgroundWorker
基础设施,集成了HangFire
和Quartz
,从过往实践来看,不太建议使用这两个功能,建议有相关需求时自行集成;不推荐做WEB项目开发,虽然
Abp vNext
提供了一系列针对WEB开发的基础功能,但还是建议团队践行前后端分离(因为专业的团队应该做专业的事情)模式进行开发。

Abp vNext学习路线
Abp vNext
框架整体还是比较复杂,集成了大量的第三方库以及一系列基础服务,学习时建议循序渐进,不要急于求成,在看官方文档的同时建议按照文档里面的步骤进行实操,这样才能事半功倍。
简单了解领域驱动设计思想;
从头到尾阅读一遍官方文档(建议阅读比较完善的英文版本),这样才能大体知道框架提供了什么功能和服务(如多租户、分布式事务总线等);
快速地把教程中的项目基础功能部分运行起来,理解基本流程和架构;
集中学习几个官方提供的模块(如租户模块、博客模块),理解模块的设计思路,然后举一反三,尝试按照最佳实践实现自己的模块;
学习官方演示微服务项目,理清每个服务之间的关系以及微服务架构设计思路。
总结
Abp vNext的出现可算得上是
.Net界的战斗机了,它提供了一套完整、标准的架构,简化了我们创建解决方案的步骤同时还能很好地契合微服务架构模式,提供了一些列最佳实践指南,很大程度上降低了我们的开发成本,基于DDD最佳实践,也能让我们的项目一直处于健壮的状态,从容应对应对随时变化的需求。但是也建议不一定要完全按照框架的模式来,可以根据团队实际情况进行选择,最主要的是可以把
Abp vNext的设计思路搬移到你的项目当中去,这样才是最大化地落地实践
Abp vNext。





