1

I’m working on a three layers architecture backend (Laravel).

Here’s the context:

  • I have a PasswordService responsible for updating a user’s password.
  • and a Otpservice responsible for verifying/creating OTPs.
  • The PasswordService depends on a UserRepository.
  • and the OtpService depends on a OtpRepository.
  • Normally, updating a password requires the user’s ID, not their email.
  • However, in the “forgot password” flow, I only have the user’s email (sent by the frontend).

Now, I have multiple design options and I’m not sure which is the cleanest in terms of separation of concerns and reusability:

  • Expose another method in PasswordService → updatePasswordByEmail(string $email, string $newPassword) (Internally finds the user, gets its ID, and updates the password)
  • Expose getUserByEmail() in UserRepository → So PasswordService calls it, gets the user’s ID, and then calls its regular updatePasswordById() method.
  • Have OtpService return the user ID after verifying the OTP, even though in some other contexts (like OTP verification for login), I don’t need that ID.

I’d like to know which approach is better architecturally, considering:

  • I’m trying to keep services focused on their own domain concerns.

  • I don’t want to create leaky abstractions (e.g., OTP service leaking user info).

  • I want to keep consistency and testability.

So my question is:

Which approach is cleaner and more in line with good design principles in a three layers architecture for handling a “forgot password” flow?

2
  • I assume you use OTPs for password reset flow? You send it to user by email or something? If so, then all you need to do is create a connection between the OTP and email, and add "getUserByEmail" to UserRepository. That's the cleanest solution IMO. I don't see any reason to couple OtpService with user data. Commented Nov 5 at 20:25
  • What does the password reset flow look like from a users perspective? Once the OtpService has validated the OTP, how does the system know what to do next? What is the essential difference between your first two options? Commented Nov 6 at 8:21

1 Answer 1

1

IMHO, User entity is the main responsible of knowing anything related to the User, not other services.

Let's take a look about your different approaches:

About First Method

Expose another method in PasswordServiceupdatePasswordByEmail(string $email, string $newPassword): For me this approach is fine. I assume you didn’t include authentication details to keep the example simple.

About Second Method

Expose getUserByEmail() in UserRepository → So PasswordService calls it, gets the user’s ID, and then calls its regular updatePasswordById() method: If you are going to do a simple monolith application that's correct, you can implement the method in the user repository and then call it in other services. However, if your intention is to be strict (I guess your intention is) services should always communicate with services and no other repositories. Repositories are responsible of handling the persistence of the entity in a deep level, if they got separated on an independent service the repository is going to be almost private from the outside, in good practice and taking a little bit of the Service-Oriented Architecture, services communicate with other services.

In that way, even if the implementation is super simple you are going to have something like:

PasswordServiceUserServiceUser Repository

PasswordServiceUserServiceUser Repository

About Third Method

Have OtpService return the user ID after verifying the OTP, even though in some other contexts (like OTP verification for login), I don’t need that ID: Indeed, this maybe is not needed in the OTP service and returning the ID of the user here is kind of unnecessary,

Note: Seems like you got multiple separated services, which is kind of good for SRP, just take care about splitting too much. I understand why you did it, if the authentication service grew too much then is not a bad practice to split it in many services.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.