# Data-Driven vs. Event-Driven Architecture: Explained with Example

I recently came across an article on [daily.dev](http://daily.dev) about Data-Driven vs. Event-Driven Architecture. As a software developer, I wanted to understand the concepts, but they weren't exactly easy to grasp at first.

So, I did some research and learned them my own way. Since **writing helps me understand things better**, I decided to turn my notes into an article instead of letting them disappear into another forgotten notebook.

I'm writing this for my future self and for fellow developers who are just starting or looking to improve their software architecture skills.

Hope this makes things a little easier to understand!

## A Simple Story: Data-Driven vs Event-Driven

Imagine you own a restaurant.

1.  **Data-Driven Approach**: You hire a waiter named Rahim. Every 5 minutes, Rahim walks into the kitchen and checks the order list. He asks: "Are there any new orders?" If there are new orders, he takes them to the chef. If there are no new orders, he goes back and checks again after 5 minutes. Rahim makes decisions by checking the current data (the order list).
    
2.  **Event-Driven Approach**: Now you hire another waiter named Karim. Karim doesn't repeatedly check the order list. Instead, whenever a customer places an order, a bell rings. Ding! Karim hears the bell and immediately takes the order to the chef. Karim takes action because something happened (an event).
    

| Data-Driven | Event-Driven |
| --- | --- |
| Rahim checks the order list | Karim waits for the bell |
| Depends on current data | Reacts to an occurrence |
| "Is there a new order?" | "A new order has arrived!" |
| In this example, checks every 5 minutes | Reacts when notified |

**Example application**: An e-commerce platform developed using Laravel, Next.js, MySQL, and Redis.

**A customer places an order. The system needs to:**

*   Save the order in MySQL (Database).
    
*   Reduce product stock.
    
*   Send an order confirmation email.
    
*   Notify the admin.
    
*   Update the Next.js dashboard. We'll implement this first using a [database-polling](https://medium.com/@newan0805/a-deep-dive-into-data-polling-what-it-is-and-when-to-use-it-8d265c22d99b) workflow, then using Laravel Events and Listeners.
    

## Data-Driven Architecture

*Example A*

### Database-driven order processing

```plaintext
Next.js
   |
   | POST /api/orders
   v
Laravel Controller
   |
   v
MySQL Database
   |
   | Save Order
   v
Laravel Scheduler
   |
   | Check pending orders
   v
Process Orders
   |
   v
Send Email + Notify Admin
```

**Step 2: Laravel saves the order**

OrderController.php

```php
public function store(Request $request)
{
    $order = Order::create([
        'user_id' => $request->user()->id,
        'product_id' => $request->product_id,
        'quantity' => $request->quantity,
        'status' => 'pending',
    ]);

    return response()->json([
        'success' => true,
        'order' => $order,
    ]);
}
```

| id | user\_id | product\_id | quantity | status |
| --- | --- | --- | --- | --- |
| 101 | 5 | 10 | 2 | pending |
| 102 | 8 | 15 | 1 | pending |
| 103 | 9 | 20 | 3 | completed |

**Step 3: Laravel periodically checks the database**

Create a scheduled command:

```shell
php artisan make:command ProcessPendingOrders
```

Inside the command:

```php
public function handle()
{
    Order::where('status', 'pending')
        ->chunkById(100, function ($orders) {

            foreach ($orders as $order) {

                // Process business logic
                $this->processOrder($order);

                $order->update([
                    'status' => 'completed'
                ]);
            }
        });
}
```

Schedule it:

```php
use Illuminate\Support\Facades\Schedule;

Schedule::command('orders:process')
    ->everyMinute();
```

Laravel checks the database every minute to find orders requiring processing.

**The key concept**: The worker discovers what needs to happen by examining stored data.

A production implementation also needs atomic job claiming, idempotency, and retry handling to avoid processing an order twice when multiple workers are running.

## Event-Driven Architecture

Now let's implement the same application using Laravel Events and Listeners.

*Example B*

### Event-driven order processing

```plaintext
Next.js
   |
   | POST /api/orders
   v
Laravel Controller
   |
   | Save Order
   v
MySQL Database
   |
   | Dispatch Event
   v
OrderPlaced Event
   |
   +-------------------+
   |         |         |
   v         v         v
Reduce    Send      Notify
Stock     Email     Admin
```

**Step 1: Create a Laravel Event**

```shell
php artisan make:event OrderPlaced
```

<mark class="bg-yellow-200 dark:bg-yellow-500/30">app/Events/OrderPlaced.php</mark>

```php
namespace App\Events;

use App\Models\Order;
use Illuminate\Foundation\Events\Dispatchable;
use Illuminate\Queue\SerializesModels;

class OrderPlaced
{
    use Dispatchable, SerializesModels;

    public function __construct(
        public Order $order
    ) {}
}
```

This event represents a fact:

> An order has been placed.

The event itself does not need to know which services will respond to it.

## Step 2: Create listeners

```shell
php artisan make:listener ReduceStock
php artisan make:listener SendOrderEmail
php artisan make:listener NotifyAdmin
```

**Listener 1: Reduce stock**

<mark class="bg-yellow-200 dark:bg-yellow-500/30">app/Listeners/ReduceStock.php</mark>

```php
namespace App\Listeners;

use App\Events\OrderPlaced;

class ReduceStock
{
    public function handle(
        OrderPlaced $event
    ): void {

        $order = $event->order;

        $order->product()
            ->decrement(
                'stock',
                $order->quantity
            );
    }
}
```

For this simplified example, stock is reduced by a listener. In a real checkout system, inventory reservation is often a critical transactional operation and should not be treated like a best-effort email notification.

**Listener 2: Send email**

```php
namespace App\Listeners;

use App\Events\OrderPlaced;
use App\Mail\OrderConfirmation;
use Illuminate\Contracts\Queue\ShouldQueue;
use Illuminate\Support\Facades\Mail;

class SendOrderEmail implements ShouldQueue
{
    public function handle(
        OrderPlaced $event
    ): void {

        Mail::to(
            $event->order->user->email
        )->send(
            new OrderConfirmation(
                $event->order
            )
        );
    }
}
```

Notice:

```php
implements ShouldQueue
```

This tells Laravel to process the listener through its queue system rather than execute it synchronously in the request.

**Listener 3: Notify admin**

```php
namespace App\Listeners;

use App\Events\OrderPlaced;
use App\Notifications\NewOrderNotification;
use App\Models\User;

class NotifyAdmin
{
    public function handle(
        OrderPlaced $event
    ): void {

        User::where('role', 'admin')
            ->get()
            ->each(function ($admin) use ($event) {

                $admin->notify(
                    new NewOrderNotification(
                        $event->order
                    )
                );

            });
    }
}
```

**Step 3: Dispatch the event from Laravel**

OrderController.php

```php
use App\Events\OrderPlaced;
use App\Models\Order;
use Illuminate\Http\Request;

public function store(Request $request)
{
    $order = Order::create([
        'user_id' => $request->user()->id,
        'product_id' => $request->product_id,
        'quantity' => $request->quantity,
        'status' => 'pending',
    ]);

    OrderPlaced::dispatch($order);

    return response()->json([
        'success' => true,
        'order' => $order,
    ]);
}
```

Now, when a customer places an order:

```plaintext
Order::create()
      |
      v
OrderPlaced::dispatch()
      |
      v
Laravel Event Dispatcher
      |
      +---- ReduceStock
      |
      +---- SendOrderEmail
      |
      +---- NotifyAdmin
```

You no longer need a scheduled database query to discover every newly created order.

Important: Laravel listeners are synchronous by default. Only queued listeners run asynchronously. Dispatching an event does not automatically make your application asynchronous.

For queued listeners associated with database transactions, configure after-commit dispatching so workers do not process an order before its transaction is committed.

## End of the Day

Since the AI revolution, I've honestly become a little too comfortable jumping straight into coding without properly planning the architecture first. Not exactly a habit I'm proud of as a software developer. 😅

So, when this architecture-related article caught my eye, I couldn't just scroll past it. I decided to actually learn something instead of asking AI to build another project for me.

And that's how this article happened!

If you've made it this far, I'd love to hear your story. How has AI changed the way you approach software development? Drop your thoughts in the comments. Let's learn from each other!
