3
\$\begingroup\$

I’m new to Clean Architecture and EF Core, and I’m trying to learn by practicing and watching tutorials. I’ve been implementing a basic authentication flow and would appreciate some feedback on whether I’m structuring my code correctly according to Clean Architecture principles.

Here’s the structure of my code:

this is my controller:

[ApiController]
[Route("/auth")]
public class AuthController(IAuthService authService, IMapper mapper) : ControllerBase
{
    [HttpPost("sign-up")]
    [AllowAnonymous]
    public async Task<IActionResult> RegisterUser([FromBody] EssentialSignUpCredentials registerCredentials)
    {
        
        var clientEmail = registerCredentials.email;
        var clientUserName = registerCredentials.userName;

        var ipAddress = HttpContext.Connection.RemoteIpAddress?.MapToIPv4();

        var response=await authService.SignUpAsync(registerCredentials.email, registerCredentials.userName, ipAddress);
        
        return response.Match<IActionResult>(Ok, BadRequest);
    }

this is my auth service sign up method:

public async Task<Result> SignUpAsync(string email, string userName, IPAddress ipAddress)
    {
        await using var transaction = await dbContext.Database.BeginTransactionAsync();

        var response = await userService.CreateUserAsync(email, userName, ipAddress);


        return await response.MatchAsync<Result, User>(async success =>
        {
            var rolesResponse = await userService.AddRolesToUserAsync(success.Data);
            if (!rolesResponse.isSuccess)
                return Result.Failure(rolesResponse.FailureValue.errors.ToArray(),
                    rolesResponse.FailureValue.StatusCode);


            await transaction.CommitAsync();
            return Result.Success();
        }, failure => Result.Failure(response.FailureValue.errors.ToArray(), response.FailureValue.StatusCode));
    }

and lastly this is the user service :

public async Task<Result<User>> CreateUserAsync(string email, string userName,
        IPAddress ipAddress)
    {
        if (string.IsNullOrEmpty(email) ||
            string.IsNullOrEmpty(userName))
        {
            logger.LogInformation("user didnt fill credentials");
            return Result<User>.Failure(["user didnt fill credentials".ToUpper()], StatusCodes.Status400BadRequest);
        }


        var userToBeRegistered = new User
        {
            Email = email, UserName = userName,
            IpAddress = ipAddress.ToString()
        };


        var response = await userManager.CreateAsync(userToBeRegistered);

        if (!response.Succeeded)
        {
            List<string> errorsList = [];

            foreach (var responseError in response.Errors)
            {
                var userError = responseError.Code switch
                {
                    "DuplicateUserName" => "the username is already in use please choose another one".ToUpper(),
                    "DuplicateEmail" => "the email address is already in use please choose another one".ToUpper(),
                    "PasswordTooShort" => "the password must contain at least 6 characters",
                    "PasswordRequiresNonAlphanumeric" => "please use both characters and numbers for your password",
                    _ => "An error occurred. Please try again"
                };
                errorsList.Add(userError);
            }

            logger.LogInformation(string.Join(", ", response.Errors));

            return Result<User>.Failure(errorsList.ToArray(), StatusCodes.Status400BadRequest);
        }

My Approach Controller: Thin controller with all the logic moved to AuthService. Service Layer: Contains business logic for signing up a user, ensuring database transaction management, and handling roles. UserService: Handles direct interaction with UserManager for user creation and error mapping.

Is my approach correct in terms of Clean Architecture principles (keeping the controller thin and moving logic to services)?

Am I managing responsibilities correctly by separating the AuthService for business logic and UserService for user-specific operations?

Are there any improvements you would recommend in terms of structure, error handling, or code practices?

Any advice or constructive feedback would be greatly appreciated!

\$\endgroup\$
8
  • \$\begingroup\$ Welcome to Code Review. Does the code currently work as intended? \$\endgroup\$ Commented Jan 29 at 11:07
  • \$\begingroup\$ @Mast thank you mast for replying and yes it works as intended , the thing that bothers me is my way of implementing it , is it considered right or is it too messy ? \$\endgroup\$ Commented Jan 29 at 11:18
  • \$\begingroup\$ I've just gone through your code quickly but I haven't seen anything specific to clean architecture. It seems more like a "classic" n-tier MVC app. \$\endgroup\$ Commented Jan 29 at 12:05
  • \$\begingroup\$ did you mean Clean Code? \$\endgroup\$ Commented Jan 29 at 12:36
  • 3
    \$\begingroup\$ @Lemonjuice if you want Clean Architecture then you will need to provide more context, such as how the project is structured, and show some code that supports that structure. Currently your question lake that, so we can't review it. But we can review it as Clean code for that part only. \$\endgroup\$ Commented Jan 29 at 13:46

0

You must log in to answer this question.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.