🧠 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
- Flujo de trabajo seguro:
- ✅ Los cambios se confirman primero en la base de datos
- ✅ Luego se publican los eventos
- Mecanismo de prevención de duplicados:
- 🔍 Filtra solo eventos no publicados (!domainEvent.IsPublished)
- 🏷 Marca los eventos como publicados antes de enviarlos
- 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