Back to Blog
Event-Driven Architecture: Building Loosely Coupled Systems
Why Events?
Event-driven architecture decouples components. Instead of direct calls, components publish events that others can react to. This creates flexibility—you can add new behaviors without modifying existing code.
Event Types
Domain Events
// Something that happened in the business domain
class OrderPlaced
{
public function __construct(
public readonly string $orderId,
public readonly string $customerId,
public readonly float $total
) {}
}
Integration Events
// Events for communication between bounded contexts
class OrderPlacedIntegrationEvent
{
public function __construct(
public readonly string $orderId,
public readonly array $items,
public readonly string $shippingAddress
) {}
}
Publishing Events
class OrderService
{
public function place(Order $order): void
{
$order->place();
$this->repository->save($order);
// Publish domain events
foreach ($order->pullDomainEvents() as $event) {
event($event);
}
}
}
Reacting to Events
class SendOrderConfirmation
{
public function handle(OrderPlaced $event): void
{
$order = Order::find($event->orderId);
Mail::send(new OrderConfirmationMail($order));
}
}
class ReserveInventory
{
public function handle(OrderPlaced $event): void
{
$order = Order::find($event->orderId);
$this->inventory->reserve($order->items);
}
}
class UpdateAnalytics
{
public function handle(OrderPlaced $event): void
{
Analytics::track('order_placed', [
'order_id' => $event->orderId,
'total' => $event->total,
]);
}
}
Event Sourcing
// Store events as the source of truth
class OrderEventStore
{
public function append(string $aggregateId, DomainEvent $event): void
{
StoredEvent::create([
'aggregate_id' => $aggregateId,
'event_type' => get_class($event),
'payload' => serialize($event),
'occurred_at' => now(),
]);
}
public function getEvents(string $aggregateId): Collection
{
return StoredEvent::where('aggregate_id', $aggregateId)
->orderBy('occurred_at')
->get()
->map(fn ($e) => unserialize($e->payload));
}
}
Message Brokers
// For distributed systems, use a message broker
class RabbitMQEventPublisher implements EventPublisher
{
public function publish(DomainEvent $event): void
{
$this->channel->basic_publish(
new AMQPMessage(json_encode($event)),
'events',
get_class($event)
);
}
}
Conclusion
Event-driven architecture enables loose coupling and flexibility. Start with simple in-process events, graduate to message brokers for distributed systems, and consider event sourcing for audit-critical domains.
Related Articles
Need Help With Your Project?
I respond to all inquiries within 24 hours. Let's discuss how I can help build your production-ready system.
Get In Touch