1

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.

Slepton
  • 51
  • 3
  • Validation is one of those things that can/should/must end up in multiple locations. See [this answer](https://softwareengineering.stackexchange.com/a/351662/115084). – John Wu Feb 10 '20 at 23:15

2 Answers2

1

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.

Bart van Ingen Schenau
  • 71,712
  • 20
  • 110
  • 179
0

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.

Slepton
  • 51
  • 3