C
C#15mo ago
Alerin

❔ How to verify data according to DDD, SOLID and Clean Architecture?

I own in two places one IF, it is:
if(credential.Status != Credential.StatusType.Verified)
if(credential.Status != Credential.StatusType.Verified)
According to the above rules, how not to duplicate the condition? I would like to avoid a problem in the future where we change something in one place and forget the other. My code:
public async Task<bool> CheckCredentialAsync(string name)
{
var credential = await _credentialRepository.GetByValueAsync(name, Domain.Entities.Credential.MethodType.Phone);

if(credential is null)
return false;

if(credential.Status != Domain.Entities.Credential.StatusType.Verified)
return false;

return true;
}
public async Task<bool> CheckCredentialAsync(string name)
{
var credential = await _credentialRepository.GetByValueAsync(name, Domain.Entities.Credential.MethodType.Phone);

if(credential is null)
return false;

if(credential.Status != Domain.Entities.Credential.StatusType.Verified)
return false;

return true;
}
4 Replies
Alerin
Alerin15mo ago
public async Task<Guid> AuthAsync(string value, Credential.MethodType type, string code)
{
var credential = await _credentialRepository.GetByValueAsync(value, type);

// Jeżeli poświadczenie nie istnieje czyli nie ma użytkownika w bazie danych
if (credential == null)
throw new AuthenticationException("Invalid credentials.");

if(credential.Status != Credential.StatusType.Verified)
throw new AuthenticationException("The credential is unverified");

var authentication = await _authenticationRepository.GetAsync(credential.User);

// Jeżeli kod się nie zgadza
if (authentication == null)
throw new AuthenticationException("Invalid authorization.");

// Jeżeli przekroczono ilość prób logowania (anti brute force)
if (authentication.Attempts <= 0)
throw new AuthenticationException("Authorization attempts exceeded.");

// Jeżeli kod jest nie poprawny
if(authentication.Code != code)
{
authentication.Attempts--;
await _authenticationRepository.UpdateAsync(credential.User, authentication);
throw new AuthenticationException("Invalid authorization code.");
}

if (this.Ip == null)
throw new AuthenticationException("Ip is empty");

var session = await _sessionRepository.AddAsync(new(credential.User, this.Ip, this.Metadata));

return session;
}
public async Task<Guid> AuthAsync(string value, Credential.MethodType type, string code)
{
var credential = await _credentialRepository.GetByValueAsync(value, type);

// Jeżeli poświadczenie nie istnieje czyli nie ma użytkownika w bazie danych
if (credential == null)
throw new AuthenticationException("Invalid credentials.");

if(credential.Status != Credential.StatusType.Verified)
throw new AuthenticationException("The credential is unverified");

var authentication = await _authenticationRepository.GetAsync(credential.User);

// Jeżeli kod się nie zgadza
if (authentication == null)
throw new AuthenticationException("Invalid authorization.");

// Jeżeli przekroczono ilość prób logowania (anti brute force)
if (authentication.Attempts <= 0)
throw new AuthenticationException("Authorization attempts exceeded.");

// Jeżeli kod jest nie poprawny
if(authentication.Code != code)
{
authentication.Attempts--;
await _authenticationRepository.UpdateAsync(credential.User, authentication);
throw new AuthenticationException("Invalid authorization code.");
}

if (this.Ip == null)
throw new AuthenticationException("Ip is empty");

var session = await _sessionRepository.AddAsync(new(credential.User, this.Ip, this.Metadata));

return session;
}
Model:
namespace Stand.Plugins.Identity.Domain.Entities;

/// <summary>
/// Dane logowania
/// Adres mail, numer telefonu, social media, karty RFID itp.
/// </summary>
[Index(nameof(Method), nameof(Value), IsUnique = true)]
public class Credential
{
public int Id { get; set; }
public User User { get; set; } = null!;
public Guid UserId { get; set; }
public MethodType Method { get; set; }
public string Value { get; set; } = string.Empty;
public StatusType Status { get; set; }
public DateTime Created { get; set; } = DateTime.UtcNow;
public enum MethodType
{
None,
Phone,
Email,
}

public enum StatusType
{
None,
Unverified, // Niezweryfikowany
Verified, // Zweryfikowany
Expired, // Wygasły, np. zablokowany itp.
}

public class MethodModel
{
public string Mail { get; set; } = "mail";
public string Phone { get; set; } = "phone";
}
}
namespace Stand.Plugins.Identity.Domain.Entities;

/// <summary>
/// Dane logowania
/// Adres mail, numer telefonu, social media, karty RFID itp.
/// </summary>
[Index(nameof(Method), nameof(Value), IsUnique = true)]
public class Credential
{
public int Id { get; set; }
public User User { get; set; } = null!;
public Guid UserId { get; set; }
public MethodType Method { get; set; }
public string Value { get; set; } = string.Empty;
public StatusType Status { get; set; }
public DateTime Created { get; set; } = DateTime.UtcNow;
public enum MethodType
{
None,
Phone,
Email,
}

public enum StatusType
{
None,
Unverified, // Niezweryfikowany
Verified, // Zweryfikowany
Expired, // Wygasły, np. zablokowany itp.
}

public class MethodModel
{
public string Mail { get; set; } = "mail";
public string Phone { get; set; } = "phone";
}
}
I added this to the model:
public bool IsVerified()
=> Status == StatusType.Verified;
public bool IsVerified()
=> Status == StatusType.Verified;
next:
if(credential.IsVerified() == false)
return false;
if(credential.IsVerified() == false)
return false;
Does it comply with the above rules?
JakenVeina
JakenVeina15mo ago
I think you're over-thinking a trivial if statement DRY doesn't mean "all your business rules will always be centralized and never be repeated, ever" it's a subjective philosophy about consolidating business logic, when you can you're spending too much time worrying about nonsense hypotheticals, and not giving future maintainers enough credit if you think that
if(credential.Status != Credential.StatusType.Verified)
if(credential.Status != Credential.StatusType.Verified)
is not semantically obvious and it's a fool's errand to try and consolidate everything so that if you make future changes, you don't have to do your dilligence to make sure related code doesn't break you always need to be doing that dilligence if you're worried about the effort that's gonna take, you back it up with testing, not try and deduplicate every possible bit of business logic
dave1
dave115mo ago
This doesnt look like ddd
Accord
Accord15mo ago
Was this issue resolved? If so, run /close - otherwise I will mark this as stale and this post will be archived until there is new activity.
Want results from more Discord servers?
Add your server
More Posts