Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 10 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,15 @@
# Riber - Changelog

## v4.2.1 - 07/11/2025
- **REFATORAÇÃO**: Unificar BaseEntity e BaseModel em Tracker para herança simplificada
- Remove `BaseEntityConfiguration` e `BaseModelConfiguration`, substituindo por `BaseTypeConfiguration`
- Introduz abstração `Tracker` consolidando propriedades e métodos comuns
- Atualiza modelos e mapeamentos para herdar de `Tracker`
- Simplifica lógica de auditoria em `AuditInterceptor` para manipular exclusivamente entidades `Tracker`
- Reorganiza mapeamentos seguindo a nova estrutura de herança

---

## v4.2.0 - 04/11/2025
- **NOVO**: Serviço para geração de Embeddings Genérico
- Adiciona um evento para gerar embeddings dos produtos
Expand Down
2 changes: 1 addition & 1 deletion Directory.Build.props
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
<Companies>Samuel Zedec</Companies>
<Copyright>Copyright © $([System.DateTime]::Now.Year)</Copyright>
<Description>Sistema de gestão financeira para um lanchonete local</Description>
<Version>4.2.0</Version>
<Version>4.2.1</Version>
</PropertyGroup>

<!-- Repositório e Documentação -->
Expand Down
27 changes: 27 additions & 0 deletions src/Riber.Domain/Abstractions/Tracker.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
namespace Riber.Domain.Abstractions;

/// <summary>
/// Define um contrato para rastreamento das propriedades fundamentais de uma entidade, incluindo seu identificador único
/// e marcações temporais para criação, última atualização e exclusão lógica.
/// </summary>
public abstract class Tracker(Guid id)
{
#region Properties

public Guid Id { get; } = id;
public DateTime CreatedAt { get; } = DateTime.UtcNow;
public DateTime? UpdatedAt { get; private set; }
public DateTime? DeletedAt { get; private set; }

#endregion

#region Methods

public void UpdateEntity()
=> UpdatedAt = DateTime.UtcNow;

public void DeleteEntity()
=> DeletedAt = DateTime.UtcNow;

#endregion
}
22 changes: 2 additions & 20 deletions src/Riber.Domain/Entities/BaseEntity.cs
Original file line number Diff line number Diff line change
Expand Up @@ -6,23 +6,15 @@
/// Representa uma entidade base abstrata que fornece funcionalidades comuns para entidades de domínio.
/// Esta classe inclui um identificador, gerenciamento de eventos de domínio e mecanismos de comparação de igualdade.
/// </summary>
public abstract class BaseEntity(Guid id) : IEquatable<BaseEntity>
public abstract class BaseEntity(Guid id)

Check warning on line 9 in src/Riber.Domain/Entities/BaseEntity.cs

View workflow job for this annotation

GitHub Actions / sonarcloud

