Blog

EF Core como Sistema Nervioso: Auditoría Automática + Eventos de Dominio = Aplicaciones más Inteligentes

DbContext
EF
C#
.NET
Azure

Imagina que tu aplicación reacciona automáticamente a cada cambio, registra toda su actividad crítica y se comunica internamente sin código repetitivo. En este post, revelo cómo lograr esto usando EF Core como el sistema nervioso central de tu dominio, con dos técnicas infalibles: auditoría automática y eventos de dominio. Incluye el código exacto que utilizo en sistemas empresariales reales.

🧠 Auditoría y Publicación de Eventos de Dominio con EF Core

En este post, exploraremos cómo utilizar Entity Framework Core para implementar dos patrones fundamentales en aplicaciones empresariales: la auditoría de entidades y la publicación de eventos de dominio.

🔍 Auditoría Automática de Entidades

EF Core nos permite implementar un sistema de auditoría automático sobrescribiendo el método SaveChangesAsync:

public override async Task<int> SaveChangesAsync(CancellationToken cancellationToken = default)
{
foreach (var entry in ChangeTracker.Entries<AuditableEntity>())
{
switch (entry.State)
{
case EntityState.Added:
entry.Entity.CreatedBy = _currentUserService.UserId;
entry.Entity.Created = _dateTime.Now;
break;
case EntityState.Modified:
entry.Entity.LastModifiedBy = _currentUserService.UserId;
entry.Entity.LastModified = _dateTime.Now;
break;
}
}
// Resto de la implementación...
}

🎯 Beneficios Clave

  • 🛠 Automatización completa de los campos de auditoría
  • 🔄 Consistencia garantizada en todos los cambios realizados

📌 Centralización de la Lógica en un Solo Lugar

Requisito: Las entidades deben implementar la clase o interfaz AuditableEntity con propiedades como:

  • CreatedBy (quién creó el registro)
  • Created (cuándo fue creado)
  • LastModifiedBy (último editor)
  • LastModified (última modificación)

🌐 Eventos de Dominio con EF Core

El mismo contexto demuestra un patrón eficiente para manejar eventos de dominio:

var events = ChangeTracker.Entries<IHasDomainEvent>()
.Select(x => x.Entity.DomainEvents)
.SelectMany(x => x)
.Where(domainEvent => !domainEvent.IsPublished)
.ToArray();
var result = await base.SaveChangesAsync(cancellationToken);
await DispatchEvents(events);

⚡ Características Principales

  1. Flujo de trabajo seguro:
  • ✅ Los cambios se confirman primero en la base de datos
  • ✅ Luego se publican los eventos
  1. Mecanismo de prevención de duplicados:
  • 🔍 Filtra solo eventos no publicados (!domainEvent.IsPublished)
  • 🏷 Marca los eventos como publicados antes de enviarlos
  1. Integración limpia:
  • 🧩 Utiliza una interfaz IHasDomainEvent para identificar entidades con eventos
  • 📤 Delega la publicación a un servicio especializado (IDomainEventService)

⚙️ Configuración Adicional

El contexto también incluye configuraciones valiosas para el modelo:

protected override void OnModelCreating(ModelBuilder builder)
{
// Ignorar DomainEvent en el modelo de la DB
builder.Ignore<DomainEvent>();
builder.Ignore<List<DomainEvent>>();
// Configurar DateTime para usar siempre UTC
foreach (var entityType in builder.Model.GetEntityTypes())
{
foreach (var property in entityType.GetProperties())
{
if (property.ClrType == typeof(DateTime) || property.ClrType == typeof(DateTime?))
{
property.SetValueConverter(new ValueConverter<DateTime, DateTime>(
v => v.Kind == DateTimeKind.Utc ? v : DateTime.SpecifyKind(v, DateTimeKind.Utc),
v => DateTime.SpecifyKind(v, DateTimeKind.Utc)));
}
}
}
}

🏆 Conclusión

Este enfoque nos ofrece:

  • 📊 Auditoría robusta y automática
  • 🚀 Eventos de dominio confiables
  • ⏱ Manejo profesional de fechas en formato UTC
  • 💎 Una integración elegante dentro del propio DbContext