I have a service that performs a series of actions based on user information (name, address, etc), one of the problems is that the User entity comes from a legacy third party API (as a repository) that did not enforce a strong validation on data, so some users may have invalid information, such as blank or incomplete name, address, etc and thus cannot proceed to complete the service's flow.
the API provider updated its API and provided an endpoint to reset specific user information, but I'm only able to know which users have invalid information when they try to use the service and an exception is thrown, so my approach to solve this problem was to add this "rollback logic" inside a catch block and send it to the API to be reset, here are some of my concerns:
1 - I'm not sure that this is an elegant solution, I'm handling errors and the "rollback logic" in a catch block, this is something I have never done before and, I honestly don't know if this is a use case for the catch block, is it okay? is there a better way to handle this?
2 - There can be multiple different exceptions for each field (InvalidNameException, InvalidAddressException, InvalidDogexception...), I have to find a way to group exceptions together in order to send the specific group of fields that need to be reset, otherwise I'm going to send multiple different requests, making the user unnecessarily repeat the service's flow several times.
for example:
A user has its name and address invalid, but the InvalidNameException was thrown first, so only the 'name' field is going to be sent to the API to be reset, but I need both (name and address)
I know that the use of exceptions is subjective to the context, and I believe that its use in this context is "correct-ish" because not having valid user information is an exceptional case, but when it comes to doing specific stuff based on specific exceptions I find it kind of hard to deal with exceptions, but I do not know a better way to handle errors...
here's an example of the current structure:
The service:
class DoSomethingService {
public constructor(
private userRepositoryInterface: UserRepositoryInterface,
private registrationManagerInterface: RegistrationManagerInterface
) { }
public execute(input: ServiceInput): void {
try {
const user = this.userRepositoryInterface.findById(input.userId);
//... bussiness actions
} catch (error) {
//only the first exception is going to be caught
// use strategy perhaps?
let fieldsToReset = [];
if (error instanceof InvalidAddressException) {
fieldsToReset.push('address');
} else if (error instanceof InvalidNameException) {
fieldsToReset.push('name')
}
this.registrationManagerInterface.resetUserInformation(input.userId, fieldsToReset)
}
}
}
The User entity and the Address value object with example validation:
class User {
public constructor(
private name: FullName,
private address: Address
) { }
}
class Address {
public constructor(private street: string) {
if (street.length <= 0) {
throw new InvalidAddressException('Invalid Street')
}
}
}