Seal class 'BaseEntity' or implement 'IEqualityComparer<T>' instead. (https://rules.sonarsource.com/csharp/RSPEC-4035)
: Tracker(id), IEquatable<BaseEntity>
{
#region Private Members

private readonly List<IDomainEvent> _events = [];

#endregion

#region Properties

public Guid Id { get; } = id;
public DateTime CreatedAt { get; } = DateTime.UtcNow;
public DateTime? UpdatedAt { get; private set; }
public DateTime? DeletedAt { get; private set; }

#endregion

#region Overrides

public override int GetHashCode()
Expand Down Expand Up @@ -51,16 +43,6 @@

#endregion

#region BaseEntity Methods

public void UpdateEntity()
=> UpdatedAt = DateTime.UtcNow;

public void DeleteEntity()
=> DeletedAt = DateTime.UtcNow;

#endregion

#region Operators

public static bool operator ==(BaseEntity? left, BaseEntity? right)
Expand Down
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Diagnostics;
using Riber.Domain.Abstractions;
using Riber.Domain.Entities;
using Riber.Infrastructure.Persistence.Identity;
using Riber.Infrastructure.Persistence.Models;

namespace Riber.Infrastructure.Persistence.Interceptors;

Expand All @@ -23,10 +23,7 @@ public override InterceptionResult<int> SavingChanges(
InterceptionResult<int> result)
{
if (eventData.Context is not null)
{
ApplyAuditEntities(eventData.Context);
ApplyAuditModels(eventData.Context);
}
ApplyAuditTracker(eventData.Context);

return base.SavingChanges(eventData, result);
}
Expand All @@ -37,20 +34,20 @@ public override ValueTask<InterceptionResult<int>> SavingChangesAsync(
CancellationToken cancellationToken = default)
{
if (eventData.Context is not null)
ApplyAuditEntities(eventData.Context);
ApplyAuditTracker(eventData.Context);

return base.SavingChangesAsync(eventData, result, cancellationToken);
}

/// <summary>
/// Aplica lógica de auditoria às entidades rastreadas pelo contexto do Entity Framework Core.
/// Aplica lógica de auditoria às trackers rastreadas pelo contexto do Entity Framework Core.
/// Atualiza os timestamps de modificação ou realiza ações específicas para estados de entidade,
/// como exclusões suaves.
/// </summary>
/// <param name="context">O contexto do banco de dados que contém o rastreador de alterações para as entidades alvo.</param>
private static void ApplyAuditEntities(DbContext context)
private static void ApplyAuditTracker(DbContext context)
{
var entries = context.ChangeTracker.Entries<BaseEntity>();
var entries = context.ChangeTracker.Entries<Tracker>();
foreach (var entry in entries)
{
if (entry.State is EntityState.Modified)
Expand All @@ -67,22 +64,6 @@ private static void ApplyAuditEntities(DbContext context)
}
}

private static void ApplyAuditModels(DbContext context)
{
var entries = context.ChangeTracker.Entries<BaseModel>();
foreach (var entry in entries)
{
if (entry.State is EntityState.Modified)
entry.Entity.UpdatedAt = DateTime.UtcNow;

if (entry.State is EntityState.Deleted)
{
entry.State = EntityState.Modified;
entry.Entity.DeletedAt = DateTime.UtcNow;
}
}
}

/// <summary>
/// Desativa um usuário da aplicação marcando-o como excluído e define
/// o estado da entidade como Modificado no contexto do banco de dados fornecido.
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Metadata.Builders;
using Riber.Domain.Abstractions;

namespace Riber.Infrastructure.Persistence.Mappings;

public abstract class BaseTypeConfiguration<T> : IEntityTypeConfiguration<T>
where T : Tracker
{
public void Configure(EntityTypeBuilder<T> builder)
{
builder.ToTable(GetTableName());

builder.HasKey(x => x.Id)
.HasName($"pk_{GetTableName()}_id");

builder.Property(x => x.Id)
.HasColumnName("id")
.HasColumnType("uuid")
.IsRequired();

builder.Property(x => x.CreatedAt)
.HasColumnName("created_at")
.HasColumnType("timestamptz")
.IsRequired();

builder.Property(x => x.UpdatedAt)
.HasColumnName("modified_at")
.HasColumnType("timestamptz");

builder.Property(x => x.DeletedAt)
.HasColumnName("deleted_at")
.HasColumnType("timestamptz");

Mapping(builder);
ConfigureQueryFilter(builder);
}

/// <summary>
/// Obtém o nome da tabela no banco de dados para o tipo mapeado.
/// </summary>
/// <returns>
/// O nome da tabela correspondente.
/// </returns>
protected abstract string GetTableName();

/// <summary>
/// Aplica configurações específicas de mapeamento para o tipo.
/// Sobrescreva este método para definir mapeamentos de propriedades, relações e outras configurações personalizadas.
/// </summary>
/// <param name="builder">
/// O construtor de configuração do tipo de entidade.
/// </param>
protected abstract void Mapping(EntityTypeBuilder<T> builder);

/// <summary>
/// Configura filtros de consulta globais para o tipo.
/// Por padrão, aplica um filtro para excluir registros marcados como deletados.
/// Sobrescreva este método para implementar filtros adicionais ou personalizados.
/// </summary>
/// <param name="builder">
/// O construtor de configuração do tipo de entidade.
/// </param>
protected virtual void ConfigureQueryFilter(EntityTypeBuilder<T> builder)
=> builder.HasQueryFilter(x => !x.DeletedAt.HasValue);
}

This file was deleted.

Original file line number Diff line number Diff line change
Expand Up @@ -4,12 +4,12 @@

namespace Riber.Infrastructure.Persistence.Mappings.Entities;

public sealed class CompanyMap : BaseEntityConfiguration<Company>
public sealed class CompanyMap : BaseTypeConfiguration<Company>
{
protected override string GetTableName()
=> "company";

protected override void ConfigureEntity(EntityTypeBuilder<Company> builder)
protected override void Mapping(EntityTypeBuilder<Company> builder)
{
builder
.ConfigureTaxId("uq_company_tax_id")
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,12 +5,12 @@

namespace Riber.Infrastructure.Persistence.Mappings.Entities;

public sealed class ImageMap : BaseEntityConfiguration<Image>
public sealed class ImageMap : BaseTypeConfiguration<Image>
{
protected override string GetTableName()
=> "image";

protected override void ConfigureEntity(EntityTypeBuilder<Image> builder)
protected override void Mapping(EntityTypeBuilder<Image> builder)
{
builder.ConfigureContentType();

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,12 +5,12 @@

namespace Riber.Infrastructure.Persistence.Mappings.Entities;

public sealed class InvitationMap : BaseEntityConfiguration<Invitation>
public sealed class InvitationMap : BaseTypeConfiguration<Invitation>
{
protected override string GetTableName()
=> "invitation";

protected override void ConfigureEntity(EntityTypeBuilder<Invitation> builder)
protected override void Mapping(EntityTypeBuilder<Invitation> builder)
{
builder
.ConfigureEmail("uq_invitations_email")
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,12 +5,12 @@

namespace Riber.Infrastructure.Persistence.Mappings.Entities;

public sealed class OrderItemMap : BaseEntityConfiguration<OrderItem>
public sealed class OrderItemMap : BaseTypeConfiguration<OrderItem>
{
protected override string GetTableName()
=> "order_item";

protected override void ConfigureEntity(EntityTypeBuilder<OrderItem> builder)
protected override void Mapping(EntityTypeBuilder<OrderItem> builder)
{
builder
.ConfigureUnitPrice()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,12 +5,12 @@

namespace Riber.Infrastructure.Persistence.Mappings.Entities;

public sealed class OrderMap : BaseEntityConfiguration<Order>
public sealed class OrderMap : BaseTypeConfiguration<Order>
{
protected override string GetTableName()
=> "order";

protected override void ConfigureEntity(EntityTypeBuilder<Order> builder)
protected override void Mapping(EntityTypeBuilder<Order> builder)
{
builder.ConfigureRandomToken(GetTableName(), "order_token");

Expand Down
Loading
Loading