feat: add base entity abstractions and domain model to DAL.Abstract

- IEntity<TId>, Entity<TId>, Entity base classes in Entities/
- Template, TemplateVariable domain models in Templates/
- Provider, ProviderSettings, SmtpProviderSettings, ProviderUsage, ProviderType in Providers/
- ITemplateRepository, IProviderRepository, IProviderUsageRepository in Repositories/
- ProviderUsage tracks daily counts; monthly derived by summing daily records
- IEntity<TId> lives in DAL.Abstract (not DAL.EF) — boundary enforced from the start

Ref: IT-628

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
This commit is contained in:
Anatolii Grynchuk
2026-05-01 19:28:34 +03:00
parent ed4a6578c3
commit 7cb691db14
14 changed files with 136 additions and 18 deletions
@@ -1,6 +0,0 @@
namespace HrynCo.NotificationService.DAL.Abstract;
public class Class1
{
}
@@ -0,0 +1,17 @@
namespace HrynCo.NotificationService.DAL.Abstract.Entities;
public abstract class Entity<TId> : IEntity<TId> where TId : struct
{
public TId Id { get; set; }
public DateTimeOffset Created { get; set; }
public DateTimeOffset? Updated { get; set; }
}
public abstract class Entity : Entity<Guid>
{
protected Entity()
{
Id = Guid.NewGuid();
Created = DateTimeOffset.UtcNow;
}
}
@@ -0,0 +1,8 @@
namespace HrynCo.NotificationService.DAL.Abstract.Entities;
public interface IEntity<TId> where TId : struct
{
TId Id { get; set; }
DateTimeOffset Created { get; set; }
DateTimeOffset? Updated { get; set; }
}
@@ -0,0 +1,15 @@
using HrynCo.NotificationService.DAL.Abstract.Entities;
namespace HrynCo.NotificationService.DAL.Abstract.Providers;
public class Provider : Entity
{
public required string ServiceName { get; set; }
public int Priority { get; set; }
public ProviderType ProviderType { get; set; }
public required ProviderSettings Settings { get; set; }
public int? DailyLimit { get; set; }
public int? MonthlyLimit { get; set; }
public int WarnThresholdPercent { get; set; } = 90;
public bool IsActive { get; set; } = true;
}
@@ -0,0 +1,21 @@
namespace HrynCo.NotificationService.DAL.Abstract.Providers;
public abstract class ProviderSettings
{
public abstract ProviderType ProviderType { get; }
}
public class SmtpProviderSettings : ProviderSettings
{
public override ProviderType ProviderType => ProviderType.Smtp;
public required string Host { get; set; }
public int Port { get; set; } = 587;
public required string Username { get; set; }
public required string Password { get; set; }
public bool UseSsl { get; set; } = true;
public required string FromEmail { get; set; }
public required string FromName { get; set; }
public required string AppDisplayName { get; set; }
public required string AppBaseUrl { get; set; }
}
@@ -0,0 +1,6 @@
namespace HrynCo.NotificationService.DAL.Abstract.Providers;
public enum ProviderType
{
Smtp = 1
}
@@ -0,0 +1,14 @@
using HrynCo.NotificationService.DAL.Abstract.Entities;
namespace HrynCo.NotificationService.DAL.Abstract.Providers;
/// <summary>
/// Tracks email send counts per provider per calendar day.
/// Monthly counts are derived by summing daily records within a month.
/// </summary>
public class ProviderUsage : Entity
{
public Guid ProviderId { get; set; }
public DateOnly Date { get; set; }
public int SentCount { get; set; }
}
@@ -0,0 +1,12 @@
using HrynCo.NotificationService.DAL.Abstract.Providers;
namespace HrynCo.NotificationService.DAL.Abstract.Repositories;
public interface IProviderRepository
{
Task<IReadOnlyList<Provider>> GetByServiceAsync(string serviceName, CancellationToken ct = default);
Task<Provider?> GetByIdAsync(Guid id, CancellationToken ct = default);
Task AddAsync(Provider provider, CancellationToken ct = default);
Task UpdateAsync(Provider provider, CancellationToken ct = default);
Task DeleteAsync(Provider provider, CancellationToken ct = default);
}
@@ -0,0 +1,10 @@
using HrynCo.NotificationService.DAL.Abstract.Providers;
namespace HrynCo.NotificationService.DAL.Abstract.Repositories;
public interface IProviderUsageRepository
{
Task<int> GetDailyCountAsync(Guid providerId, DateOnly date, CancellationToken ct = default);
Task<int> GetMonthlyCountAsync(Guid providerId, int year, int month, CancellationToken ct = default);
Task IncrementAsync(Guid providerId, DateOnly date, CancellationToken ct = default);
}
@@ -0,0 +1,12 @@
using HrynCo.NotificationService.DAL.Abstract.Templates;
namespace HrynCo.NotificationService.DAL.Abstract.Repositories;
public interface ITemplateRepository
{
Task<IReadOnlyList<Template>> GetByServiceAsync(string serviceName, CancellationToken ct = default);
Task<Template?> GetAsync(string serviceName, string key, string languageCode, CancellationToken ct = default);
Task AddAsync(Template template, CancellationToken ct = default);
Task UpdateAsync(Template template, CancellationToken ct = default);
Task DeleteAsync(Template template, CancellationToken ct = default);
}
@@ -0,0 +1,14 @@
using HrynCo.NotificationService.DAL.Abstract.Entities;
namespace HrynCo.NotificationService.DAL.Abstract.Templates;
public class Template : Entity
{
public required string ServiceName { get; set; }
public required string Key { get; set; }
public required string LanguageCode { get; set; }
public required string Subject { get; set; }
public required string HtmlBody { get; set; }
public required string TextBody { get; set; }
public IReadOnlyList<TemplateVariable> Variables { get; set; } = [];
}
@@ -0,0 +1,7 @@
namespace HrynCo.NotificationService.DAL.Abstract.Templates;
public record TemplateVariable
{
public required string Name { get; init; }
public bool Required { get; init; }
}