Add domain project and update references

This commit is contained in:
Aaron Po
2026-02-11 00:23:13 -05:00
parent 2b0f9876bc
commit c74b20079b
24 changed files with 110 additions and 77 deletions

View File

@@ -17,6 +17,7 @@
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<ProjectReference Include="..\..\Domain\Domain.Core\Domain.Core.csproj" />
<ProjectReference Include="..\..\Repository\Repository.Core\Repository.Core.csproj" /> <ProjectReference Include="..\..\Repository\Repository.Core\Repository.Core.csproj" />
<ProjectReference Include="..\..\Service\Service.Core\Service.Core.csproj" /> <ProjectReference Include="..\..\Service\Service.Core\Service.Core.csproj" />
</ItemGroup> </ItemGroup>

View File

@@ -1,5 +1,5 @@
using System.Net; using System.Net;
using Repository.Core.Entities; using Domain.Core.Entities;
using Microsoft.AspNetCore.Mvc; using Microsoft.AspNetCore.Mvc;
using Service.Core.Auth; using Service.Core.Auth;
using Service.Core.Jwt; using Service.Core.Jwt;

View File

@@ -1,4 +1,4 @@
using Repository.Core.Entities; using Domain.Core.Entities;
using Microsoft.AspNetCore.Mvc; using Microsoft.AspNetCore.Mvc;
using Service.Core.User; using Service.Core.User;

View File

@@ -7,6 +7,10 @@
<Project Path="Database/Database.Migrations/Database.Migrations.csproj" /> <Project Path="Database/Database.Migrations/Database.Migrations.csproj" />
<Project Path="Database/Database.Seed/Database.Seed.csproj" /> <Project Path="Database/Database.Seed/Database.Seed.csproj" />
</Folder> </Folder>
<Folder Name="/Domain/">
<Project Path="Domain/Domain.Core/Domain.Core.csproj" />
<Project Path="Domain/Domain.Validation/Domain.Validation.csproj" />
</Folder>
<Folder Name="/Repository/"> <Folder Name="/Repository/">
<Project Path="Repository/Repository.Core/Repository.Core.csproj" /> <Project Path="Repository/Repository.Core/Repository.Core.csproj" />
<Project Path="Repository/Repository.Tests/Repository.Tests.csproj" /> <Project Path="Repository/Repository.Tests/Repository.Tests.csproj" />

View File

@@ -18,6 +18,7 @@
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<ProjectReference Include="..\..\Domain\Domain.Core\Domain.Core.csproj" />
<ProjectReference Include="..\..\Repository\Repository.Core\Repository.Core.csproj" /> <ProjectReference Include="..\..\Repository\Repository.Core\Repository.Core.csproj" />
</ItemGroup> </ItemGroup>
</Project> </Project>

View File

@@ -1,7 +1,7 @@
using System.Data; using System.Data;
using System.Security.Cryptography; using System.Security.Cryptography;
using System.Text; using System.Text;
using Repository.Core.Entities; using Domain.Core.Entities;
using Repository.Core.Repositories; using Repository.Core.Repositories;
using idunno.Password; using idunno.Password;
using Konscious.Security.Cryptography; using Konscious.Security.Cryptography;

View File

@@ -0,0 +1,8 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net10.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
<RootNamespace>Domain.Core</RootNamespace>
</PropertyGroup>
</Project>

View File

@@ -0,0 +1,14 @@
namespace Domain.Core.Entities;
public class UserAccount
{
public Guid UserAccountId { get; set; }
public string Username { get; set; } = string.Empty;
public string FirstName { get; set; } = string.Empty;
public string LastName { get; set; } = string.Empty;
public string Email { get; set; } = string.Empty;
public DateTime CreatedAt { get; set; }
public DateTime? UpdatedAt { get; set; }
public DateTime DateOfBirth { get; set; }
public byte[]? Timer { get; set; }
}

View File

@@ -0,0 +1,11 @@
namespace Domain.Core.Entities;
public class UserCredential
{
public Guid UserCredentialId { get; set; }
public Guid UserAccountId { get; set; }
public DateTime CreatedAt { get; set; }
public DateTime Expiry { get; set; }
public string Hash { get; set; } = string.Empty;
public byte[]? Timer { get; set; }
}

