Have fun learning about Test-Driven Development with JitterTed's TDD Game

Live Coding Journal - Mar 24, 2026

Reflections, Learnings, and Mistakes from live coding my JitterTicket Event Sourcing application


Notes from Today’s Live Coding Session

Exception vs. Result Pattern

Today I reviewed my decision (around 10min into the video) to use an Exception for when there’s a scheduling conflict for concerts (trying to schedule a concert on a date where one was already scheduled) by looking at what the code would for something like Vavr’s Try (“Result” pattern) vs. a normal try..catch. I think an Exception is just as readable and, since this isn’t in a tight processing loop, any performance implications of throwing an Exception are irrelevant since the result is an HTTP response (redirect). If we were validating hundreds or thousands of lines of input, then there might be some noticeable impact, but that’s not the case here.

External Validation: Scheduling Conflicts

Once I figured out where the scheduling validation check should go (sketching the call in the Commands.createScheduleCommand() method), I determined (around 24m) that the (cached) projection state (ScheduledConcerts record) has the knowledge to execute the conflict-check query. Having the cached projection state execute the query is fine if there aren’t thousands of concerts to check. For something like email uniqueness where there may be many thousands (or millions?) of entries, we would want to run a query against the database, though we could optimize using a Bloom filter if we wanted. However, even in a production system, there wouldn’t be a problem with this cache unless the system was handling tickets for many venues (vs. just one). A nice benefit of the conflict check inside a simple record is that TDDing the implementation was straightforward.

Value Objects: Smallest Consistency Boundary?

At 2:51:20 in the video I ponder the common code ensureNoConflictFor(showDateTime) between the validation for Schedule New Concert and Reschedule Existing Concert actions:

public CommandWithParams<ConcertId, RescheduleParams> createRescheduleCommand() {
    return commandExecutorFactory.wrapWithParams(
            (concert, reschedule) -> {
                LocalDateTime showDateTime = reschedule.showDateTime();
                ensureNoConflictFor(showDateTime);
                concert.rescheduleTo(
                        showDateTime,
                        reschedule.doorsTime());
            });
}

public CreateWithParams<ConcertId, ScheduleParams> createScheduleCommand() {
    return commandExecutorFactory.wrapForCreation(
            scheduleParams -> {
                LocalDateTime showDateTime = scheduleParams.showDateTime();
                LocalTime doorsTime = scheduleParams.doorsTime();
                ensureNoConflictFor(showDateTime);
                return Concert.schedule(
                        ConcertId.createRandom(),
                        scheduleParams.artist(),
                        scheduleParams.ticketPrice(),
                        showDateTime,
                        doorsTime,
                        scheduleParams.capacity(),
                        scheduleParams.maxTicketsPerPurchase());
            });
}

With Command-Consistency Boundaries replacing Aggregates, the question is: what is the smallest consistency boundary that makes sense? It seems like Value Objects are the minimum boundary, as anything smaller probably wouldn’t need to change simultaneously. For example, there’s no need for one user to make a change to a Concert’s “Doors Time” at the same time as another user making a change to the “Show Time”. Both of those are linked (there’s even validation for Doors Time that depends on the Show Time, as I implement later in the stream), so that’s the minimal consistency boundary. Similarly for an address, there’s no point in allowing changes to the City by one user and the State for another user. An Address is a Value Object and is a complete unit of consistency.

Next Steps

With the validation out of the way, the next step is to try Avro as a serialization format and see how performance compares with the current JSON implementation.

Join me on my next stream, which I usually do Monday through Thursday, starting at 19:00 UTC on Twitch: https://jitterted.stream.


Make Your Code More Testable™️

Don't miss out on ways to make your code more testable. Hear about new Refactoring techniques, improving the Test-Driven Development process, Hexagonal Architecture, and much more.

    We respect your privacy. Unsubscribe at any time.