@startuml class-diagram !theme plain skinparam backgroundColor #FFFFFF skinparam defaultFontName Arial skinparam classAttributeIconSize 0 skinparam linetype ortho title Biergarten Application - Class Diagram ' API Layer package "API.Core" <> #E3F2FD { class AuthController { - IRegisterService _registerService - ILoginService _loginService + <> Task Register(RegisterRequest) + <> Task Login(LoginRequest) } class UserController { - IUserService _userService + <> Task>> GetAll(int?, int?) + <> Task> GetById(Guid) } class GlobalExceptionFilter { - ILogger _logger + void OnException(ExceptionContext) } package "Contracts" { class RegisterRequest <> { + string Username + string FirstName + string LastName + string Email + DateTime DateOfBirth + string Password } class LoginRequest <> { + string Username + string Password } class RegisterRequestValidator { + RegisterRequestValidator() } class LoginRequestValidator { + LoginRequestValidator() } class "ResponseBody" <> { + string Message + T Payload } class LoginPayload <> { + Guid UserAccountId + string Username + string RefreshToken + string AccessToken } class RegistrationPayload <> { + Guid UserAccountId + string Username + string RefreshToken + string AccessToken + bool ConfirmationEmailSent } } } ' Service Layer package "Service Layer" <> #C8E6C9 { package "Service.Auth" { interface IRegisterService { + <> Task RegisterAsync(UserAccount, string) } class RegisterService { - IAuthRepository _authRepo - IPasswordInfrastructure _passwordInfra - ITokenService _tokenService - IEmailService _emailService + <> Task RegisterAsync(UserAccount, string) - <> Task ValidateUserDoesNotExist(UserAccount) } interface ILoginService { + <> Task LoginAsync(string, string) } class LoginService { - IAuthRepository _authRepo - IPasswordInfrastructure _passwordInfra - ITokenService _tokenService + <> Task LoginAsync(string, string) } interface ITokenService { + string GenerateAccessToken(UserAccount) + string GenerateRefreshToken(UserAccount) } class TokenService { - ITokenInfrastructure _tokenInfrastructure + string GenerateAccessToken(UserAccount) + string GenerateRefreshToken(UserAccount) } class RegisterServiceReturn <> { + bool IsAuthenticated + bool EmailSent + UserAccount UserAccount + string AccessToken + string RefreshToken } class LoginServiceReturn <> { + UserAccount UserAccount + string RefreshToken + string AccessToken } } package "Service.UserManagement" { interface IUserService { + <> Task> GetAllAsync(int?, int?) + <> Task GetByIdAsync(Guid) + <> Task UpdateAsync(UserAccount) } class UserService { - IUserAccountRepository _repository + <> Task> GetAllAsync(int?, int?) + <> Task GetByIdAsync(Guid) + <> Task UpdateAsync(UserAccount) } } package "Service.Emails" { interface IEmailService { + <> Task SendRegistrationEmailAsync(UserAccount, string) } class EmailService { - IEmailProvider _emailProvider - IEmailTemplateProvider _templateProvider + <> Task SendRegistrationEmailAsync(UserAccount, string) } } } ' Domain Layer package "Domain" <> #FFF9C4 { package "Domain.Entities" { class UserAccount { + Guid UserAccountId + string Username + string FirstName + string LastName + string Email + DateTime CreatedAt + DateTime? UpdatedAt + DateTime DateOfBirth + byte[]? Timer } class UserCredential { + Guid UserCredentialId + Guid UserAccountId + DateTime CreatedAt + DateTime Expiry + string Hash + byte[]? Timer } class UserVerification { + Guid UserVerificationId + Guid UserAccountId + DateTime VerificationDateTime + byte[]? Timer } } package "Domain.Exceptions" { class ConflictException { + ConflictException(string) } class NotFoundException { + NotFoundException(string) } class UnauthorizedException { + UnauthorizedException(string) } class ForbiddenException { + ForbiddenException(string) } class ValidationException { + ValidationException(string) } } } ' Infrastructure Layer package "Infrastructure" <> #E1BEE7 { package "Infrastructure.Repository" { interface IAuthRepository { + <> Task RegisterUserAsync(string, string, string, string, DateTime, string) + <> Task GetUserByEmailAsync(string) + <> Task GetUserByUsernameAsync(string) + <> Task GetActiveCredentialByUserAccountIdAsync(Guid) + <> Task RotateCredentialAsync(Guid, string) } class AuthRepository { - ISqlConnectionFactory _connectionFactory + <> Task RegisterUserAsync(...) + <> Task GetUserByEmailAsync(string) + <> Task GetUserByUsernameAsync(string) + <> Task GetActiveCredentialByUserAccountIdAsync(Guid) + <> Task RotateCredentialAsync(Guid, string) # UserAccount MapToEntity(DbDataReader) - UserCredential MapToCredentialEntity(DbDataReader) } interface IUserAccountRepository { + <> Task GetByIdAsync(Guid) + <> Task> GetAllAsync(int?, int?) + <> Task UpdateAsync(UserAccount) + <> Task DeleteAsync(Guid) + <> Task GetByUsernameAsync(string) + <> Task GetByEmailAsync(string) } class UserAccountRepository { - ISqlConnectionFactory _connectionFactory + <> Task GetByIdAsync(Guid) + <> Task> GetAllAsync(int?, int?) + <> Task UpdateAsync(UserAccount) + <> Task DeleteAsync(Guid) + <> Task GetByUsernameAsync(string) + <> Task GetByEmailAsync(string) # UserAccount MapToEntity(DbDataReader) } abstract class "Repository" { # ISqlConnectionFactory _connectionFactory # <> Task CreateConnection() # {abstract} T MapToEntity(DbDataReader) } package "Sql" { interface ISqlConnectionFactory { + DbConnection CreateConnection() } class DefaultSqlConnectionFactory { - string _connectionString + DbConnection CreateConnection() } class SqlConnectionStringHelper <> { + {static} string BuildConnectionString(string?) + {static} string BuildMasterConnectionString() } } } package "Infrastructure.PasswordHashing" { interface IPasswordInfrastructure { + string Hash(string) + bool Verify(string, string) } class Argon2Infrastructure { - {static} int SaltSize = 16 - {static} int HashSize = 32 - {static} int ArgonIterations = 4 - {static} int ArgonMemoryKb = 65536 + string Hash(string) + bool Verify(string, string) } } package "Infrastructure.Jwt" { interface ITokenInfrastructure { + string GenerateJwt(Guid, string, DateTime) } class JwtInfrastructure { - string? _secret + string GenerateJwt(Guid, string, DateTime) } } package "Infrastructure.Email" { interface IEmailProvider { + <> Task SendAsync(string, string, string, bool) + <> Task SendAsync(IEnumerable, string, string, bool) } class SmtpEmailProvider { - string _host - int _port - string? _username - string? _password - bool _useSsl - string _fromEmail - string _fromName + <> Task SendAsync(string, string, string, bool) + <> Task SendAsync(IEnumerable, string, string, bool) } } package "Infrastructure.Email.Templates" { interface IEmailTemplateProvider { + <> Task RenderUserRegisteredEmailAsync(string, string) } class EmailTemplateProvider { - IServiceProvider _serviceProvider - ILoggerFactory _loggerFactory + <> Task RenderUserRegisteredEmailAsync(string, string) - <> Task RenderComponentAsync(Dictionary) } class "UserRegistration <>" { + string Username + string ConfirmationLink } class "Header <>" { } class "Footer <>" { + string? FooterText } } } ' Database Layer package "Database" <> #FFCCBC { class "SQL Server" <> { .. Tables .. UserAccount UserCredential UserVerification UserAvatar Photo UserFollow Country StateProvince City BreweryPost BreweryPostLocation BreweryPostPhoto BeerStyle BeerPost BeerPostPhoto BeerPostComment .. Stored Procedures .. USP_RegisterUser usp_GetUserAccountByUsername usp_GetUserAccountByEmail usp_GetUserAccountById USP_GetActiveUserCredentialByUserAccountId USP_RotateUserCredential USP_CreateUserVerification } } ' Relationships - API to Service AuthController ..> IRegisterService : uses AuthController ..> ILoginService : uses UserController ..> IUserService : uses AuthController ..> RegisterRequest : receives AuthController ..> LoginRequest : receives AuthController ..> "ResponseBody" : returns AuthController ..> RegistrationPayload : returns AuthController ..> LoginPayload : returns RegisterRequest ..> RegisterRequestValidator : validated by LoginRequest ..> LoginRequestValidator : validated by ' Relationships - Service Layer IRegisterService <|.. RegisterService : implements ILoginService <|.. LoginService : implements ITokenService <|.. TokenService : implements IUserService <|.. UserService : implements IEmailService <|.. EmailService : implements RegisterService ..> IAuthRepository : uses RegisterService ..> IPasswordInfrastructure : uses RegisterService ..> ITokenService : uses RegisterService ..> IEmailService : uses RegisterService ..> RegisterServiceReturn : returns RegisterService ..> UserAccount : uses LoginService ..> IAuthRepository : uses LoginService ..> IPasswordInfrastructure : uses LoginService ..> ITokenService : uses LoginService ..> LoginServiceReturn : returns LoginService ..> UserAccount : uses LoginService ..> UserCredential : uses TokenService ..> ITokenInfrastructure : uses TokenService ..> UserAccount : uses UserService ..> IUserAccountRepository : uses UserService ..> UserAccount : uses EmailService ..> IEmailProvider : uses EmailService ..> IEmailTemplateProvider : uses EmailService ..> UserAccount : uses ' Relationships - Repository Layer IAuthRepository <|.. AuthRepository : implements IUserAccountRepository <|.. UserAccountRepository : implements "Repository" <|-- AuthRepository : extends "Repository" <|-- UserAccountRepository : extends AuthRepository ..> ISqlConnectionFactory : uses AuthRepository ..> UserAccount : returns AuthRepository ..> UserCredential : returns AuthRepository ..> "SQL Server" : queries UserAccountRepository ..> ISqlConnectionFactory : uses UserAccountRepository ..> UserAccount : returns UserAccountRepository ..> "SQL Server" : queries "Repository" ..> ISqlConnectionFactory : uses ISqlConnectionFactory <|.. DefaultSqlConnectionFactory : implements DefaultSqlConnectionFactory ..> SqlConnectionStringHelper : uses ' Relationships - Infrastructure IPasswordInfrastructure <|.. Argon2Infrastructure : implements ITokenInfrastructure <|.. JwtInfrastructure : implements IEmailProvider <|.. SmtpEmailProvider : implements IEmailTemplateProvider <|.. EmailTemplateProvider : implements EmailTemplateProvider ..> "UserRegistration <>" : renders "UserRegistration <>" ..> "Header <>" : includes "UserRegistration <>" ..> "Footer <>" : includes ' Relationships - Domain UserAccount -- UserCredential : "1" -- "*" UserAccount -- UserVerification : "1" -- "0..1" ' Exception handling GlobalExceptionFilter ..> ConflictException : catches GlobalExceptionFilter ..> NotFoundException : catches GlobalExceptionFilter ..> UnauthorizedException : catches GlobalExceptionFilter ..> ForbiddenException : catches GlobalExceptionFilter ..> ValidationException : catches RegisterService ..> ConflictException : throws LoginService ..> UnauthorizedException : throws UserService ..> NotFoundException : throws ' Notes note right of Argon2Infrastructure Security Parameters: - Memory: 64MB - Iterations: 4 - Salt: 16 bytes - Output: 32 bytes end note note right of JwtInfrastructure JWT Configuration: - Algorithm: HS256 - Access: 1 hour - Refresh: 21 days end note note right of "SQL Server" Stored Procedures: - USP_RegisterUser: Transaction creates UserAccount + UserCredential - Credentials tracked with IsRevoked flag end note note right of AuthRepository Uses ADO.NET with parameterized queries to prevent SQL injection end note note bottom of RegisterService Registration Flow: 1. Validate user doesn't exist 2. Hash password (Argon2) 3. Create account + credential 4. Generate tokens 5. Send confirmation email end note note bottom of LoginService Login Flow: 1. Find user by username 2. Get active credential 3. Verify password 4. Generate tokens 5. Return authenticated user end note @enduml