Live Coding Journal - Feb 9, 2026
Reflections, Learnings, and Mistakes from live coding my JitterTicket Event Sourcing application
Notes from Today’s Live Coding Session
Aggregates, Deciders, and Projections
I continued to refine my mental model around Aggregates as a bundle of related Deciders that use an internal Projection to use as the input state to the domain logic when executing a Command. For example, when the Concert aggregate is loaded, it replays all events having to do with that specific aggregate (scheduled, rescheduled, tickets sold, ticket sales stopped), even if the Command being executed (say, rescheduling) only needs part of the Concert’s state, e.g., just the Show Date & Time. We could break down the Aggregate into Deciders, which have the minimal state needed to make the decision, which therefore means playing back (processing) fewer events. As a long time DDD and OOP developer, I’m not sure I’d like having to handle Deciders as more granular objects, but I’ll experiment with it at some point to explore how it feels.
Processor vs. Aggregate (Decider)
I’m still trying to figure out the heuristic for when to use a Processor to execute a Command (and therefore generate an Event) vs. using the Aggregate to handle the decision-making and generate the Event. It feels similar to Domain Services vs. Aggregates in that Domain Services are used when state from multiple Aggregates is needed to make a decision, or the state change affects multiple Aggregates. It’s still unclear to me how I would implement the functionality of refunding tickets to all customers that purchased tickets to a Concert that was then canceled. It could certainly be done with the Use Case (or Command Object, see below), but I could also see it being done with a Processor, especially since it doesn’t have to be done synchronously with the cancellation process.
Two Types of Projections?
After thinking about Processors vs. Aggregates (around the 12-minute mark in the video), it really seems like there are two types of Projections: those that are used as “read (only) models”, e.g., for reporting or display on a UI, and those that are used as input state for automated decision-making, i.e., for Processors. It also led me to think about Processors that make decisions solely on the input Event, which turns out to be what some folks call “Translators”. I think it’s really useful to separate those two, as Processors need the Projections as “accumulated state”, whereas Translators are truly functional and require no state.
Use Case vs. Command Object
I’ve been implementing the Application-level Use Cases as stateless service objects, but when I started thinking about the implementation for the next use case (around the 2h56m mark in the video), stopping ticket sales, I realized that the structure of that code follows the same pattern:
- Load the
ConcertAggregate from the Concert Event Store. - Invoke the Command method on the
Concert - Have the Concert Event Store persist the generated Events (if any).
Creating a Command class that follows the Composite pattern where it takes the event store and the Concert ID would be very handy. I also decided that a Command Factory configured with the Event Store would make it easier to create Commands for a specific Concert, as I wouldn’t have to pass the Event Store each time I want a Command. It also means fewer generics to deal with for the Command itself.
ConcertStartedProcessor
I made good progress on the ConcertStartedProcessor, test-driving the implementation step-by-step, handling a new event each time around the TDD loop. I also implemented ignoring Concerts that had a Show Date & Time in the past, as there’s no reason to track them as they’ve already happened. The refactoring along the way was interesting, because I went from an ugly if block to a switch statement, then to a switch expression, but then back to a switch statement as I fleshed out the events that it handles. At the end of the refactoring, I noticed that I had two methods:
private void cancelAlarm(ConcertId concertId) {
alarmMap.remove(concertId);
}
private void scheduleAlarm(ConcertId concertId, LocalDateTime showDateTime) {
if (showDateTime.isAfter(LocalDateTime.now())) {
alarmMap.put(concertId, showDateTime);
}
}
This triggered my mental “Primitive Obsession” heuristic, because both methods (which are command methods) take the same parameter, ConcertId. Any time I see a pattern like that, I consider creating a wrapper object that encapsulates the parameter and provides those command methods on the new object. Something like ConcertAlarm.scheduled(LocalDateTime showDateTime) and ConcertAlarm.cancel() are what I’d want instead.
I didn’t do that refactoring, because I already had in mind a ConcertAlarm class, but I’m not yet sure what else it would contain, so I’ll delay the refactoring until I know more.
Precursor to Primitive Obsession
When multiple methods take the same parameter, that’s a sign that there you’re missing a wrapper object that encapsulates the parameter and provides those methods.
I talk about this in-depth in my online presentation “Stop Obsessing About Primitives”
Next Steps
In my next live coding stream, I’ll dig into the actual scheduling of the alarms, using Java’s ScheduledExecutorService. I still need to figure out the right “seam” for separating the I/O (which are the ScheduledFutures) from the processor logic.
Join me on my streams, which I usually do Monday through Thursday, starting at 20:00 UTC on Twitch: https://jitterted.stream.