暂无图片
暂无图片
暂无图片
暂无图片
暂无图片

.NET框架中的战斗机:Abp vNext

云筑网技术团队 2021-09-24
894

云筑网技术团队

助推建筑产业互联网


子曰:工欲善其事,必先利其器。作为新生代农民工
的我们,手中的这把“锄头”必须要十分趁手才能在挖墙脚
比赛中获得胜利。
老铁扯远了,日常生活中可不建议大家这么做,回到我们今天的正题,作为一名合格的.NETer,“锄头”这事儿可不能落下,毕竟是吃饭的家伙事儿。这把“锄头”有自家锻造的,也有开源社区大佬锻造的,各家的“锄头”在自家这块地里面发挥着不可替代的作用,可谓"百架争鸣"。目前社区里面此类框架比较多,这里不做赘述,今天给大家带来的是框架中的战斗机:Abp vNext
读完这篇文章你可学到:
  • 什么是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
    来存储和可视化日志。

项目整体架构图如下:
以上两个项目,有兴趣的朋友可以从Github上拉取源码,在本地把玩一番。

多租户

多租户指的是一种软件架构,这种架构可以支持一个软件实例在服务器上运行并为多个租户服务。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
更多命令选项请参考:https://docs.abp.io/zh-Hans/abp/latest/CLI
  • 清理模板项目

使用官方模板创建的解决方案里面包含了一些我们用不到的项目(如WebAssembly相关的项目),因此开始之前我们先整理一下它,让项目更加简洁,以下是官方默认模板重新整理以后的项目结构:
.Domain.Shared项目:项目包含常量、枚举和其他对象,这些对象实际上是领域层的一部分,但是解决方案中所有的层/项目中都可以使用它。
.Domain项目:领域层。它主要包含实体、聚合根、领域服务、值类型、仓储接口和解决方案中其他的领域对象。
.Application.Contracts项目:项目主要包含应用服务接口和应用层的数据传输对象(DTO),它用于分离应用层的接口和实现。
.Application项目:项目包含.Application.Contracts
项目的应用服务接口实现。
.EntityFrameworkCore项目:集成了EntityFramework Core
项目,它定义了业务DbContext
并实现了.Domain
项目中定义的仓储接口。
.HttpApi项目:定义服务Controller,为模块提供REST风格的HTTP API。
下图展示了模块的层和项目的依赖关系:
  • 初始化基础数据库

官方模版中,将基础数据和业务数据从数据库层面隔离开来,因此第一步我们需要把基础库进行初始化,设置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命令测试创建接口:
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

作 者:赵轶(派大星)
审 稿:吴友强(技巅)
编 :周旭龙(爱迪生)


往期回顾

01
MySQL 8.0双写负优化
02
如何设计一个通用点赞系统
03
.NET应用全方位体检


文章转载自云筑网技术团队,如果涉嫌侵权,请发送邮件至:contact@modb.pro进行举报,并提供相关证据,一经查实,墨天轮将立刻删除相关内容。

评论