View File

@@ -0,0 +1,9 @@
namespace Domain.Core.Entities;
public class UserVerification
{
public Guid UserVerificationId { get; set; }
public Guid UserAccountId { get; set; }
public DateTime VerificationDateTime { get; set; }
public byte[]? Timer { get; set; }
}

View File

@@ -0,0 +1,12 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net10.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
<RootNamespace>Domain.Validation</RootNamespace>
</PropertyGroup>
<ItemGroup>
<ProjectReference Include="..\Domain.Core\Domain.Core.csproj" />
</ItemGroup>
</Project>

View File

@@ -1,16 +0,0 @@
namespace Repository.Core.Entities;
public class UserAccount
{
public Guid UserAccountId { get; set; }
public string Username { get; set; } = string.Empty;
public string FirstName { get; set; } = string.Empty;
public string LastName { get; set; } = string.Empty;
public string Email { get; set; } = string.Empty;
public DateTime CreatedAt { get; set; }
public DateTime? UpdatedAt { get; set; }
public DateTime DateOfBirth { get; set; }
public byte[]? Timer { get; set; }
}

View File

@@ -1,11 +0,0 @@
namespace Repository.Core.Entities;
public class UserCredential
{
public Guid UserCredentialId { get; set; }
public Guid UserAccountId { get; set; }
public DateTime CreatedAt { get; set; }
public DateTime Expiry { get; set; }
public string Hash { get; set; } = string.Empty;
public byte[]? Timer { get; set; }
}

View File

@@ -1,9 +0,0 @@
namespace Repository.Core.Entities;
public class UserVerification
{
public Guid UserVerificationId { get; set; }
public Guid UserAccountId { get; set; }
public DateTime VerificationDateTime { get; set; }
public byte[]? Timer { get; set; }
}

View File

