Building a Robust Reservation System with Event-Driven Architecture
In this post, I’ll guide you through creating a scalable reservation system using event-driven architecture. We’ll explore domain modeling, event handling, and message processing - essential concepts for modern distributed systems.
The Reservation Process Flow
Let’s start by visualizing the reservation workflow with a Mermaid diagram:
This diagram shows the end-to-end process from receiving a request to returning a response, with key decision points and actions along the way.
Domain Model for Reservations
A well-designed domain model is crucial for representing our business entities and their relationships:
public class Reservation
{
public Guid ReservationId { get; set; }
public DateTime ReservationDate { get; set; }
public DateTime CreatedAt { get; set; }
public Customer Customer { get; set; }
public Resource Resource { get; set; }
public ReservationStatus Status { get; set; }
}
public class Customer
{
public Guid CustomerId { get; set; }
public string FirstName { get; set; }
public string LastName { get; set; }
public string Email { get; set; }
public string PhoneNumber { get; set; }
}
public class Resource
{
public Guid ResourceId { get; set; }
public string Name { get; set; }
public string Description { get; set; }
public ResourceType Type { get; set; }
}
public enum ReservationStatus
{
Pending,
Confirmed,
Cancelled
}
public enum ResourceType
{
Room,
Equipment,
Venue
}
This model captures the core entities: Reservations, Customers, and Resources, along with their properties and relationships.
Implementing Domain Events
For an event-driven architecture, we need to define events that represent significant system occurrences:
public interface IDomainEvent
{
DateTime OccurredOn { get; }
}
public class ReservationCreatedEvent : IDomainEvent
{
public Guid ReservationId { get; }
public DateTime ReservationDate { get; }
public Guid CustomerId { get; }
public Guid ResourceId { get; }
public DateTime OccurredOn { get; } = DateTime.UtcNow;
public ReservationCreatedEvent(Guid reservationId, DateTime reservationDate, Guid customerId, Guid resourceId)
{
ReservationId = reservationId;
ReservationDate = reservationDate;
CustomerId = customerId;
ResourceId = resourceId;
}
}
public class ReservationCancelledEvent : IDomainEvent
{
public Guid ReservationId { get; }
public Guid CustomerId { get; }
public DateTime OccurredOn { get; } = DateTime.UtcNow;
public ReservationCancelledEvent(Guid reservationId, Guid customerId)
{
ReservationId = reservationId;
CustomerId = customerId;
}
}
Enhancing the Domain Model with Event Publishing
Let’s modify our Reservation
class to publish domain events:
public class Reservation
{
public Guid ReservationId { get; private set; }
public DateTime ReservationDate { get; private set; }
public DateTime CreatedAt { get; private set; }
public Customer Customer { get; private set; }
public Resource Resource { get; private set; }
public ReservationStatus Status { get; private set; }
private readonly List<IDomainEvent> _events = new List<IDomainEvent>();
public IReadOnlyCollection<IDomainEvent> Events => _events.AsReadOnly();
public Reservation(Guid reservationId, DateTime reservationDate, Customer customer, Resource resource)
{
ReservationId = reservationId;
ReservationDate = reservationDate;
CreatedAt = DateTime.UtcNow;
Customer = customer;
Resource = resource;
Status = ReservationStatus.Pending;
_events.Add(new ReservationCreatedEvent(ReservationId, ReservationDate, Customer.CustomerId, Resource.ResourceId));
}
public void Cancel()
{
Status = ReservationStatus.Cancelled;
_events.Add(new ReservationCancelledEvent(ReservationId, Customer.CustomerId));
}
}
This implementation tracks domain events for later processing by event handlers.
Creating a Message Envelope Pattern
To properly transmit events across system boundaries, we’ll implement the Message Envelope pattern:
public class MessageEnvelope<T>
{
public Guid MessageId { get; private set; }
public DateTime OccurredOn { get; private set; }
public string MessageType { get; private set; }
public T Payload { get; private set; }
public Dictionary<string, string> Metadata { get; private set; }
public MessageEnvelope(T payload, string messageType)
{
MessageId = Guid.NewGuid();
OccurredOn = DateTime.UtcNow;
MessageType = messageType;
Payload = payload;
Metadata = new Dictionary<string, string>();
}
public void AddMetadata(string key, string value)
{
Metadata[key] = value;
}
}
Adding Serialization Support
For transmitting messages between services, we need serialization capabilities:
using System.Text.Json;
public class MessageEnvelope<T>
{
// Previous properties and constructor...
// Serialize to JSON
public string Serialize()
{
return JsonSerializer.Serialize(this);
}
// Deserialize from JSON
public static MessageEnvelope<T> Deserialize(string json)
{
return JsonSerializer.Deserialize<MessageEnvelope<T>>(json);
}
}
Usage Example
Here’s how you might use these components in practice:
// Create a reservation event
var reservationEvent = new ReservationCreatedEvent(
Guid.NewGuid(),
DateTime.UtcNow,
Guid.NewGuid(),
Guid.NewGuid()
);
// Wrap it in an envelope with metadata
var envelope = new MessageEnvelope<ReservationCreatedEvent>(
reservationEvent,
nameof(ReservationCreatedEvent)
);
envelope.AddMetadata("CorrelationId", "12345");
envelope.AddMetadata("Source", "ReservationSystem");
// Serialize for transmission
string json = envelope.Serialize();
// Later, deserialize
var deserializedEnvelope = MessageEnvelope<ReservationCreatedEvent>.Deserialize(json);
Benefits of This Approach
- Loose coupling - Services communicate through events rather than direct calls
- Scalability - Components can be scaled independently
- Resilience - System remains operational even if some services are temporarily unavailable
- Auditability - Events provide a complete history of system activities
- Flexibility - Easy to extend with new event types and handlers
In this post, we’ve covered the essential building blocks for a robust, event-driven reservation system. This architecture provides a solid foundation that can evolve to meet changing business requirements while maintaining system integrity.
Related Posts
For more details on specific aspects of this architecture, check out these related posts:
- Reservation System Component Diagram
- Reservation System Activity Diagram
- Reservation System Sequence Diagram