Documentação
O que é o Nexo
Um mediator CQRS e uma abstração de dados no mesmo pacote, desenhados para que o handler só fale de entidades.
A ideia
O Nexo junta duas coisas que quase todo projeto .NET escreve de novo: um mediator para commands, queries e events, e uma camada de acesso a dados que não seja um repositório por agregado. A união não é acidental — é dela que sai o ganho real.
Quando o mediator conhece o unit of work, o handler para de ser responsável pelo ciclo de vida da transação. Ele declara o que precisa (ICreate<Product>, IQueryable<Order>), faz o trabalho, e termina. O pipeline abre a transação, salva uma vez, despacha os eventos de domínio e faz commit. Se algo falhar, nada é gravado.
public class ProductService(AppDbContext db, IMediator mediator)
{
public async Task<Guid> CreateAsync(CreateProductDto dto, CancellationToken ct)
{
using var tx = await db.Database.BeginTransactionAsync(ct);
var category = await db.Categories
.FirstOrDefaultAsync(c => c.ExternalId == dto.CategoryId, ct);
if (category is null) throw new NotFoundException();
var product = new Product { /* ... */ };
db.Products.Add(product);
await db.SaveChangesAsync(ct); // 1
db.PriceHistory.Add(new PriceHistory
{
ProductId = product.Id, // precisa da chave
Price = dto.Price,
});
await db.SaveChangesAsync(ct); // 2
await mediator.Publish(new ProductCreated(product.Id), ct);
await db.SaveChangesAsync(ct); // 3
await tx.CommitAsync(ct);
return product.ExternalId;
}
}public sealed class CreateProductHandler(
ICreate<Product> create,
IReadByExternalId<Category> categories)
: ICommandHandler<CreateProduct, Guid>
{
public async Task<Guid> HandleAsync(CreateProduct command, CancellationToken ct)
{
var category = await categories.ReadRequiredAsync(command.CategoryId, ct);
var product = await create.CreateAsync(
new Product(category.Id, command.Sku, command.Price),
ct);
return product.ExternalId;
}
}
// A linha de histórico é escrita pelo handler de ProductCreated,
// que a própria entidade registrou no construtor. Mesma transação.A versão de baixo não é só mais curta. Ela é atômica (uma transação, um save), não tem NotFoundException escrita à mão, não precisa da chave do banco para funcionar, e não publica evento nenhum — a entidade registra, o pipeline despacha.
O que tem no pacote
| Pacote | Conteúdo |
|---|---|
Nexo.Abstractions | Contratos sem dependência nenhuma: mensagens, handlers, IPipeline, operações de dados, IUnitOfWork, Page<T>, traits de entidade e exceções. |
Nexo | O mediator, o registro na DI e os pipelines embutidos: logging, timeout, enriquecimento e unit of work. Traz dentro o gerador de código que registra os handlers em tempo de compilação. |
Nexo.EntityFrameworkCore | Implementação sobre um DbContext, mais auditoria, soft delete, operações em lote, outbox, health checks e coleta de eventos de domínio. |
Nexo.FluentValidation | Pipeline de validação que reporta falhas de forma agnóstica. |
Nexo.Testing | Host de teste, registro de despachos e usuário fixo, para testar handlers com o pipeline de verdade. |
Alvo: net10.0, com EF Core 10 e Microsoft.Extensions.* 10. O .NET 8, o .NET 9 e o EF Core 9 saem de suporte em novembro de 2026, então o 1.0 nasce só na versão LTS atual; o net11.0 entra quando sair.
Os princípios
- O handler declara capacidade, não infraestrutura.
ICreate<Product>diz exatamente o que aquele handler pode fazer. UmDbContextinjetado diz "qualquer coisa". - Atomicidade é padrão, não disciplina. Salvar uma vez no fim é o que faz três escritas serem uma mudança. Um auto-save por operação parece conveniente e entrega escrita não atômica.
- Nada é obrigatório. Auditoria, soft delete, eventos de domínio, identificador público: cada um liga porque a entidade implementou uma interface. Uma entidade que não implementa nada se comporta como POCO puro.
- Falha cedo, e no lugar certo. Dois handlers no mesmo comando falham no start-up. Um handler que não existe traz o nome exato do tipo que faltou registrar.
- Sem vazamento de biblioteca. A falha de validação chega como
ValidationFailedException, deNexo.Abstractions. Sua camada web nunca referencia FluentValidation nem EF Core.
Por onde começar
- Primeiros passos — instalar, configurar e escrever o primeiro handler.
- CRUD completo — o sample que exercita todos os casos de uso, endpoint por endpoint.
- Unit of work — o que exatamente acontece em volta do seu handler.