I read some answers here on stack exchange, but none really "hits the nail on the head" about where to throw exceptions, where I shouldn't and where to catch them.
the idea that an exception should be thrown in exceptional cases is very vague, for example, if I can't find a user in my data source (repository) I can't continue the flow of my code, can this be considered exceptional?
I also understand that there are caveats about flow control with exceptions, for me it does not seem right to use try/catch within the service, to catch all the exceptions that a repository/adapter/whatever may throw to control what to do next.
based on this, a code example with a "service" and a controller calling the service and catching all of the service exceptions:
Service:
class CheckUserUltraSecretInformation
{
public function __construct(
private CachedUserDataRepository $cachedDataRepository,
)
{
}
public function handle(string $document)
{
$cachedUserData = $this->cachedDataRepository->findByDocument($document);
if (empty($cachedUserData)) {
throw new CachedUserDataNotFoundException('User Cached information not found, cannot proceed');
}
if ($cachedUserData->hasExpired()) {
throw new CachedUserDataHasExpiredException('Cached user data is expired');
}
// return ultra secret information
}
}
Controller:
class UserSecretInformationController
{
public function __construct(
private CheckUserUltraSecretInformation $checkUserUltraSecretInformation)
{
}
public function checkUserInformation(Request $request)
{
$document = $request->input('document');
try {
$userUltraSecretInformation = $this->checkUserUltraSecretInformation->handle($document);
} catch (CachedUserDataNotFoundException $exception) {
// return json response with http code 400
} catch (CachedUserDataExpiredException $exception) {
//returns json response with http code 500
}
}
}
Is it "correct" that my repository does not throw exceptions, and only returns
null/false
when it cannot return an entity? if not, what is the problem?if my repository throws exceptions, should my service capture them and re-launch them as service exceptions? is this considered flow control?
Are the outer layers the "most correct" to capture and handle exceptions? in my example the controller is used, can It know the name of the services/use-cases exceptions?