In the first part of our journey, we became familiar with CQRS and Event Sourcing. In this episode, we’re going to implement a few classes in our brand new system. Before we start let’s discuss a little bit about a business problem that we’ll try to model. At first, I thought about something very easy like a bookstore which would allow users only to buy some books. But that would be boring, wouldn’t be? By chance, a few weeks ago I received the kind of interesting recruitment objective from some company. Implementation of… Google Calendar. Sounds good? Well, I hope so!

Project assumptions

First, we need to specify some requirements that our application should meet. However, we will focus only on the main functionalities such as:

Creating a single event

Creating event cycles (daily/weekly/monthly/yearly)

Editing single event

Editing event in cycles (all events/single event/future events)

In our case, word „event” is quite unfortunate since we will introduce some system events and ES in the future. Therefore we will use another name for that object – Calendar Item.

Read Database

Let’s start with modeling entities, which will be included in our database for reading. Thanks to that we will be able more easily to implement domain objects. For meeting first two requirements such a model should be enough for us:

To understand this model better, consider a short example. We would like to add a calendar item called „gym” which will regularly be repeated every Wednesday and Friday at 7 PM. Let’s say we create that on July 24 (Sunday). Our data would look like this:

I think that the data in the CalendarItems table does not require any explanation. But what have we just inserted into CalendarItemCycles? There are two rows. Each of them represents one day of the week. This means that each CalendarItem can assign up to seven cycles. The first row is a Wednesday cycle, which begins July 27, and the second is Friday cycle (starting July 29). Both cycles don’t have a defined EndDate (NULL value) suggesting that CalendarItem should always appear on our calendar. Type informs about the frequency of the cycle (2 means weekly), and Interval column determines the length of one cycle. In our case, a value of 1 tells us that the cycle is renewed every week. In both tables, the IsDeleted flag is used for soft deleting.

Domain objects

Knowing the read database model, we can finally implement our Domain Objects which look like this:

public class CalendarItem : AggregateRoot { public string UserId { get; set; } public string Name { get; set; } public string Description { get; set; } public DateTime StartDate { get; set; } public DateTime EndDate { get; set; } List<CalendarItemCycle> Cycles { get; } = new List<CalendarItemCycle>(); public CalendarItem(Guid id, string userId, string name, string description, DateTime startDate, DateTime endDate, IEnumerable<Contracts.Commands.CalendarItemCycle> cycles) { // implementation in next parts } public CalendarItem() { } } class CalendarItemCycle { public DateTime StartDate { get; set; } public DateTime? EndDate { get; set; } public int Interval { get; set; } public CalendarItemCycleType Type { get; set; } public CalendarItemCycle(DateTime startDate, DateTime? endDate, int interval, CalendarItemCycleType type) { StartDate = startDate; EndDate = endDate; Interval = interval; Type = type; } }

Of course, our domain objects don’t have any business logic yet. Notice that CalendarItem inherits from a class called AggreagteRoot. I described this pattern in the previous part, so if you’re not familiar with that just stop here for a moment and come back to read when you’ll be ready. AggreagteRoot will have a few liabilities in our code (but we won’t break SRP):

defines the unique domain object in our application (that’s why CalendarItemCycle is not also AggregateRoot)

stores events generated within the Aggregate

restricts access to domain objects other than root

Implementation of AggragateRoot looks as below:

public abstract class AggregateRoot : IAggregateRoot { public Guid Id { get; set; } public int Version { get; set; } protected List<IEvent> Events { get; set; } = new List<IEvent>(); public List<IEvent> GetUncommittedEvents() => Events; public void MarkEventsAsCommitted() => Events.Clear(); public virtual void LoadFromHistory(IEnumerable<IEvent> events) { foreach (var @event in events) ApplyEvent(@event, false); } protected void ApplyEvent(IEvent @event, bool isNew = true) { //implementation in next parts } }

The class contains three properties:

Id – identifies our unique Aggregate in the domain

Version – we’ll use that for checking any inconsistencies in the Event Store

Events – keeps events generated within the Aggregate

Apart from that, we can notice the LoadFromHistory method which responsibility is to reconstruct the state of domain objects based on events received from Event Store. We will come back here to implement the ApplyEvent method in one of next parts.

Conclusion

Knowing our domain objects, we can go further to implement other parts of the CQRS. In the next part, we’ll look more closely at the role of commands and command handlers. If you don’t want to miss all of that, I encourage you to follow me on Twitter or leave a like on my Facebook fan page