Refactor solution structure and add RabbitMQ config

Reorganized the solution structure to align with a layered architecture:
- Replaced `src` folder with `core`, `infrastructure`, and `presentation`.
- Moved projects to their respective folders.
- Added `DigitalData.MessagingService.Publisher.Abstraction` project.
- Removed `DigitalData.MessagingService.Client` project.

Updated project configurations and nesting in the solution file.

Added `appsettings.Secrets.json` with RabbitMQ and email account settings:
- RabbitMQ configuration includes hostname, port, credentials, and queue/exchange details.
- Email configuration includes SMTP server details and credentials.
This commit is contained in:
2026-07-28 10:26:15 +02:00
parent 2ad2dc6b4d
commit 78c82bf129
40 changed files with 67 additions and 40 deletions

View File

@@ -0,0 +1,158 @@
using System.Linq.Expressions;
using AutoMapper;
using DigitalData.MessagingService.Application.Common.Interfaces;
using DigitalData.MessagingService.Domain.Exceptions;
using DigitalData.MessagingService.Infrastructure.Persistence;
using Microsoft.EntityFrameworkCore;
namespace DigitalData.MessagingService.Infrastructure.Repositories;
/// <summary>
/// Generic repository implementation with AutoMapper-based CRUD operations.
/// IMPORTANT: Each operation auto-saves changes - NO explicit SaveChangesAsync needed!
/// </summary>
public class Repository<TEntity>(MessagingServiceDbContext Context, IMapper Mapper) : IRepository<TEntity> where TEntity : class
{
private readonly DbSet<TEntity> _dbSet = Context.Set<TEntity>();
// --- CREATE ---
public async Task<TEntity> CreateAsync<TDto>(TDto dto, CancellationToken cancellationToken = default)
{
var entity = Mapper.Map<TEntity>(dto);
await _dbSet.AddAsync(entity, cancellationToken);
await Context.SaveChangesAsync(cancellationToken);
return entity;
}
// --- READ ---
public async Task<TEntity?> GetByIdAsync(int id, CancellationToken cancellationToken = default)
{
return await _dbSet.FindAsync([id], cancellationToken);
}
public async Task<IEnumerable<TEntity>> GetAllAsync(CancellationToken cancellationToken = default)
{
return await _dbSet.ToListAsync(cancellationToken);
}
public async Task<IEnumerable<TEntity>> FindAsync(
Expression<Func<TEntity, bool>> predicate,
int? skip = null,
int? take = null,
CancellationToken cancellationToken = default)
{
var query = _dbSet.Where(predicate);
if (skip.HasValue)
query = query.Skip(skip.Value);
if (take.HasValue)
query = query.Take(take.Value);
return await query.ToListAsync(cancellationToken);
}
public async Task<TEntity?> FindFirstAsync(
Expression<Func<TEntity, bool>> predicate,
CancellationToken cancellationToken = default)
{
return await _dbSet.FirstOrDefaultAsync(predicate, cancellationToken);
}
public async Task<TEntity?> FindSingleAsync(
Expression<Func<TEntity, bool>> predicate,
CancellationToken cancellationToken = default)
{
return await _dbSet.SingleOrDefaultAsync(predicate, cancellationToken);
}
public async Task<int> CountAsync(
Expression<Func<TEntity, bool>>? predicate = null,
CancellationToken cancellationToken = default)
{
return predicate == null
? await _dbSet.CountAsync(cancellationToken)
: await _dbSet.CountAsync(predicate, cancellationToken);
}
public async Task<bool> AnyAsync(
Expression<Func<TEntity, bool>> predicate,
CancellationToken cancellationToken = default)
{
return await _dbSet.AnyAsync(predicate, cancellationToken);
}
// --- UPDATE ---
/// <summary>
/// Updates a SINGLE entity that matches the predicate.
/// Throws NotFoundException if 0 or 2+ records match.
/// Auto-saves changes.
/// </summary>
public async Task UpdateSingleAsync<TDto>(
Expression<Func<TEntity, bool>> predicate,
TDto dto,
CancellationToken cancellationToken = default)
{
var entity = await _dbSet.SingleOrDefaultAsync(predicate, cancellationToken)
?? throw new NotFoundException($"No {typeof(TEntity).Name} found matching the predicate.");
Mapper.Map(dto, entity);
await Context.SaveChangesAsync(cancellationToken);
}
/// <summary>
/// Updates ALL entities that match the predicate (bulk operation).
/// Returns count of updated records.
/// Auto-saves changes.
/// </summary>
public async Task<int> UpdateAsync<TDto>(
Expression<Func<TEntity, bool>> predicate,
TDto dto,
CancellationToken cancellationToken = default)
{
var entities = await _dbSet.Where(predicate).ToListAsync(cancellationToken);
foreach (var entity in entities)
{
Mapper.Map(dto, entity);
}
await Context.SaveChangesAsync(cancellationToken);
return entities.Count;
}
// --- DELETE ---
/// <summary>
/// Deletes a SINGLE entity that matches the predicate.
/// Throws NotFoundException if 0 or 2+ records match.
/// Auto-saves changes.
/// </summary>
public async Task DeleteSingleAsync(
Expression<Func<TEntity, bool>> predicate,
CancellationToken cancellationToken = default)
{
var entity = await _dbSet.SingleOrDefaultAsync(predicate, cancellationToken)
?? throw new NotFoundException($"No {typeof(TEntity).Name} found matching the predicate.");
_dbSet.Remove(entity);
await Context.SaveChangesAsync(cancellationToken);
}
/// <summary>
/// Deletes ALL entities that match the predicate (bulk operation).
/// Returns count of deleted records.
/// Auto-saves changes.
/// </summary>
public async Task<int> DeleteAsync(
Expression<Func<TEntity, bool>> predicate,
CancellationToken cancellationToken = default)
{
var entities = await _dbSet.Where(predicate).ToListAsync(cancellationToken);
_dbSet.RemoveRange(entities);
await Context.SaveChangesAsync(cancellationToken);
return entities.Count;
}
}