Synchronous requests: the problem
A user clicks "Send Invoice." Behind the scenes, the following happens: a PDF is generated, that PDF is stored, an email goes to the customer, the invoice is synced to the accounting system, and a webhook is sent to an external system. All of this in a single HTTP request. The user stares at a loading screen and hopes it works.
This is the problem with synchronous architecture. Every step in the chain must succeed before the next one starts, and the user waits until everything is done. Does the PDF generation take three seconds? The user waits three seconds. Is the accounting system's API slow? The user waits on top of that. Does that API time out? The user gets an error — even though the PDF was already generated and the email already sent.
Synchronous chains are fragile. A slow link slows the entire process. A failing link breaks the entire process. And worst of all: partial failures are difficult to recover from. The PDF was sent but the accounting sync failed — what is the status of that invoice now? Those are questions you do not want to answer in production.
With a handful of users, you barely notice this. But the moment your SaaS grows and dozens of users simultaneously send invoices, requests get stuck, workers become occupied, and your application slows down for everyone. Cascading failures: a problem in an external system propagates through your entire application.
Events and queues
The solution is conceptually simple: separate the registration of an action from its execution. Instead of cramming everything into an HTTP request, you record what needs to happen and process it in the background. Fire and forget.
The user clicks "Send Invoice." Your application saves the invoice, records that something needs to happen, and immediately returns a confirmation. The user sees the confirmation within milliseconds. In the background, the associated tasks are picked up: generate PDF, send email, sync accounting. Each independently, at their own pace, with their own error handling.
Laravel makes this remarkably accessible. The queue system supports multiple drivers — from database queues for simple setups to Amazon SQS for production environments on AWS. You define an event, attach listeners, and dispatch it. The framework handles the rest.
Events become first-class citizens in your architecture. They are no longer an afterthought, but the backbone of your application. Every important moment in your domain — an order placed, a user registered, a payment received — becomes an event that other parts of your system can pick up and process.
A well-designed system not only tells you what happened, but enables every component to respond to it independently.
When synchronous calls are perfectly fine
Not everything needs to be asynchronous. In fact, making simple operations unnecessarily asynchronous adds complexity without benefit. Be deliberate in your choice.
Standard CRUD operations — creating, updating, or deleting a record — are almost always fine as synchronous calls. The user expects immediate feedback, and the operation is fast enough to deliver it. The same goes for read operations: loading a page, fetching a list, showing a detail screen. That data needs to be available immediately; a queue does not belong there.
Validation and authorization are synchronous by definition. You need to know immediately whether a user has permissions, whether input is valid, whether a resource exists. An immediate answer is required.
The rule of thumb: if an operation takes less than 200 milliseconds, has predictable duration, and has no dependency on external systems, a synchronous call is the right choice. Do not add complexity you do not need.
When you need events
There are clear signals that an operation does not belong in a synchronous request:
- External API calls — Communication with payment providers, shipping platforms, email services, or accounting systems. You have no control over their response time or availability.
- Heavy computations — PDF generation, image processing, AI inference, reports over large datasets. Anything that takes more than a second does not belong in an HTTP request.
- Multi-step workflows — Processes with multiple steps where each step can succeed or fail independently. An order flow with payment, inventory reservation, shipping label, and confirmation email is a classic example.
- Tasks that can fail independently — If a failed email notification should not mean the entire invoice fails, those two operations must be decoupled.
- Operations that can wait — Updating search indexes, processing analytics, warming caches. The user does not need to wait for these.
The common denominator: whenever you are dealing with unpredictable duration, external dependencies, or tasks that must be able to succeed or fail independently, events and queues are the right choice.
Patterns in practice
Event-driven architecture is more than dispatching events and writing listeners. There are proven patterns that help you keep complex systems manageable.
Event sourcing
With event sourcing, you do not store the current state of an entity, but the sequence of events that led to that state. An order is not a row in a table with status "shipped." It is a sequence: order placed, payment received, packed, shipped. The current state is derived from those events.
The advantage: you have a complete audit trail, you can reconstruct the state at any point in time, and you can build new projections over historical data. The disadvantage: it is complex and not suited for every domain. Use it where the history of an entity is genuinely valuable — financial transactions, order processes, compliance-sensitive data.
The saga pattern
A saga coordinates a multi-step process where each step is its own transaction. Say a customer places an order. Step 1: reserve inventory. Step 2: process payment. Step 3: generate shipping label. If step 2 fails, step 1 must be rolled back (compensating action). The saga manages that flow and knows at every step what needs to happen if something goes wrong.
In Laravel, you often implement this with a job chain or a state machine. The key requirement is that every step is idempotent — safe to re-execute — and that a compensating action is defined for each step.
Error handling and retry strategies
Jobs fail. That is not the exception, that is the norm. A robust queue system is designed for this. Laravel provides out-of-the-box retry mechanisms: you configure how many times a job may be retried, with what delay (exponential backoff), and what should happen when all attempts are exhausted.
That last part is critical: the dead letter queue. Jobs that still fail after all retries are stored separately for manual inspection. You do not want those to silently disappear. An invoice that never reaches the accounting system is a problem you want to know about — not one you want to ignore.
Monitor your queues actively. Know how many tasks are waiting, what the average processing time is, and how many tasks are failing. Real-time insight into your background processes is essential for catching problems before they impact users.
Decoupling is not about technology. It is about the freedom to let every component of your system evolve, scale, and fail independently — without the rest falling over.
Conclusion
Event-driven architecture is not an all-or-nothing choice. It is a tool you deploy where it adds value. Simple CRUD stays synchronous. External integrations, heavy tasks, and multi-step processes move to the background.
The gains are threefold. Your users get a faster experience because they are not waiting on background processes. Your system becomes more resilient because a failing external service does not take down your entire application. And your architecture becomes more flexible because you can add new functionality by simply attaching a new listener to an existing event — without touching existing code.
Start small. Identify the synchronous calls that cause the most problems — the slow API connections, the heavy computations, the fragile chains — and move those to queues. Build from there. A well-designed event-driven architecture grows with your product, instead of getting in its way. Contact Coding Agency Meppel to discuss how event-driven architecture can improve your SaaS platform.