When using data and domain models, where does validation take place? Both or just
For example:
class UsersDB():
def create(self, user_data):
# Create user here
return insert_status
def confirm_user(self, token):
if token_date < today_start:
# Token expired, return False
return False
if not self.collection.exists({'token':token}):
# Token doesn't exist
return False
# Confirm user account
self.collection.update_one({}, {})
class UserService():
def __init__():
self.users_db = UsersDb()
def create(self, user_data):
if self.users_db.exists(user_data['email']):
self._set_error(status=409, error='This user already exists.')
return False
if self.users_db.create(user_data):
EmailService().send_registration_email(user_data)
AuditService().add_event('registration', ....)
else:
self._set_error(status=500, message='Unable to create user.'
return False
Now, in the UsersDB.confirm_user(), if this method fails, it's impossible to determine why the method call fails, be it due to the non-existant token or the expired token. However, this is validation that I don't necessarily want mixed in with the business logic. I'd prefer to keep validation within the UsersDB wrapper.
Similarly, if I wanted to do some more advanced validation on the user_data parameter passed through to UserService.create(), then I'd need to do it at the UserService level in order to get any meaningful feedback to the user.
Should I just separate validation out of the UsersDB object and do the validation within the UserService methods before passing over to the UsersDB object, or is there a problem with the design that needs addressing?
Ideally, I'd like to keep the validation out of UserService so that it only handles calls to other services, but I'm not sure if that's a good idea.
-
Validation is one of those things that can/should/must end up in multiple locations. See this answer.John Wu– John Wu2020年02月10日 23:15:10 +00:00Commented Feb 10, 2020 at 23:15
2 Answers 2
If you want to keep the validation logic out of UserService, but keep the reporting of errors to the user contained within that class, then you should look at other ways to internally communicate validation failures, rather than a boolean return value.
Either the UserDb methods should return an error code, or they should throw an exception for validation failures. The UserService class can then convert those internal indications into error responses for the user.
-
I did consider having a error attribute that is set on the object, and then if the method returns False, then pull the error string from the object. Thoughts?Slepton– Slepton2020年02月10日 19:06:33 +00:00Commented Feb 10, 2020 at 19:06
-
@Slepton, that is a possibility.Bart van Ingen Schenau– Bart van Ingen Schenau2020年02月10日 19:09:58 +00:00Commented Feb 10, 2020 at 19:09
I've come to the conclusion through both Bart van Ingen Schenau's answer and also this answer suggested in a comment by John Wu, that ideally there should be validation in all layers of the platform. My plan is to have an error attribute on UsersDB() that is set when a method() returns False. The service object can then access the error to get the reason.
Explore related questions
See similar questions with these tags.