Nexo

Documentação

Todas as opções

Referência completa de cada opção, com o valor padrão e o motivo dele.

NexoOptions

O construtor de configuração que AddNexo entrega.

MembroPadrãoDescrição
HandlerLifetimeScopedTempo de vida dos handlers descobertos. Scoped combina com o unit of work pelo qual eles escrevem.
MediatorLifetimeScopedTempo de vida de IMediator. Singleton só é aceito com handlers singleton: um mediator singleton resolveria handlers scoped no escopo raiz, e o registro falha em vez de deixar isso acontecer.
Services—A IServiceCollection, para registros que o Nexo não cobre.
C#
services.AddNexo(nexo =>
{
    // Descoberta de handlers
    nexo.AddHandlersFromAssemblyOf<Program>();
    nexo.AddHandlersFromAssemblyOf<Program>(filter: t => !t.IsNested);
    nexo.AddHandlersFromAssemblies(assemblyA, assemblyB);
    nexo.AddHandlersFromAssemblies(filter: t => !t.IsNested, assemblyA, assemblyB);
    nexo.AddHandler<UmHandlerEspecifico>();
    nexo.AddGeneratedHandlers();   // gerado em tempo de compilação para este assembly

    // Pipelines (registrar duas vezes não duplica)
    nexo.AddLoggingPipeline(logging => { /* ... */ });
    nexo.AddTimeoutPipeline();     // para [MessageTimeout]
    nexo.AddEnrichmentPipeline();
    nexo.AddUnitOfWorkPipeline(uow => { /* ... */ });
    nexo.AddPipeline<MeuPipeline>();
    nexo.AddPipeline<MeuPipeline>(ServiceLifetime.Singleton);
    nexo.AddPipeline(sp => new MeuPipeline(sp.GetRequiredService<IFoo>()));
    nexo.AddPipeline(new PipelineSemDependencia());

    // Mediator
    nexo.ConfigureMediator(m => m.EventDispatchMode = EventDispatchMode.Parallel);

    // Usuário ambiente
    nexo.UseCurrentUser<HttpCurrentUserAccessor>();
    nexo.UseCurrentUser(sp => "sistema");

    // Identificador público das entidades (padrão: UUID v7)
    nexo.UseExternalIdGenerator(ExternalIdGenerators.SqlServer);

    // Outros bancos, outbox e inbox (Nexo.EntityFrameworkCore)
    nexo.UseDbContext<BillingDbContext>();
    nexo.UseDbContext<ActivityDbContext>(session => session.UseTransactions = false);
    nexo.UseOutbox(outbox => outbox.PollingInterval = TimeSpan.FromSeconds(1));
    nexo.UseInbox();
});

MediatorOptions

OpçãoPadrãoDescrição
EventDispatchModeSequentialComo os handlers de um evento rodam. Sequencial é o padrão porque eles compartilham o escopo, e portanto o DbContext, que não é thread-safe. Parallel agrega as falhas via Task.WhenAll.

UnitOfWorkPipelineOptions

OpçãoPadrãoDescrição
SaveChangesOnCommandtrueSalva depois que o handler de comando retorna. É o que permite ao handler apenas parar.
SaveChangesOnEventtrueSalva depois de um evento publicado de fora. Eventos publicados de dentro de um comando entram no unit of work dele, independente disso.
UseTransactiontrueEnvolve handler, save e eventos numa transação, com a execution strategy do provider. Ignorado em provider sem transação.
DispatchDomainEventstrueColeta os eventos das entidades alteradas — antes do save, para não perder os de uma entidade removida — e os despacha depois dele, na mesma transação. Eventos de integração vão para o outbox, se houver um.
MaxDomainEventRounds4Quantas rodadas de cascata são permitidas. Estourar aborta a transação em vez de entrar em laço.
WarnOnPendingQueryChangestrueRegistra warning quando um handler de query deixa mudanças pendentes — quase sempre uma escrita que vazou.

LoggingPipelineOptions

OpçãoPadrãoDescrição
LevelDebugNível das entradas de início e sucesso.
FailureLevelErrorNível quando o handler lança algo inesperado.
ExpectedFailureLevelInformationNível das falhas que são respostas e não incidentes: validação, não encontrado, regra de negócio, chave duplicada, conflito de concorrência, cancelamento pedido.
IsExpectedFailurea lista acimaDecide quais exceções são esperadas.
LogPayloadfalseDesligado de propósito. Comando carrega senha, token e dado pessoal; um default que vaza isso é passivo, não recurso.
LogResultfalseSerializa o resultado. Raramente vale o volume.
SerializerSystem.Text.JsonComo o payload virou texto. Substitua por um serializador que redija campos.

Todo despacho abre um escopo de log com NexoMessageKind, NexoMessageName e NexoMessageId, então qualquer linha escrita dentro do handler já sai correlacionada.

ValidationPipelineOptions

OpçãoPadrãoDescrição
ValidateCommandstrueComandos mudam estado.
ValidateQueriestrueMantém filtros e paginação sãos.
ValidateEventsfalseUm evento descreve algo que já aconteceu; rejeitar depois do fato raramente ajuda.
FailIfValidatorNotFoundfalseLigue quando o time decidir que todo comando tem de ter validator, e você quiser que o container cobre isso.

