Desarrollando una API en ASP.NET con CQRS y MediatR:第 1 部分
介绍
什么是 CQRS?
在 ASP.NET Core 上实施ando CQRS
参考文献
介绍
在本次公开活动中,我们将探索之前的一些场合中的主题,但我们将在一系列文章中深入探讨并开始探索创建服务网络的不同赞助者。
CQRS 的大力支持者,是我的守护者,是我在过去的系统中预先确定的。 CQRS 是一个很好的选择,现在已经是一个重要的时刻,但他对我们的实验没有任何意义。
Esperamos que este artículo te resulte utile。例如,如果您在 GitHub 存储库中遇到了与代码相关的问题,请不要访问该代码。
什么是 CQRS?
在前面的公共场合,请注意使用 CQRS 的优点是一个出色的想法,尤其是与MediatR图书馆相关的工作。在不同时代的公共场合,媒体与系统的各种方面都具有完美的联系。在这个场合,请重新检查 CQRS。
命令查询职责分离是 CQRS 的标志,它是最近流行的一种管理方式。 CQRS 的基本理念是我们在相关原则上的应用的主要区别:
- Comandos(命令):负责修改国家主权,并且不具有幂等性。
- 咨询(查询):获取国家主权信息并代表幂等操作。
如果有 CRUD(创建、查看、更新和更新),则操作人员对应于创建(创建)、更新(更新)和删除(删除) ,以及与浏览(读取)操作相关的咨询信息。
图像的功能与责任是分开的:
一般来说,应用程序分为基本概念:命令(命令)和咨询(查询)。 CQRS 的主要理念包括在讲授和书写中划分数据和其他数据(统一大师和其他复制品)、逻辑划分的概念、有效管理系统的功能,以及在系统中的使用。 Base de datos(aunque también esfacible Implementar el uso de bases de datos físicamente separadas)。
¿Qué 问题是意图解决者吗?
El enfoque tradicional para diseñar aplicaciones en "n-capas" Generalmente implica dividirlas en tres capas: Interfaz de usuario, Lógica de Negocio y Almacenamiento de Datos.
原则上,这是没有任何问题的,但外科医生会遇到困难,并会根据新的特点灵活调整,消除问题和其他问题。
“n-capas”系统是一个包含大量存储库的菜单终端,可实现所有操作。一切服务都非常重要。
这是在系统管理中责任分离的关键。修改功能不会影响不同的区域。想象一下,所有的骆驼类别ProductosService
都与产品相关的操作相关。这就是解决有关系统问题的问题,并为您提供新的装备和曲线。如果您需要对功能进行修改,那么它是一种自然的连体算法,您可以在服务或存储库中使用该功能。
查询、命令和垂直分段(Características) 的分离允许管理和组织。新的功能很简单,可以通过查询或命令来修改服务或巨大的存储库。
Además,esta estructura facilita las pruebas。一个服务完全依赖于各种操作,这对于特定功能的“模拟”系列来说是必要的。在实施过程中,联合国指挥部单独包括功能上的必要性、影响其他功能的罪过。 Cada Command 是封装的,并且可以修改其他内容。
当然,重构也是很重要的。如果联合国指挥部实现了其他指挥部的目标,则可以考虑其他赞助人的战略、装饰和实现重构。 Además,esencial encontrar un equalibrio entre no repetir código (DRY - Don't Repeat Yourself) y cumplir con el principio de Responsabilidad Única (aunque puede ser un desafío, con el tiempo te acostumbrarás)。
调解员赞助人
媒介守护者简单地定义了一个对象,将其他对象封装在其中并进行交互。与其他对象直接相关的其他对象一样,这些对象直接依赖于“中介”,并且是中介者之间的交互管理:
Como se muestra en el diagrama, SomeService
envía un mensaje al mediador, y el mediador a su vez llama a otros servicios para que realicen acciones basadas en el mensaje recibido.SomeService
没有必要采取任何其他服务措施;单独通信 al mediador lo que necesita que se haga。
中介者的支持者是控制反转(IoC) 的支持者。不允许将完整组件拆下,但也允许进行交互。请务必注意功能组件,首先要注意以下事项、注意事项和注意事项。
MediatR:CQRS 实施设施和 Mediador 赞助人
MediatR 是一个在应用程序(进程中)中完成的守护者调解器实现,也是 CQRS 基础系统创建的基础。 Toda la comunicación entre el usuario y la capa de persistencia se gestiona a través de MediatR .
这是重要的 MediatR se ejecuta dentro del Mismo proceso ( in-process ),lo que es una limitación clave。 Dado que .NET maneja todas interacciones entre objetos en elmismo processes, MediatR no es apropiado si deseamos separar los Queries y Commands en aplicaciones distintas (es decir, sibuscamos sistemas completamente independientes)。
对于需要分离的场景,最好使用 Message Broker,以便在前面的公共讨论中进行讨论。
在 ASP.NET Core 上实施ando CQRS
在 ASP.NET Core 中实现 CQRS 的想法,特别是在 Web API 中,是委托处理请求(请求)和“管理器”(处理程序)的责任,在控制者中进行操作,例如前面提到的。
这是什么?但最重要的是,API 的关心程序不依赖于控制者的直接支持。在这样的情况下,委托人应承担“中央应用程序”(siguiendo los principios de Clean Architecture o Vertical Slices)的责任。
在 .NET 7 中,可以使用最小 API 来获得最佳性能。如果您无法控制整个过程,请单独接受请求,但可能会带来不便。
在 ASP.NET Core 中使用 MediatR 来实现 CQRS(例如,作为 SQLite 数据的基础),在 Web API 项目中使用这些组件(例如创建dotnet new webapi
):
<PackageReference Include="Swashbuckle.AspNetCore" Version="6.2.3" />
<PackageReference Include="FluentValidation.AspNetCore" Version="10.4.0" />
<PackageReference Include="MediatR.Extensions.Microsoft.DependencyInjection" Version="10.0.1" />
<PackageReference Include="Microsoft.EntityFrameworkCore.Sqlite" Version="6.0.3" />
<PackageReference Include="Microsoft.EntityFrameworkCore.Design" Version="6.0.3">
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
<PrivateAssets>all</PrivateAssets>
</PackageReference>
Puedes ignorar el código de ejemplo que viene con la plantilla (como las clases Weather, entre otras) y trabajar con la siguiente estructura en un alone proyecto (aunque a largo plazo, es recomendable recognizer como dividir tus proyectos, ya sea en dos o más proyectos dentro de una Misma solución 等):
Controllers/
Domain/
Features/
├─ Products/
Infrastructure/
├─ Persistence/
En este ejemplo, estamos siguiendo los Conceptos típicos que usaríamos en una arquitectura limpia (Clean Architecture)。没有什么重要的事情是在一个单独的项目中发生的; Con el tiempo, podrás tomar Decisiones sobre cómo dividir tus proyectos (en uno o varios proyectos dentro de la Misma solución, 等等)。
领域
在这部分,没有太多解释,你可以简单地使用它Product
:
namespace MediatrValidationExample.Domain;
public class Product
{
public int ProductId { get; set; }
public string Description { get; set; } = default!;
public double Price { get; set; }
}
这是重要的注意事项,对于我们来说,操作员default!
简单地对字符串进行初始化,并确定编译器的值null
。重要的是,这在技术上是不正确的,也是在字符串中预先确定的null
。 Son detalles técnicos divertidos,¡pero no siempre son precisos!
基础设施 → 持久性
在成本方面,我们使用 Entity Framework Core 来实现持久化。数据基础上下文的定义:
using MediatrValidationExample.Domain;
using Microsoft.EntityFrameworkCore;
namespace MediatrValidationExample.Infrastructure.Persistence;
public class MyAppDbContext : DbContext
{
public MyAppDbContext(DbContextOptions<MyAppDbContext> options) : base(options)
{ }
public DbSet<Product> Products => Set<Product>();
}
最初创建数据和迁移基地,执行任务:
dotnet ef migrations add FirstMigration -o Infrastructure/Persistence/Migrations
dotnet ef database update
功能 → 产品 → 查询
此部分代表应用程序的核心,以及 Web API 所需的咨询和命令。请注意咨询产品。
大多数建筑的形式是咨询和指挥的形式,是垂直分段建筑的最新采用的实践。如果您想获取更多有关主题的信息,请参阅有关文章。
在简历中,la idea es colocar todo lo necesario en un alone archivo(la solicitud、el manejador、los validadores、los mapeadores、los modelos 等)。 Como menciono en el artículo, si es necesario, se puede refactorizar (aunque es otro tema, pero queda a tu criterio cómo hacerlo)。
using MediatR;
using MediatrValidationExample.Infrastructure.Persistence;
namespace MediatrValidationExample.Features.Products.Queries;
public class GetProductQuery : IRequest<GetProductQueryResponse>
{
public int ProductId { get; set; }
}
public class GetProductQueryHandler : IRequestHandler<GetProductQuery, GetProductQueryResponse>
{
private readonly MyAppDbContext _context;
public GetProductQueryHandler(MyAppDbContext context)
{
_context = context;
}
public async Task<GetProductQueryResponse> Handle(GetProductQuery request, CancellationToken cancellationToken)
{
var product = await _context.Products.FindAsync(request.ProductId);
return new GetProductQueryResponse
{
Description = product.Description,
ProductId = product.ProductId,
Price = product.Price
};
}
}
public class GetProductQueryResponse
{
public int ProductId { get; set; }
public string Description { get set; } = default!;
public double Price { get; set; }
}
最重要的是要注意接口方面的问题IRequest<T>
。IRequestHandler<T>
IRequest<T>
es el mensaje que especica la tarea a realizar, solicitado por SomeService y dirigido a uno o más manejadores (como se muestra en la imagen front)。
在其他巴拉布拉斯,El mediador tomará la IRequest<T>
y la enviará los manejadores registrados。请将其放置在卡波拉塔雷亚角。
因此,这GetProductQuery
代表IRequest<T>
了产品的总线。IRequest<T>
包括关于具体目标的一般类型,您可以这样做,实现协商并获得国家主权。
在其他时间里,我们创造了ProductsService
一种ProductsRepository
方法GetById
。禁运,在这种情况下,该类代表了一种实现操作,在一种额外的方法中,在一种有多种方法的类中。
Esto es lo que me encanta de este patrón;因此,许多档案和地毯、档案和公共设施都可以作为文本编辑器和集成开发工具 (IDE) 的强大工具。
El GetProductQueryHandler
es el manejador del Mismo Query
que definimos anteriormente. Dado que ambos están en el Mismo archivo, podríamos decir que el Request
y el Handler
están acoplados entre sí, pero aislados del Resto del codigo. Agregar funcionalidad o probarla simplemente implica trabajar con lo que se encuentra en este archivo y nada más。
using MediatR;
using MediatrValidationExample.Infrastructure.Persistence;
using Microsoft.EntityFrameworkCore;
namespace MediatrValidationExample.Features.Products.Queries;
public class GetProductsQuery : IRequest<List<GetProductsQueryResponse>>
{
}
public class GetProductsQueryHandler : IRequestHandler<GetProductsQuery, List<GetProductsQueryResponse>>
{
private readonly MyAppDbContext _context;
public GetProductsQueryHandler(MyAppDbContext context)
{
_context = context;
}
public Task<List<GetProductsQueryResponse>> Handle(GetProductsQuery request, CancellationToken cancellationToken) =>
_context.Products
.AsNoTracking()
.Select(s => new GetProductsQueryResponse
{
ProductId = s.ProductId,
Description = s.Description,
Price = s.Price
})
.ToListAsync();
}
public class GetProductsQueryResponse
{
public int ProductId { get; set; }
public string Description { get; set; } = default!;
public double Price { get; set; }
}
在其他情况下,IRequest<T>
如果您需要公共汽车产品、分页、订购等,则可以在其他类别中使用GetProductsQuery
,这代表了 API 请求(lo veremos en el controlador)。
所有事项包括Queries
方法、咨询方法AsNoTracking
以及实现实体国家的必要条件。
功能 → 产品 → 命令
指挥官的儿子最终完成了实际行动。在公共场合,最重要的是验证、装饰和其他功能,以及FluentValidation和MediatR 的其他图书馆的实施工具,以及您的使用情况。
using MediatR;
using MediatrValidationExample.Domain;
using MediatrValidationExample.Infrastructure.Persistence;
namespace MediatrValidationExample.Features.Products.Commands;
public class CreateProductCommand : IRequest
{
public string Description { get; set; } = default!;
public double Price { get; set; }
}
public class CreateProductCommandHandler : IRequestHandler<CreateProductCommand>
{
private readonly MyAppDbContext _context;
public CreateProductCommandHandler(MyAppDbContext context)
{
_context = context;
}
public async Task<Unit> Handle(CreateProductCommand request, CancellationToken cancellationToken)
{
var newProduct = new Product
{
Description = request.Description,
Price = request.Price
};
_context.Products.Add(newProduct);
await _context.SaveChangesAsync();
return Unit.Value;
}
}
Aquí,la única información que necesitamos del request son el nombre del products que deseamos registrar and su precio.使用 MediatR 接口时IRequest
,如果不需要通用类型,则需要提供一般信息。
控制者
控制者的声音,最终使用媒体。实践中的用途:
using MediatR;
using MediatrValidationExample.Features.Products.Commands;
using MediatrValidationExample.Features.Products.Queries;
using Microsoft.AspNetCore.Mvc;
namespace MediatrValidationExample.Controllers;
[ApiController]
[Route("api/products")]
public class ProductsController : ControllerBase
{
private readonly IMediator _mediator;
public ProductsController(IMediator mediator)
{
_mediator = mediator;
}
[HttpGet]
public Task<List<GetProductsQueryResponse>> GetProducts() => _mediator.Send(new GetProductsQuery());
[HttpPost]
public async Task<IActionResult> CreateProduct([FromBody] CreateProductCommand command)
{
await _mediator.Send(command);
return Ok();
}
[HttpGet("{ProductId}")]
public Task<GetProductQueryResponse> GetProductById([FromRoute] GetProductQuery query) =>
_mediator.Send(query);
}
依赖关系的旅行,请求使用国际间的中介IMediator
。最初的请求信函IRequest
、简单的调解方式、确定的请求人的决定因素。
En CreateProduct
, la IRequest
(también conocida como comando) se recibe desde elcuerpo de la solicitud (como una clase POCO), 允许在遇到问题时进行重复和序列化。
En GetProductById
, la IRequest
(también conocida como Consulta) se obtiene del segmento de la URL.这是一个重要的名称,与该游戏的相关属性一致。
是的GetProducts
,在请求手册中,您没有设置任何附加请求,但请使用[FromQuery]
附加请求参数。
结论
您可以使用 MediatR 和ASP.NET Core Web API项目来配置 CQRS 。
Vimos 封装了新 API 的功能,用于个人存档,代表了咨询或命令。
使用 CQRS 可以增强各种条件,但也有一些缺点。一个 Medida que el sistema crece,cada miembro del 设备体验 en este patrón tentrá que superar una curva de aprendizaje。 Sin 禁运,en ultima instancia,es para un bien 市长。
该系统管理系统是一个解决方案和解决方案的元数据,您可以在未来的系统中管理系统。这是一个痛苦的过程,是一个最伟大的国家。
我们的概念部门有很多关于设备的最新项目。 Agregar funcionalidad o modificarla no debería ser una tarea complicada。
参考文献
- 使用 MediatR 和 FluentValidation 的 CQRS 验证管道 - Code Maze (code-maze.com)
- ASP.NET Core 中的 CQRS 和 MediatR - Code Maze (code-maze.com)
- 使用 Web API 实现微服务应用层 | Microsoft Docs