JavaScript/React

"> JavaScript/React

"> Skip to content
Breaking
Latest technical intelligence from Northeast India • Infrastructure, AI, Cloud & Security Analysis • Precision Analysis | Raw Intelligence | Your North Star of Tech Latest technical intelligence from Northeast India • Infrastructure, AI, Cloud & Security Analysis • Precision Analysis | Raw Intelligence | Your North Star of Tech
WEBDEV

Analysis: Why I Divorced Laravel Observers

Why I Abandoned Laravel Observers: A Journey Towards Predictability

Why I Abandoned Laravel Observers: A Journey Towards Predictability

In the realm of PHP frameworks, Laravel has gained a significant following, including developers in North East India. One of its powerful features is the use of Observers for managing events. However, as I delved deeper into complex business domains, I realized that Observers, while seemingly convenient, were hindering the stability of my code. This article shares my experiences and the lessons I learned in my journey away from Observers and towards explicit code.

The Illusion of Clean Code

The allure of Observers lies in their ability to keep controllers clean and logic tidy. For instance, a simple store method may appear pristine:

 public function store(UserData $userData) { // Look how clean! No clutter! $user = User::create($userData->toArray()); return response()->json($user); } 

However, this apparent cleanliness masks underlying issues. The single User::create() line triggers a chain reaction, executing tasks like sending a welcome email, creating a Stripe customer ID, logging an activity, and notifying a Slack channel. This is what I call "Spooky Action at a Distance." You change the database in one place, and code executes somewhere you didn't even know existed.

The Execution Order Trap

Another significant problem with Observers is race conditions, especially when dealing with relationship-dependent logic. For example, if you use User::create(), the created event may fire before you've had a chance to attach related models. This leads to fragile code and the need for patches to work around the issue.

The Solution: Explicit over Implicit

Just as I replaced Form Requests with Data Transfer Objects (DTOs) to make data explicit, I replaced Observers with explicit Services (or Actions). While this means writing a few more lines of code, that code tells a story, making it easier for future developers to read, understand, and debug.

A Refactored Creation Flow

Here's an example of the refactored creation flow, demonstrating the explicit nature of the code:

 class CreateUserService { public function handle(UserData $data): User { return DB::transaction(function() use ($data) { // 1. Create the user $user = User::create($data->toArray()); // 2. Handle side effects explicitly $this->billingService->createCustomer($user); // 3. Send notifications $user->notify(new WelcomeNotification()); return $user; }); } } 

When Are Observers Acceptable?

This doesn't mean Observers are useless. They are still useful for handling purely technical concerns that should always happen, regardless of context, and that don't impact business rules. Examples include generating a UUID for a model, clearing cache keys, and updating search indexes.

The Conclusion: Maturity is Predictability

As developers, we initially appreciate tools that do things for us. However, as we grow in experience and responsibility, we value tools that allow us to express exactly what we want to happen. Abandoning Observers was initially painful, but the explicit code I now write serves as living documentation, making my codebase more predictable and maintainable.

If you're tired of side effects breaking your application, it's time to stop depending on the created event. Make your code explicit.

In the context of North East India, this shift towards explicit code can help foster a culture of maintainable, scalable, and predictable codebases in our growing Laravel community.