DbSessionOptions

Um conjunto por contexto. services.Configure<DbSessionOptions>(…) define o padrão de todos os bancos; o callback de AddNexo<TContext> ou de UseDbContext<TContext> ajusta um deles, partindo desse padrão.

OpçãoPadrãoDescrição
SaveBehaviorUnitOfWorkQuando as mudanças vão ao banco. Immediate salva a cada operação: conveniente num script, mas abre mão da atomicidade.
TrackReadstrueLeituras por chave voltam rastreadas. IQueryable injetado nunca rastreia, independente disso.
SoftDeletetrueIDelete marca entidades ISoftDeletable em vez de removê-las.
ApplyAudittrueLiga as estampas do AuditInterceptor.
AssignMissingExternalIdstrueAtribui um identificador no insert quando o campo está vazio, com o gerador de UseExternalIdGenerator.
UseTransactionsnullnull detecta pelo provider: relacional sim, in-memory e MongoDB não. Defina true para um MongoDB em replica set. Sem transação, num contexto não relacional, o Nexo também desliga a transação automática do provider.
C#
// O padrão de todos os bancos.
services.Configure<DbSessionOptions>(session => session.TrackReads = true);

services.AddNexo<CatalogDbContext>(nexo => nexo
    // Herda TrackReads = true, sobrescreve o resto.
    .UseDbContext<ActivityDbContext>(session =>
    {
        session.UseTransactions = false;
        session.SoftDelete = false;
    }));

// Conferindo o que valeu, por banco:
var sessions = scope.ServiceProvider.GetRequiredService<DbSessionProvider>();
sessions.ForEntity<ActivityEntry>().Options.SoftDelete;   // false
sessions.ForEntity<Product>().Options.SoftDelete;         // true

OutboxOptions

Passadas em nexo.UseOutbox(outbox => …). Veja Outbox.

OpçãoPadrãoDescrição
RunDispatchertrueRegistra o serviço em background que entrega os eventos. Desligue para chamar OutboxProcessor.ProcessAsync de um agendador seu.
PollingInterval2 sO intervalo entre passadas quando não há nada pendente.
BatchSize50Quantos eventos uma passada tenta entregar.
MaxAttempts10Depois disso o evento fica na tabela, com o último erro, para uma pessoa olhar.
RetryDelay / MaxRetryDelay5 s / 10 minO back-off exponencial entre tentativas.
ClaimDuration1 minPor quanto tempo uma instância reserva um evento que está entregando.
SerializerOptionsJsonSerializerDefaults.GeneralComo o evento é gravado e lido.

Um setup completo, tudo explícito

Program.cs
// Um AddDbContext por banco. O Nexo nunca registra o contexto por você.
builder.Services.AddDbContext<CatalogDbContext>((sp, options) => options
    .UseSqlServer(
        builder.Configuration.GetConnectionString("Catalog"),
        sql => sql.EnableRetryOnFailure())
    .UseNexo(sp));

builder.Services.AddDbContext<PurchasingDbContext>((sp, options) => options
    .UseMySQL(
        builder.Configuration.GetConnectionString("Purchasing")!,
        mySql => mySql.EnableRetryOnFailure())
    .UseNexo(sp));

builder.Services.AddDbContext<ActivityDbContext>((sp, options) => options
    .UseMongoDB(builder.Configuration.GetConnectionString("Activity")!, "activity")
    .UseNexo(sp));

builder.Services.AddNexo<CatalogDbContext>(
    nexo =>
    {
        nexo.HandlerLifetime = ServiceLifetime.Scoped;
        nexo.MediatorLifetime = ServiceLifetime.Scoped;

        // Os outros bancos, cada um com as opções de sessão dele.
        nexo.UseDbContext<PurchasingDbContext>();
        nexo.UseDbContext<ActivityDbContext>(session =>
        {
            session.UseTransactions = false;
            session.SoftDelete = false;
        });

        nexo.AddHandlersFromAssemblyOf<Program>();
        nexo.AddValidatorsFromAssemblyOf<Program>();
        nexo.UseCurrentUser<HttpCurrentUserAccessor>();

        nexo.ConfigureMediator(mediator =>
            mediator.EventDispatchMode = EventDispatchMode.Sequential);

        nexo.AddPipeline<MetricsPipeline>(ServiceLifetime.Singleton);
    },
    // As opções de sessão do CatalogDbContext.
    session =>
    {
        session.SaveBehavior = SaveBehavior.UnitOfWork;
        session.TrackReads = true;
        session.SoftDelete = true;
        session.ApplyAudit = true;
    });

Configurando por appsettings

Todas as opções passam por IOptions<T>, então podem vir da configuração como qualquer outra.

appsettings.json
{
  "Nexo": {
    "UnitOfWork": {
      "UseTransaction": true,
      "MaxDomainEventRounds": 4
    },
    "Logging": {
      "Level": "Information",
      "LogPayload": false
    }
  }
}
C#
builder.Services.Configure<UnitOfWorkPipelineOptions>(
    builder.Configuration.GetSection("Nexo:UnitOfWork"));

builder.Services.Configure<LoggingPipelineOptions>(
    builder.Configuration.GetSection("Nexo:Logging"));