I have stumbled upon Gary Bernhardt's "Functional core, imperative shell" pattern and I find it and correlates with many other architectures such as the Hexagonal Architecture.
I have been trying to apply these principles but with no "success".
For instance, say I have a service AddUser
. AddUser
is the imperative shell. It needs to do 3 things:
- Check if a user with same username/email is already taken in the database
- Validate the user
- Insert the user in the database
Here it is:
class AddUser {
execute (username, email, password) {
if (this.userRepository.findUserByUsername(username)) { // tries to find an user with same username
throw new Error('Oops, username is already taken!')
} else if (this.userRepository.findUserByEmail(email)) { // tries to find an user with same email
throw new Error('Oops, email is already taken!')
}
if (!this.userValidator.validateUsername(username)) { // calls the UserValidator to validate username
throw new Error('Oops, username is invalid!')
} else if (!this.userValidator.validateEmail(email)) { // calls UserValidator to validate email
throw new Error('Oops, email is invalid!')
}
const hashedPassword = this.hasher.hash(password)
return await this.userRepository.insert({ username, email, hashedPassword })
}
The issue, however, is that I think this is not a true "imperative" shell as described by Gary and I would have to resort to mocking and stubbing to see if it behaves properly.
How would I go about making this more imperative?
Should I create a method validate()
in UserValidator
so it handles all the conditions?
Should the UserValidator
throw the error instead of the AddUser
service?
What about checking if user is already existent in the database?
Aucun commentaire:
Enregistrer un commentaire