0

We are adopting a modular monolith approach to our application development and everything is fine thus far. However, when implementing a custom exception handler for each of module this becomes quite tricky when attempting to register/inject this handler into Laravel similar to the way you would with the service providers.

File structure:

- Laravel
  - app
    - Exceptions
      - Handler.php
  - src
    - module 1
      - Http
        - Client.php
      - Exceptions
        - Handler.php
        - ExceptionOne.php
      - Tests
        - TestOne.php
    - module 2

My understanding is because Laravel's error handler is a singleton this is going to be quite tricky...not possible.

I have tried extending Laravel's handler and then call the register method in my module's service provider but this didn't work.

There were some other methods not worth mentioning that didn't work either.

Ideal solution would be:

class Handler 
{
    protected $registeredHandlers = [
        \App\Exceptions::class,
        \Modular\Monolith\Exceptions\Handler::class
    ];

    public function register(): void
    {
        foreach(this->registeredHandlers as $handler) {
            $handler->register();
        }
    }
}

Something along these lines where we can have multiple handlers where we can register the exceptions, this would still allow the singleton to be there but also, allows for separation of the exceptions from the main apps handler.

1
  • Which laravel major version are you using? The syntax can be a bit different if you're using 12. Commented Jun 19 at 14:30

1 Answer 1

0

Perhaps you're looking at it the wrong way. Just like a modular monolith uses a singular (singleton) application under the hood, instead of implementing a custom Exception Handler for each module you should try to register exception handling to the Exception Handler in each module

With Laravel 12, I think this should work.

namespace Module1\Providers;

use Illuminate\Foundation\Configuration\Exceptions;
use Illuminate\Foundation\Exceptions\Handler;

// Probably use a better name
class ExceptionHandlingServiceProvider extends ServiceProvider
{
    public function boot()
    {
        $handler = $this->app->afterResolving(
            abstract: Handler::class,
            callback: fn ($handler) => $this->registerCallbacks(new Exceptions($handler)),
        );
    }

    protected function registerCallbacks(Exceptions $exceptions): void
    {
        $exceptions->report(function (Module1Exception $ex) {
            // what should be done in the backend when this exception is not caught?
        });

        $exceptions->render(function (Module1Exception $ex) {
            // what should be returned when this exception is not caught?
        });

        ...
    }
}

Make sure the application has that service provider in the array located in /bootstrap/providers.php.

This is pretty much what happens under the hood when the withExceptions is used in /bootstrap/app.php. The only difference is I don't register the Handler singleton since it should already be registered. You can check the implementation details in the api documentation.

public function withExceptions(?callable $using = null)
{
    $this->app->singleton(
        \Illuminate\Contracts\Debug\ExceptionHandler::class,
        \Illuminate\Foundation\Exceptions\Handler::class
    );

    $using ??= fn () => true;

    $this->app->afterResolving(
        \Illuminate\Foundation\Exceptions\Handler::class,
        fn ($handler) => $using(new Exceptions($handler)),
    );

    return $this;
}
Sign up to request clarification or add additional context in comments.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.