Live Coding Journal - Feb 12, 2026
Reflections, Learnings, and Mistakes from live coding my JitterTicket Event Sourcing application
Notes from Today’s Live Coding Session
Don’t Use an SDK When a POST Will Do
I spent far too long trying to replace the email service in my Ensembler tool from SendGrid (who started charging even for minimal usage) to Brevo.
It was a terrible experience, with inconsistent documentation, a vendor-supplied Java SDK with many problems, a third-party SDK that was overkill, and a support chat LLM that provided completely wrong information.
I really should have looked closer at what I needed, because a single HTTP POST would have been sufficient.
However, I’m extremely displeased at Brevo’s use of LLM for support chat, since it is well known that they provide wrong answers.
It’s likely I’ll try out Postmark (using straight HTTP requests) unless Brevo decides to kick the LLM chatbot off the site—I refuse to support companies that waste my time in that way.
Generics and Lambdas Break My Brain
The main task for the rest of the stream was to expand the use of lambda-based Command Objects for executing command methods against the Concert aggregate.
I had a nice wrap method that returned a “unit of work” composed object:
copypublic Command<ConcertId> wrap(Command<Concert> concertCommand) { return concertId -> { List<ConcertEvent> concertEvents = concertEventStore.eventsForAggregate(concertId); Concert concert = Concert.reconstitute(concertEvents); concertCommand.execute(concert); concertEventStore.save(concert); }; }
However, this was only for methods (like .stopTicketSales()) that take no parameters.
I wanted to implement the Reschedule Concert command similarly, but it takes two parameters, and it took a surprising amount of time for me to get that working.
Around 1:47:42 is when the struggle began and, after a number of false starts, @Suigi comes through (again) and reminds me to revert the lambda I was trying to write into an anonymous inner class.
That made everything so much more clear, and was able to get where I wanted:
copypublic CommandWithParams<ConcertId, Reschedule> wrapWithParams(CommandWithParams<Concert, Reschedule> command) { return (concertId, reschedule) -> { List<ConcertEvent> concertEvents = concertEventStore.eventsForAggregate(concertId); Concert concert = Concert.reconstitute(concertEvents); command.execute(concert, reschedule); concertEventStore.save(concert); }; }
I wasn’t ready to ship it yet, because (around 2:28:40) as I was writing a test for the code (a rare code-first episode) I started to realize something was wrong with the unit work of work code wrapping the command, i.e., reconstituting the Concert by loading the events and calling the .reconstitute() static method: if the Concert didn’t exist, we’d try to reconstitute it from an empty list of events!
Oops, that won’t work, and, in fact, that’s an important missing validation in the reconstitute() method! I added a quick validation for that:
copypublic static Concert reconstitute(List<ConcertEvent> concertEvents) { if (concertEvents.isEmpty()) { throw new IllegalArgumentException("Can not reconstitute from an empty list of ConcertEvents."); } return new Concert(concertEvents); }
I then updated the code to properly call the event store’s findById() method instead:
copypublic CommandWithParams<ConcertId, Reschedule> wrapWithParams(CommandWithParams<Concert, Reschedule> command) { return (concertId, reschedule) -> { Concert concert = concertEventStore .findById(concertId) .orElseThrow(() -> new IllegalArgumentException("Could not find Concert with ID " + concertId)); command.execute(concert, reschedule); concertEventStore.save(concert); }; }
Once again, thinking through things to predict how the test would fail revealed a problem even before I had a failing test, but then the test still failed in a different way, which revealed a problem with the test.
One thing I hadn’t realized until I was writing these notes is that I need to go back and fix the no-arg
wrap()to also usefindById()!
Event Modeling for Reschedule Concert
Around 3:00:30, I do a quick event model for rescheduling concerts, and then decide not to build a completely new UI for it. The list of concerts to display for scheduling is the same as the list of concerts for purchasing, so I decided to just add another button for “reschedule”. Then I did the usual test-driving of a new UI endpoint, though it had been a while since I wrote one of those, so I made some silly mistakes. I ended the stream with an Olive test (fails as expected) that I’ll pick up on the next stream.
Next Steps
For next time, I’ll:
- Fix
wrap()to usefindById() - Finish the Olive test for the reschedule controller
- Look at a tool for doing event-modeling?
- Maybe figure out in which package the
CommandExecutorFactoryshould live - Generify
CommandExecutorFactoryso I can use it with theCustomeraggregate - And maybe look at how to design aggregate-slices (Deciders) that can leverage the command objects, which might help in converting the
PurchaseTicketsUseCaseto use theCommandExecutorFactory
Join me on my next stream, which I usually do Monday through Thursday, starting at 20:00 UTC on Twitch: https://jitterted.stream.