diff --git a/src/backend/src/Randall.Domain/Reservations/Reservation.cs b/src/backend/src/Randall.Domain/Reservations/Reservation.cs
index 421a48d..7f4b322 100644
--- a/src/backend/src/Randall.Domain/Reservations/Reservation.cs
+++ b/src/backend/src/Randall.Domain/Reservations/Reservation.cs
@@ -13,11 +13,21 @@ public class Reservation : Entity
public ReservationStatus Status { get; private set; }
public DateTime CreatedAt { get; private set; }
- // EF Core constructor
- private Reservation() : base()
+ public static Reservation Reconstitute(
+ Guid id, Guid workplaceId, string employeeEmail, string employeeName,
+ DateOnly date, ReservationStatus status, DateTime createdAt) =>
+ new(id, workplaceId, employeeEmail, employeeName, date, status, createdAt);
+
+ private Reservation(
+ Guid id, Guid workplaceId, string employeeEmail, string employeeName,
+ DateOnly date, ReservationStatus status, DateTime createdAt) : base(id)
{
- EmployeeEmail = string.Empty;
- EmployeeName = string.Empty;
+ WorkplaceId = workplaceId;
+ EmployeeEmail = employeeEmail;
+ EmployeeName = employeeName;
+ Date = date;
+ Status = status;
+ CreatedAt = createdAt;
}
private Reservation(Guid workplaceId, string employeeEmail, string employeeName, DateOnly date)
diff --git a/src/backend/src/Randall.Domain/Users/User.cs b/src/backend/src/Randall.Domain/Users/User.cs
index abc10ca..422b796 100644
--- a/src/backend/src/Randall.Domain/Users/User.cs
+++ b/src/backend/src/Randall.Domain/Users/User.cs
@@ -10,11 +10,16 @@ public class User : Entity
public bool IsApproved { get; private set; }
public bool IsAdmin { get; private set; }
- private User() : base()
+ public static User Reconstitute(Guid id, string email, string name, string passwordHash, bool isApproved, bool isAdmin) =>
+ new(id, email, name, passwordHash, isApproved, isAdmin);
+
+ private User(Guid id, string email, string name, string passwordHash, bool isApproved, bool isAdmin) : base(id)
{
- Email = string.Empty;
- Name = string.Empty;
- PasswordHash = string.Empty;
+ Email = email;
+ Name = name;
+ PasswordHash = passwordHash;
+ IsApproved = isApproved;
+ IsAdmin = isAdmin;
}
private User(string email, string name, string passwordHash, bool isAdmin) : base()
diff --git a/src/backend/src/Randall.Domain/Workplaces/Workplace.cs b/src/backend/src/Randall.Domain/Workplaces/Workplace.cs
index fd47377..eca5e77 100644
--- a/src/backend/src/Randall.Domain/Workplaces/Workplace.cs
+++ b/src/backend/src/Randall.Domain/Workplaces/Workplace.cs
@@ -8,10 +8,14 @@ public class Workplace : Entity
public string Location { get; private set; }
public bool IsActive { get; private set; }
- private Workplace() : base()
+ public static Workplace Reconstitute(Guid id, string name, string location, bool isActive) =>
+ new(id, name, location, isActive);
+
+ private Workplace(Guid id, string name, string location, bool isActive) : base(id)
{
- Name = string.Empty;
- Location = string.Empty;
+ Name = name;
+ Location = location;
+ IsActive = isActive;
}
public Workplace(string name, string location) : base()
diff --git a/src/backend/src/Randall.Infrastructure/Migrations/20260327154255_SeparateStorageModels.Designer.cs b/src/backend/src/Randall.Infrastructure/Migrations/20260327154255_SeparateStorageModels.Designer.cs
new file mode 100644
index 0000000..d23a6ae
--- /dev/null
+++ b/src/backend/src/Randall.Infrastructure/Migrations/20260327154255_SeparateStorageModels.Designer.cs
@@ -0,0 +1,120 @@
+//
+using System;
+using Microsoft.EntityFrameworkCore;
+using Microsoft.EntityFrameworkCore.Infrastructure;
+using Microsoft.EntityFrameworkCore.Migrations;
+using Microsoft.EntityFrameworkCore.Storage.ValueConversion;
+using Randall.Infrastructure.Persistence;
+
+#nullable disable
+
+namespace Randall.Infrastructure.Migrations
+{
+ [DbContext(typeof(AppDbContext))]
+ [Migration("20260327154255_SeparateStorageModels")]
+ partial class SeparateStorageModels
+ {
+ ///
+ protected override void BuildTargetModel(ModelBuilder modelBuilder)
+ {
+#pragma warning disable 612, 618
+ modelBuilder.HasAnnotation("ProductVersion", "10.0.5");
+
+ modelBuilder.Entity("Randall.Infrastructure.Persistence.Records.ReservationRecord", b =>
+ {
+ b.Property("Id")
+ .ValueGeneratedOnAdd()
+ .HasColumnType("TEXT");
+
+ b.Property("CreatedAt")
+ .HasColumnType("TEXT");
+
+ b.Property("Date")
+ .HasColumnType("TEXT");
+
+ b.Property("EmployeeEmail")
+ .IsRequired()
+ .HasMaxLength(200)
+ .HasColumnType("TEXT");
+
+ b.Property("EmployeeName")
+ .IsRequired()
+ .HasMaxLength(200)
+ .HasColumnType("TEXT");
+
+ b.Property("Status")
+ .HasColumnType("INTEGER");
+
+ b.Property("WorkplaceId")
+ .HasColumnType("TEXT");
+
+ b.HasKey("Id");
+
+ b.HasIndex("EmployeeEmail", "Date");
+
+ b.HasIndex("WorkplaceId", "Date");
+
+ b.ToTable("Reservations", (string)null);
+ });
+
+ modelBuilder.Entity("Randall.Infrastructure.Persistence.Records.UserRecord", b =>
+ {
+ b.Property("Id")
+ .ValueGeneratedOnAdd()
+ .HasColumnType("TEXT");
+
+ b.Property("Email")
+ .IsRequired()
+ .HasMaxLength(200)
+ .HasColumnType("TEXT");
+
+ b.Property("IsAdmin")
+ .HasColumnType("INTEGER");
+
+ b.Property("IsApproved")
+ .HasColumnType("INTEGER");
+
+ b.Property("Name")
+ .IsRequired()
+ .HasMaxLength(200)
+ .HasColumnType("TEXT");
+
+ b.Property("PasswordHash")
+ .IsRequired()
+ .HasColumnType("TEXT");
+
+ b.HasKey("Id");
+
+ b.HasIndex("Email")
+ .IsUnique();
+
+ b.ToTable("Users", (string)null);
+ });
+
+ modelBuilder.Entity("Randall.Infrastructure.Persistence.Records.WorkplaceRecord", b =>
+ {
+ b.Property("Id")
+ .ValueGeneratedOnAdd()
+ .HasColumnType("TEXT");
+
+ b.Property("IsActive")
+ .HasColumnType("INTEGER");
+
+ b.Property("Location")
+ .IsRequired()
+ .HasMaxLength(200)
+ .HasColumnType("TEXT");
+
+ b.Property("Name")
+ .IsRequired()
+ .HasMaxLength(100)
+ .HasColumnType("TEXT");
+
+ b.HasKey("Id");
+
+ b.ToTable("Workplaces", (string)null);
+ });
+#pragma warning restore 612, 618
+ }
+ }
+}
diff --git a/src/backend/src/Randall.Infrastructure/Migrations/20260327154255_SeparateStorageModels.cs b/src/backend/src/Randall.Infrastructure/Migrations/20260327154255_SeparateStorageModels.cs
new file mode 100644
index 0000000..ecb04f1
--- /dev/null
+++ b/src/backend/src/Randall.Infrastructure/Migrations/20260327154255_SeparateStorageModels.cs
@@ -0,0 +1,22 @@
+using Microsoft.EntityFrameworkCore.Migrations;
+
+#nullable disable
+
+namespace Randall.Infrastructure.Migrations
+{
+ ///
+ public partial class SeparateStorageModels : Migration
+ {
+ ///
+ protected override void Up(MigrationBuilder migrationBuilder)
+ {
+
+ }
+
+ ///
+ protected override void Down(MigrationBuilder migrationBuilder)
+ {
+
+ }
+ }
+}
diff --git a/src/backend/src/Randall.Infrastructure/Migrations/AppDbContextModelSnapshot.cs b/src/backend/src/Randall.Infrastructure/Migrations/AppDbContextModelSnapshot.cs
index 5efe56b..25e1cfe 100644
--- a/src/backend/src/Randall.Infrastructure/Migrations/AppDbContextModelSnapshot.cs
+++ b/src/backend/src/Randall.Infrastructure/Migrations/AppDbContextModelSnapshot.cs
@@ -17,7 +17,7 @@ namespace Randall.Infrastructure.Migrations
#pragma warning disable 612, 618
modelBuilder.HasAnnotation("ProductVersion", "10.0.5");
- modelBuilder.Entity("Randall.Domain.Reservations.Reservation", b =>
+ modelBuilder.Entity("Randall.Infrastructure.Persistence.Records.ReservationRecord", b =>
{
b.Property("Id")
.ValueGeneratedOnAdd()
@@ -51,10 +51,10 @@ namespace Randall.Infrastructure.Migrations
b.HasIndex("WorkplaceId", "Date");
- b.ToTable("Reservations");
+ b.ToTable("Reservations", (string)null);
});
- modelBuilder.Entity("Randall.Domain.Users.User", b =>
+ modelBuilder.Entity("Randall.Infrastructure.Persistence.Records.UserRecord", b =>
{
b.Property("Id")
.ValueGeneratedOnAdd()
@@ -85,10 +85,10 @@ namespace Randall.Infrastructure.Migrations
b.HasIndex("Email")
.IsUnique();
- b.ToTable("Users");
+ b.ToTable("Users", (string)null);
});
- modelBuilder.Entity("Randall.Domain.Workplaces.Workplace", b =>
+ modelBuilder.Entity("Randall.Infrastructure.Persistence.Records.WorkplaceRecord", b =>
{
b.Property("Id")
.ValueGeneratedOnAdd()
@@ -109,7 +109,7 @@ namespace Randall.Infrastructure.Migrations
b.HasKey("Id");
- b.ToTable("Workplaces");
+ b.ToTable("Workplaces", (string)null);
});
#pragma warning restore 612, 618
}
diff --git a/src/backend/src/Randall.Infrastructure/Persistence/AppDbContext.cs b/src/backend/src/Randall.Infrastructure/Persistence/AppDbContext.cs
index 3d5c9f0..7104c34 100644
--- a/src/backend/src/Randall.Infrastructure/Persistence/AppDbContext.cs
+++ b/src/backend/src/Randall.Infrastructure/Persistence/AppDbContext.cs
@@ -1,28 +1,28 @@
using Microsoft.EntityFrameworkCore;
-using Randall.Domain.Reservations;
-using Randall.Domain.Users;
-using Randall.Domain.Workplaces;
+using Randall.Infrastructure.Persistence.Records;
namespace Randall.Infrastructure.Persistence;
public class AppDbContext(DbContextOptions options) : DbContext(options)
{
- public DbSet Workplaces => Set();
- public DbSet Reservations => Set();
- public DbSet Users => Set();
+ public DbSet Workplaces => Set();
+ public DbSet Reservations => Set();
+ public DbSet Users => Set();
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
- modelBuilder.Entity(entity =>
+ modelBuilder.Entity(entity =>
{
+ entity.ToTable("Workplaces");
entity.HasKey(w => w.Id);
entity.Property(w => w.Name).IsRequired().HasMaxLength(100);
entity.Property(w => w.Location).IsRequired().HasMaxLength(200);
entity.Property(w => w.IsActive).IsRequired();
});
- modelBuilder.Entity(entity =>
+ modelBuilder.Entity(entity =>
{
+ entity.ToTable("Reservations");
entity.HasKey(r => r.Id);
entity.Property(r => r.WorkplaceId).IsRequired();
entity.Property(r => r.EmployeeEmail).IsRequired().HasMaxLength(200);
@@ -35,8 +35,9 @@ public class AppDbContext(DbContextOptions options) : DbContext(op
entity.HasIndex(r => new { r.EmployeeEmail, r.Date });
});
- modelBuilder.Entity(entity =>
+ modelBuilder.Entity(entity =>
{
+ entity.ToTable("Users");
entity.HasKey(u => u.Id);
entity.Property(u => u.Email).IsRequired().HasMaxLength(200);
entity.Property(u => u.Name).IsRequired().HasMaxLength(200);
diff --git a/src/backend/src/Randall.Infrastructure/Persistence/Mappers/ReservationMapper.cs b/src/backend/src/Randall.Infrastructure/Persistence/Mappers/ReservationMapper.cs
new file mode 100644
index 0000000..621ce91
--- /dev/null
+++ b/src/backend/src/Randall.Infrastructure/Persistence/Mappers/ReservationMapper.cs
@@ -0,0 +1,34 @@
+using Randall.Domain.Reservations;
+using Randall.Infrastructure.Persistence.Records;
+
+namespace Randall.Infrastructure.Persistence.Mappers;
+
+public static class ReservationMapper
+{
+ public static Reservation ToDomain(ReservationRecord record) =>
+ Reservation.Reconstitute(
+ record.Id,
+ record.WorkplaceId,
+ record.EmployeeEmail,
+ record.EmployeeName,
+ record.Date,
+ record.Status,
+ record.CreatedAt);
+
+ public static ReservationRecord ToRecord(Reservation domain) =>
+ new()
+ {
+ Id = domain.Id,
+ WorkplaceId = domain.WorkplaceId,
+ EmployeeEmail = domain.EmployeeEmail,
+ EmployeeName = domain.EmployeeName,
+ Date = domain.Date,
+ Status = domain.Status,
+ CreatedAt = domain.CreatedAt,
+ };
+
+ public static void SyncToRecord(Reservation domain, ReservationRecord record)
+ {
+ record.Status = domain.Status;
+ }
+}
diff --git a/src/backend/src/Randall.Infrastructure/Persistence/Mappers/UserMapper.cs b/src/backend/src/Randall.Infrastructure/Persistence/Mappers/UserMapper.cs
new file mode 100644
index 0000000..979f71a
--- /dev/null
+++ b/src/backend/src/Randall.Infrastructure/Persistence/Mappers/UserMapper.cs
@@ -0,0 +1,33 @@
+using Randall.Domain.Users;
+using Randall.Infrastructure.Persistence.Records;
+
+namespace Randall.Infrastructure.Persistence.Mappers;
+
+public static class UserMapper
+{
+ public static User ToDomain(UserRecord record) =>
+ User.Reconstitute(
+ record.Id,
+ record.Email,
+ record.Name,
+ record.PasswordHash,
+ record.IsApproved,
+ record.IsAdmin);
+
+ public static UserRecord ToRecord(User domain) =>
+ new()
+ {
+ Id = domain.Id,
+ Email = domain.Email,
+ Name = domain.Name,
+ PasswordHash = domain.PasswordHash,
+ IsApproved = domain.IsApproved,
+ IsAdmin = domain.IsAdmin,
+ };
+
+ public static void SyncToRecord(User domain, UserRecord record)
+ {
+ record.IsApproved = domain.IsApproved;
+ record.IsAdmin = domain.IsAdmin;
+ }
+}
diff --git a/src/backend/src/Randall.Infrastructure/Persistence/Mappers/WorkplaceMapper.cs b/src/backend/src/Randall.Infrastructure/Persistence/Mappers/WorkplaceMapper.cs
new file mode 100644
index 0000000..e7af9c6
--- /dev/null
+++ b/src/backend/src/Randall.Infrastructure/Persistence/Mappers/WorkplaceMapper.cs
@@ -0,0 +1,23 @@
+using Randall.Domain.Workplaces;
+using Randall.Infrastructure.Persistence.Records;
+
+namespace Randall.Infrastructure.Persistence.Mappers;
+
+public static class WorkplaceMapper
+{
+ public static Workplace ToDomain(WorkplaceRecord record) =>
+ Workplace.Reconstitute(
+ record.Id,
+ record.Name,
+ record.Location,
+ record.IsActive);
+
+ public static WorkplaceRecord ToRecord(Workplace domain) =>
+ new()
+ {
+ Id = domain.Id,
+ Name = domain.Name,
+ Location = domain.Location,
+ IsActive = domain.IsActive,
+ };
+}
diff --git a/src/backend/src/Randall.Infrastructure/Persistence/Records/ReservationRecord.cs b/src/backend/src/Randall.Infrastructure/Persistence/Records/ReservationRecord.cs
new file mode 100644
index 0000000..6f51210
--- /dev/null
+++ b/src/backend/src/Randall.Infrastructure/Persistence/Records/ReservationRecord.cs
@@ -0,0 +1,14 @@
+using Randall.Domain.Reservations;
+
+namespace Randall.Infrastructure.Persistence.Records;
+
+public class ReservationRecord
+{
+ public Guid Id { get; set; }
+ public Guid WorkplaceId { get; set; }
+ public string EmployeeEmail { get; set; } = string.Empty;
+ public string EmployeeName { get; set; } = string.Empty;
+ public DateOnly Date { get; set; }
+ public ReservationStatus Status { get; set; }
+ public DateTime CreatedAt { get; set; }
+}
diff --git a/src/backend/src/Randall.Infrastructure/Persistence/Records/UserRecord.cs b/src/backend/src/Randall.Infrastructure/Persistence/Records/UserRecord.cs
new file mode 100644
index 0000000..8b885fb
--- /dev/null
+++ b/src/backend/src/Randall.Infrastructure/Persistence/Records/UserRecord.cs
@@ -0,0 +1,11 @@
+namespace Randall.Infrastructure.Persistence.Records;
+
+public class UserRecord
+{
+ public Guid Id { get; set; }
+ public string Email { get; set; } = string.Empty;
+ public string Name { get; set; } = string.Empty;
+ public string PasswordHash { get; set; } = string.Empty;
+ public bool IsApproved { get; set; }
+ public bool IsAdmin { get; set; }
+}
diff --git a/src/backend/src/Randall.Infrastructure/Persistence/Records/WorkplaceRecord.cs b/src/backend/src/Randall.Infrastructure/Persistence/Records/WorkplaceRecord.cs
new file mode 100644
index 0000000..c9de0a2
--- /dev/null
+++ b/src/backend/src/Randall.Infrastructure/Persistence/Records/WorkplaceRecord.cs
@@ -0,0 +1,9 @@
+namespace Randall.Infrastructure.Persistence.Records;
+
+public class WorkplaceRecord
+{
+ public Guid Id { get; set; }
+ public string Name { get; set; } = string.Empty;
+ public string Location { get; set; } = string.Empty;
+ public bool IsActive { get; set; }
+}
diff --git a/src/backend/src/Randall.Infrastructure/Persistence/Reservations/ReservationRepository.cs b/src/backend/src/Randall.Infrastructure/Persistence/Reservations/ReservationRepository.cs
index 4369d2d..58e1269 100644
--- a/src/backend/src/Randall.Infrastructure/Persistence/Reservations/ReservationRepository.cs
+++ b/src/backend/src/Randall.Infrastructure/Persistence/Reservations/ReservationRepository.cs
@@ -1,23 +1,40 @@
using Microsoft.EntityFrameworkCore;
using Randall.Domain.Reservations;
+using Randall.Infrastructure.Persistence.Mappers;
+using Randall.Infrastructure.Persistence.Records;
namespace Randall.Infrastructure.Persistence.Reservations;
public class ReservationRepository(AppDbContext context) : IReservationRepository
{
- public Task GetByIdAsync(Guid id, CancellationToken ct = default) =>
- context.Reservations.FirstOrDefaultAsync(r => r.Id == id, ct);
+ private readonly Dictionary _tracked = [];
- public async Task> GetByEmployeeAsync(string employeeEmail, CancellationToken ct = default) =>
- await context.Reservations
- .Where(r => r.EmployeeEmail.ToLower() == employeeEmail.ToLower())
+ public async Task GetByIdAsync(Guid id, CancellationToken ct = default)
+ {
+ var record = await context.Reservations.FirstOrDefaultAsync(r => r.Id == id, ct);
+ if (record is null) return null;
+ var domain = ReservationMapper.ToDomain(record);
+ _tracked[domain.Id] = (domain, record);
+ return domain;
+ }
+
+ public async Task> GetByEmployeeAsync(string employeeEmail, CancellationToken ct = default)
+ {
+ var today = DateOnly.FromDateTime(DateTime.UtcNow);
+ var records = await context.Reservations
+ .Where(r => r.EmployeeEmail.ToLower() == employeeEmail.ToLower() && r.Date >= today)
.ToListAsync(ct);
+ return records.Select(ReservationMapper.ToDomain).ToList();
+ }
public async Task> GetByWorkplaceAndDateAsync(
- Guid workplaceId, DateOnly date, CancellationToken ct = default) =>
- await context.Reservations
+ Guid workplaceId, DateOnly date, CancellationToken ct = default)
+ {
+ var records = await context.Reservations
.Where(r => r.WorkplaceId == workplaceId && r.Date == date)
.ToListAsync(ct);
+ return records.Select(ReservationMapper.ToDomain).ToList();
+ }
public Task ExistsActiveForEmployeeOnDateAsync(
string employeeEmail, DateOnly date, CancellationToken ct = default) =>
@@ -36,14 +53,24 @@ public class ReservationRepository(AppDbContext context) : IReservationRepositor
ct);
public async Task> GetActiveReservationsForDateAsync(
- DateOnly date, CancellationToken ct = default) =>
- await context.Reservations
+ DateOnly date, CancellationToken ct = default)
+ {
+ var records = await context.Reservations
.Where(r => r.Date == date && r.Status == ReservationStatus.Active)
.ToListAsync(ct);
+ return records.Select(ReservationMapper.ToDomain).ToList();
+ }
- public async Task AddAsync(Reservation reservation, CancellationToken ct = default) =>
- await context.Reservations.AddAsync(reservation, ct);
+ public async Task AddAsync(Reservation reservation, CancellationToken ct = default)
+ {
+ var record = ReservationMapper.ToRecord(reservation);
+ await context.Reservations.AddAsync(record, ct);
+ }
- public Task SaveChangesAsync(CancellationToken ct = default) =>
- context.SaveChangesAsync(ct);
+ public Task SaveChangesAsync(CancellationToken ct = default)
+ {
+ foreach (var (domain, record) in _tracked.Values)
+ ReservationMapper.SyncToRecord(domain, record);
+ return context.SaveChangesAsync(ct);
+ }
}
diff --git a/src/backend/src/Randall.Infrastructure/Persistence/Seeding/DatabaseSeeder.cs b/src/backend/src/Randall.Infrastructure/Persistence/Seeding/DatabaseSeeder.cs
index 82fac1f..967a8a7 100644
--- a/src/backend/src/Randall.Infrastructure/Persistence/Seeding/DatabaseSeeder.cs
+++ b/src/backend/src/Randall.Infrastructure/Persistence/Seeding/DatabaseSeeder.cs
@@ -2,6 +2,7 @@ using Microsoft.EntityFrameworkCore;
using Randall.Application.Common;
using Randall.Domain.Users;
using Randall.Domain.Workplaces;
+using Randall.Infrastructure.Persistence.Mappers;
namespace Randall.Infrastructure.Persistence.Seeding;
@@ -25,7 +26,7 @@ public static class DatabaseSeeder
// Seed workplaces only if none exist yet
if (!await context.Workplaces.AnyAsync())
{
- var workplaces = ExpectedWorkplaces.Select(w => new Workplace(w.Name, w.Location));
+ var workplaces = ExpectedWorkplaces.Select(w => WorkplaceMapper.ToRecord(new Workplace(w.Name, w.Location)));
context.Workplaces.AddRange(workplaces);
await context.SaveChangesAsync();
}
@@ -35,7 +36,7 @@ public static class DatabaseSeeder
{
var hash = passwordHasher.Hash(AdminPassword);
var admin = User.Create(AdminEmail, "Admin", hash, isAdmin: true).Value!;
- context.Users.Add(admin);
+ context.Users.Add(UserMapper.ToRecord(admin));
await context.SaveChangesAsync();
}
}
diff --git a/src/backend/src/Randall.Infrastructure/Persistence/Users/UserRepository.cs b/src/backend/src/Randall.Infrastructure/Persistence/Users/UserRepository.cs
index 62c89ae..311dbc3 100644
--- a/src/backend/src/Randall.Infrastructure/Persistence/Users/UserRepository.cs
+++ b/src/backend/src/Randall.Infrastructure/Persistence/Users/UserRepository.cs
@@ -1,37 +1,73 @@
using Microsoft.EntityFrameworkCore;
using Randall.Domain.Users;
+using Randall.Infrastructure.Persistence.Mappers;
+using Randall.Infrastructure.Persistence.Records;
namespace Randall.Infrastructure.Persistence.Users;
public class UserRepository(AppDbContext context) : IUserRepository
{
- public Task GetByEmailAsync(string email, CancellationToken ct = default) =>
- context.Users.FirstOrDefaultAsync(u => u.Email == email.ToLowerInvariant(), ct);
+ private readonly Dictionary _tracked = [];
- public Task GetByIdAsync(Guid id, CancellationToken ct = default) =>
- context.Users.FirstOrDefaultAsync(u => u.Id == id, ct);
+ private User Track(UserRecord record)
+ {
+ var domain = UserMapper.ToDomain(record);
+ _tracked[domain.Id] = (domain, record);
+ return domain;
+ }
+
+ public async Task GetByEmailAsync(string email, CancellationToken ct = default)
+ {
+ var record = await context.Users.FirstOrDefaultAsync(u => u.Email == email.ToLowerInvariant(), ct);
+ return record is null ? null : Track(record);
+ }
+
+ public async Task GetByIdAsync(Guid id, CancellationToken ct = default)
+ {
+ var record = await context.Users.FirstOrDefaultAsync(u => u.Id == id, ct);
+ return record is null ? null : Track(record);
+ }
public Task ExistsByEmailAsync(string email, CancellationToken ct = default) =>
context.Users.AnyAsync(u => u.Email == email.ToLowerInvariant(), ct);
- public Task> GetPendingAsync(CancellationToken ct = default) =>
- context.Users.Where(u => !u.IsApproved && !u.IsAdmin).ToListAsync(ct);
+ public async Task> GetPendingAsync(CancellationToken ct = default)
+ {
+ var records = await context.Users.Where(u => !u.IsApproved && !u.IsAdmin).ToListAsync(ct);
+ return records.Select(UserMapper.ToDomain).ToList();
+ }
- public Task> GetAllAsync(CancellationToken ct = default) =>
- context.Users.OrderBy(u => u.Name).ToListAsync(ct);
+ public async Task> GetAllAsync(CancellationToken ct = default)
+ {
+ var records = await context.Users.OrderBy(u => u.Name).ToListAsync(ct);
+ return records.Select(UserMapper.ToDomain).ToList();
+ }
public Task CountAdminsAsync(CancellationToken ct = default) =>
context.Users.CountAsync(u => u.IsAdmin);
- public Task> GetAllNonAdminAsync(CancellationToken ct = default) =>
- context.Users.Where(u => !u.IsAdmin).OrderBy(u => u.Name).ToListAsync(ct);
+ public async Task> GetAllNonAdminAsync(CancellationToken ct = default)
+ {
+ var records = await context.Users.Where(u => !u.IsAdmin).OrderBy(u => u.Name).ToListAsync(ct);
+ return records.Select(UserMapper.ToDomain).ToList();
+ }
- public async Task AddAsync(User user, CancellationToken ct = default) =>
- await context.Users.AddAsync(user, ct);
+ public async Task AddAsync(User user, CancellationToken ct = default)
+ {
+ var record = UserMapper.ToRecord(user);
+ await context.Users.AddAsync(record, ct);
+ }
- public void Delete(User user) =>
- context.Users.Remove(user);
+ public void Delete(User user)
+ {
+ if (_tracked.TryGetValue(user.Id, out var entry))
+ context.Users.Remove(entry.Record);
+ }
- public Task SaveChangesAsync(CancellationToken ct = default) =>
- context.SaveChangesAsync(ct);
+ public Task SaveChangesAsync(CancellationToken ct = default)
+ {
+ foreach (var (domain, record) in _tracked.Values)
+ UserMapper.SyncToRecord(domain, record);
+ return context.SaveChangesAsync(ct);
+ }
}
diff --git a/src/backend/src/Randall.Infrastructure/Persistence/Workplaces/WorkplaceRepository.cs b/src/backend/src/Randall.Infrastructure/Persistence/Workplaces/WorkplaceRepository.cs
index 9a69c07..6ef7da3 100644
--- a/src/backend/src/Randall.Infrastructure/Persistence/Workplaces/WorkplaceRepository.cs
+++ b/src/backend/src/Randall.Infrastructure/Persistence/Workplaces/WorkplaceRepository.cs
@@ -1,13 +1,20 @@
using Microsoft.EntityFrameworkCore;
using Randall.Domain.Workplaces;
+using Randall.Infrastructure.Persistence.Mappers;
namespace Randall.Infrastructure.Persistence.Workplaces;
public class WorkplaceRepository(AppDbContext context) : IWorkplaceRepository
{
- public Task GetByIdAsync(Guid id, CancellationToken ct = default) =>
- context.Workplaces.FirstOrDefaultAsync(w => w.Id == id, ct);
+ public async Task GetByIdAsync(Guid id, CancellationToken ct = default)
+ {
+ var record = await context.Workplaces.FirstOrDefaultAsync(w => w.Id == id, ct);
+ return record is null ? null : WorkplaceMapper.ToDomain(record);
+ }
- public async Task> GetAllActiveAsync(CancellationToken ct = default) =>
- await context.Workplaces.Where(w => w.IsActive).ToListAsync(ct);
+ public async Task> GetAllActiveAsync(CancellationToken ct = default)
+ {
+ var records = await context.Workplaces.Where(w => w.IsActive).ToListAsync(ct);
+ return records.Select(WorkplaceMapper.ToDomain).ToList();
+ }
}