I’m working on a three layers architecture backend (Laravel).
Here’s the context:
- I have a
PasswordServiceresponsible for updating a user’s password. - and a
Otpserviceresponsible for verifying/creating OTPs. - The
PasswordServicedepends on aUserRepository. - and the
OtpServicedepends on aOtpRepository. - 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?