@@ -1,10 +1,11 @@
using System.Data; using System.Data;
using System.Data.Common; using System.Data.Common;
using Domain.Core.Entities;
using Repository.Core.Sql; using Repository.Core.Sql;
namespace Repository.Core.Repositories.Auth namespace Repository.Core.Repositories.Auth
{ {
public class AuthRepository : Repository<Entities.UserAccount>, IAuthRepository public class AuthRepository : Repository<Domain.Core.Entities.UserAccount>, IAuthRepository
{ {
public AuthRepository(ISqlConnectionFactory connectionFactory) public AuthRepository(ISqlConnectionFactory connectionFactory)
: base(connectionFactory) : base(connectionFactory)
@@ -12,7 +13,7 @@ namespace Repository.Core.Repositories.Auth
} }
public async Task<Entities.UserAccount> RegisterUserAsync( public async Task<Domain.Core.Entities.UserAccount> RegisterUserAsync(
string username, string username,
string firstName, string firstName,
string lastName, string lastName,
@@ -36,7 +37,7 @@ namespace Repository.Core.Repositories.Auth
var result = await command.ExecuteScalarAsync(); var result = await command.ExecuteScalarAsync();
var userAccountId = result != null ? (Guid)result : Guid.Empty; var userAccountId = result != null ? (Guid)result : Guid.Empty;
return new Entities.UserAccount return new Domain.Core.Entities.UserAccount
{ {
UserAccountId = userAccountId, UserAccountId = userAccountId,
Username = username, Username = username,
@@ -49,7 +50,7 @@ namespace Repository.Core.Repositories.Auth
} }
public async Task<Entities.UserAccount?> GetUserByEmailAsync(string email) public async Task<Domain.Core.Entities.UserAccount?> GetUserByEmailAsync(string email)
{ {
await using var connection = await CreateConnection(); await using var connection = await CreateConnection();
await using var command = connection.CreateCommand(); await using var command = connection.CreateCommand();
@@ -63,7 +64,7 @@ namespace Repository.Core.Repositories.Auth
} }
public async Task<Entities.UserAccount?> GetUserByUsernameAsync(string username) public async Task<Domain.Core.Entities.UserAccount?> GetUserByUsernameAsync(string username)
{ {
await using var connection = await CreateConnection(); await using var connection = await CreateConnection();
await using var command = connection.CreateCommand(); await using var command = connection.CreateCommand();
@@ -76,7 +77,7 @@ namespace Repository.Core.Repositories.Auth
return await reader.ReadAsync() ? MapToEntity(reader) : null; return await reader.ReadAsync() ? MapToEntity(reader) : null;
} }
public async Task<Entities.UserCredential?> GetActiveCredentialByUserAccountIdAsync(Guid userAccountId) public async Task<UserCredential?> GetActiveCredentialByUserAccountIdAsync(Guid userAccountId)
{ {
await using var connection = await CreateConnection(); await using var connection = await CreateConnection();
await using var command = connection.CreateCommand(); await using var command = connection.CreateCommand();
@@ -105,9 +106,9 @@ namespace Repository.Core.Repositories.Auth
/// <summary> /// <summary>
/// Maps a data reader row to a UserAccount entity. /// Maps a data reader row to a UserAccount entity.
/// </summary> /// </summary>
protected override Entities.UserAccount MapToEntity(DbDataReader reader) protected override Domain.Core.Entities.UserAccount MapToEntity(DbDataReader reader)
{ {
return new Entities.UserAccount return new Domain.Core.Entities.UserAccount
{ {
UserAccountId = reader.GetGuid(reader.GetOrdinal("UserAccountId")), UserAccountId = reader.GetGuid(reader.GetOrdinal("UserAccountId")),
Username = reader.GetString(reader.GetOrdinal("Username")), Username = reader.GetString(reader.GetOrdinal("Username")),
@@ -128,9 +129,9 @@ namespace Repository.Core.Repositories.Auth
/// <summary> /// <summary>
/// Maps a data reader row to a UserCredential entity. /// Maps a data reader row to a UserCredential entity.
/// </summary> /// </summary>
private static Entities.UserCredential MapToCredentialEntity(DbDataReader reader) private static UserCredential MapToCredentialEntity(DbDataReader reader)
{ {
var entity = new Entities.UserCredential var entity = new UserCredential
{ {
UserCredentialId = reader.GetGuid(reader.GetOrdinal("UserCredentialId")), UserCredentialId = reader.GetGuid(reader.GetOrdinal("UserCredentialId")),
UserAccountId = reader.GetGuid(reader.GetOrdinal("UserAccountId")), UserAccountId = reader.GetGuid(reader.GetOrdinal("UserAccountId")),

View File

@@ -1,3 +1,5 @@
using Domain.Core.Entities;
namespace Repository.Core.Repositories.Auth namespace Repository.Core.Repositories.Auth
{ {
/// <summary> /// <summary>
@@ -16,7 +18,7 @@ namespace Repository.Core.Repositories.Auth
/// <param name="dateOfBirth">User's date of birth</param> /// <param name="dateOfBirth">User's date of birth</param>
/// <param name="passwordHash">Hashed password</param> /// <param name="passwordHash">Hashed password</param>
/// <returns>The newly created UserAccount with generated ID</returns> /// <returns>The newly created UserAccount with generated ID</returns>
Task<Entities.UserAccount> RegisterUserAsync( Task<Domain.Core.Entities.UserAccount> RegisterUserAsync(
string username, string username,
string firstName, string firstName,
string lastName, string lastName,
@@ -30,7 +32,7 @@ namespace Repository.Core.Repositories.Auth
/// </summary> /// </summary>
/// <param name="email">Email address to search for</param> /// <param name="email">Email address to search for</param>
/// <returns>UserAccount if found, null otherwise</returns> /// <returns>UserAccount if found, null otherwise</returns>
Task<Entities.UserAccount?> GetUserByEmailAsync(string email); Task<Domain.Core.Entities.UserAccount?> GetUserByEmailAsync(string email);
/// <summary> /// <summary>
/// Retrieves a user account by username (typically used for login). /// Retrieves a user account by username (typically used for login).
@@ -38,7 +40,7 @@ namespace Repository.Core.Repositories.Auth
/// </summary> /// </summary>
/// <param name="username">Username to search for</param> /// <param name="username">Username to search for</param>
/// <returns>UserAccount if found, null otherwise</returns> /// <returns>UserAccount if found, null otherwise</returns>
Task<Entities.UserAccount?> GetUserByUsernameAsync(string username); Task<Domain.Core.Entities.UserAccount?> GetUserByUsernameAsync(string username);
/// <summary> /// <summary>
/// Retrieves the active (non-revoked) credential for a user account. /// Retrieves the active (non-revoked) credential for a user account.
@@ -46,7 +48,7 @@ namespace Repository.Core.Repositories.Auth
/// </summary> /// </summary>
/// <param name="userAccountId">ID of the user account</param> /// <param name="userAccountId">ID of the user account</param>
/// <returns>Active UserCredential if found, null otherwise</returns> /// <returns>Active UserCredential if found, null otherwise</returns>
Task<Entities.UserCredential?> GetActiveCredentialByUserAccountIdAsync(Guid userAccountId); Task<UserCredential?> GetActiveCredentialByUserAccountIdAsync(Guid userAccountId);
/// <summary> /// <summary>
/// Rotates a user's credential by invalidating all existing credentials and creating a new one. /// Rotates a user's credential by invalidating all existing credentials and creating a new one.

View File

@@ -1,14 +1,15 @@
using Domain.Core.Entities;
namespace Repository.Core.Repositories.UserAccount namespace Repository.Core.Repositories.UserAccount
{ {
public interface IUserAccountRepository public interface IUserAccountRepository
{ {
Task<Entities.UserAccount?> GetByIdAsync(Guid id); Task<Domain.Core.Entities.UserAccount?> GetByIdAsync(Guid id);
Task<IEnumerable<Entities.UserAccount>> GetAllAsync(int? limit, int? offset); Task<IEnumerable<Domain.Core.Entities.UserAccount>> GetAllAsync(int? limit, int? offset);
Task UpdateAsync(Entities.UserAccount userAccount); Task UpdateAsync(Domain.Core.Entities.UserAccount userAccount);
Task DeleteAsync(Guid id); Task DeleteAsync(Guid id);
Task<Entities.UserAccount?> GetByUsernameAsync(string username); Task<Domain.Core.Entities.UserAccount?> GetByUsernameAsync(string username);
Task<Entities.UserAccount?> GetByEmailAsync(string email); Task<Domain.Core.Entities.UserAccount?> GetByEmailAsync(string email);
} }
} }

View File

@@ -1,13 +1,14 @@
using System.Data; using System.Data;
using System.Data.Common; using System.Data.Common;
using Domain.Core.Entities;
using Repository.Core.Sql; using Repository.Core.Sql;
namespace Repository.Core.Repositories.UserAccount namespace Repository.Core.Repositories.UserAccount
{ {
public class UserAccountRepository(ISqlConnectionFactory connectionFactory) public class UserAccountRepository(ISqlConnectionFactory connectionFactory)
: Repository<Entities.UserAccount>(connectionFactory), IUserAccountRepository : Repository<Domain.Core.Entities.UserAccount>(connectionFactory), IUserAccountRepository
{ {
public async Task<Entities.UserAccount?> GetByIdAsync(Guid id) public async Task<Domain.Core.Entities.UserAccount?> GetByIdAsync(Guid id)
{ {
await using var connection = await CreateConnection(); await using var connection = await CreateConnection();
await using var command = connection.CreateCommand(); await using var command = connection.CreateCommand();
@@ -20,7 +21,7 @@ namespace Repository.Core.Repositories.UserAccount
return await reader.ReadAsync() ? MapToEntity(reader) : null; return await reader.ReadAsync() ? MapToEntity(reader) : null;
} }
public async Task<IEnumerable<Entities.UserAccount>> GetAllAsync(int? limit, int? offset) public async Task<IEnumerable<Domain.Core.Entities.UserAccount>> GetAllAsync(int? limit, int? offset)
{ {
await using var connection = await CreateConnection(); await using var connection = await CreateConnection();
await using var command = connection.CreateCommand(); await using var command = connection.CreateCommand();
@@ -34,7 +35,7 @@ namespace Repository.Core.Repositories.UserAccount
AddParameter(command, "@Offset", offset.Value); AddParameter(command, "@Offset", offset.Value);
await using var reader = await command.ExecuteReaderAsync(); await using var reader = await command.ExecuteReaderAsync();
var users = new List<Entities.UserAccount>(); var users = new List<Domain.Core.Entities.UserAccount>();
while (await reader.ReadAsync()) while (await reader.ReadAsync())
{ {
@@ -44,7 +45,7 @@ namespace Repository.Core.Repositories.UserAccount
return users; return users;
} }
public async Task UpdateAsync(Entities.UserAccount userAccount) public async Task UpdateAsync(Domain.Core.Entities.UserAccount userAccount)
{ {
await using var connection = await CreateConnection(); await using var connection = await CreateConnection();
await using var command = connection.CreateCommand(); await using var command = connection.CreateCommand();
@@ -72,7 +73,7 @@ namespace Repository.Core.Repositories.UserAccount
await command.ExecuteNonQueryAsync(); await command.ExecuteNonQueryAsync();
} }
public async Task<Entities.UserAccount?> GetByUsernameAsync(string username) public async Task<Domain.Core.Entities.UserAccount?> GetByUsernameAsync(string username)
{ {
await using var connection = await CreateConnection(); await using var connection = await CreateConnection();
await using var command = connection.CreateCommand(); await using var command = connection.CreateCommand();
@@ -85,7 +86,7 @@ namespace Repository.Core.Repositories.UserAccount
return await reader.ReadAsync() ? MapToEntity(reader) : null; return await reader.ReadAsync() ? MapToEntity(reader) : null;
} }
public async Task<Entities.UserAccount?> GetByEmailAsync(string email) public async Task<Domain.Core.Entities.UserAccount?> GetByEmailAsync(string email)
{ {
await using var connection = await CreateConnection(); await using var connection = await CreateConnection();
await using var command = connection.CreateCommand(); await using var command = connection.CreateCommand();
@@ -98,9 +99,9 @@ namespace Repository.Core.Repositories.UserAccount
return await reader.ReadAsync() ? MapToEntity(reader) : null; return await reader.ReadAsync() ? MapToEntity(reader) : null;
} }
protected override Entities.UserAccount MapToEntity(DbDataReader reader) protected override Domain.Core.Entities.UserAccount MapToEntity(DbDataReader reader)
{ {
return new Entities.UserAccount return new Domain.Core.Entities.UserAccount
{ {
UserAccountId = reader.GetGuid(reader.GetOrdinal("UserAccountId")), UserAccountId = reader.GetGuid(reader.GetOrdinal("UserAccountId")),
Username = reader.GetString(reader.GetOrdinal("Username")), Username = reader.GetString(reader.GetOrdinal("Username")),

View File

@@ -14,4 +14,7 @@
<PackageReference Include="System.Data.SqlClient" Version="4.9.0" /> <PackageReference Include="System.Data.SqlClient" Version="4.9.0" />
<PackageReference Include="Microsoft.Extensions.Configuration.Abstractions" Version="8.0.0" /> <PackageReference Include="Microsoft.Extensions.Configuration.Abstractions" Version="8.0.0" />
</ItemGroup> </ItemGroup>
<ItemGroup>
<ProjectReference Include="..\..\Domain\Domain.Core\Domain.Core.csproj" />
</ItemGroup>
</Project> </Project>

View File

@@ -1,4 +1,4 @@
using Repository.Core.Entities; using Domain.Core.Entities;
using Repository.Core.Repositories.Auth; using Repository.Core.Repositories.Auth;
using Service.Core.Password; using Service.Core.Password;

View File

@@ -1,4 +1,4 @@
using Repository.Core.Entities; using Domain.Core.Entities;
namespace Service.Core.Auth; namespace Service.Core.Auth;

View File

@@ -11,6 +11,7 @@
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<ProjectReference Include="..\..\Domain\Domain.Core\Domain.Core.csproj" />
<ProjectReference Include="..\..\Repository\Repository.Core\Repository.Core.csproj" /> <ProjectReference Include="..\..\Repository\Repository.Core\Repository.Core.csproj" />
</ItemGroup> </ItemGroup>
</Project> </Project>

View File

@@ -1,4 +1,4 @@
using Repository.Core.Entities; using Domain.Core.Entities;
namespace Service.Core.User; namespace Service.Core.User;

View File

@@ -1,4 +1,4 @@
using Repository.Core.Entities; using Domain.Core.Entities;
using Repository.Core.Repositories.UserAccount; using Repository.Core.Repositories.UserAccount;
namespace Service.Core.User; namespace Service.Core.User;