The application user has a lot of standard functionality we see in most applications. At a high-level, this includes some form of authentication, authorization, and session management. At a low-level, we have functions like:
- Checking the password strength;
- Hashing the password, perhaps with a bit of salt;
- Manipulating a hashmap of data tied to the session object;
- Checking if the user has a role that has the permission necessary to call a function.
Looking at Martin's Clean Architecture, I believe that the user high-level logic falls under use cases/application business rules. But do the details belong here as well?
For example, when a User registers or performs a login, the application needs to utilize best-practice algorithms, that should change as new attacks and vulnerabilities are discovered.
The request arrives from some UI and is handled by a controller at its first stop. Now, from a security perspective, we would want the password to be hashed as close to the entry point as possible, to reduce the severity of bugs that would leak the password into error messages, session data, logs, etc. This would mean that such functionality belongs in the controller. However, my understanding is that the controller is very thin and serves only as a proxy adapter between the UI and the application logic. As such, it shouldn't worry about complex operations such as hashing, but should focus on data translation and invoking the correct functions.
Now, at the use case layer, we might have a UserService
class that offers a registerUser(username, password)
function. The registerUser
might have code such as:
registerUser(username, password) {
if(isNotUnique(username)) throw new UserNameNotUniqueException();
if(isNotStrong(password, username)) throw new InsufficientPasswordStrengthException();
var password_hash = hashPassword(password);
userRepo.save(new User(username, password_hash));
The isNotUnique
calls the userRepository to retrieve a user with the given username. If the list is empty, the username is unique. The isNotStrong
performs a series of checks for length, complexity, and similairty with the username. The hashPassword
calls the latest and greatest library for performing the password hashing and supplies the strongest configuration parameters.
Ideally, we would have an adapter between the UserService
and the PasswordHasher
so that we can switch between implementations if the library becomes vulnerable. Would that make the PasswordHasher
an interface adapter, while the actual library belongs to the Frameworks and Drivers layer?
My second question is how does all of this fit into the old MVC design? Would the user-related application logic fall inside the model? Is the MVC controller a similarly thin layer as Martin's interface adapter layer?
Third, would access control logic be more suitable at the application logic layer, instead of the controllers?
Finally, I would welcome comments and corrections for all the reasoning given above.