Planning for the unplanned

Every once in a while you get to build something truly unique. That was the case with Quizzpy - a real-time quiz for which idea was kicked around since the start of Tooploox. The founders were looking for money to build Quizzpy and started a software house that way.

Event storming the MVP

The idea is rather simple: a scheduled quiz which you can take part in via the mobile app. The much harder problem is: how do you build it? My first challenge in the project was distilling the vision everyone had into something tangible.

Fresh after NowThis and some workshops run by Arkency, I was pretty sure this is a good case for Domain-Driven Design. So the first step was an Event Storming - it’s a workshop to figure out the core flow within your product. Usually, it’s a way to connect Domain Experts and engineers - but here we had no experts since none of us had experience with quizzes. We decided that the Product Owner and Lead Designer will take the roles of domain experts since it’s their vision that will guide the product. Engineers focused on the few points in the system where the real-time nature of the game might impact the process.

In the end, it took us a couple of hours to get to the point where we had basic idioms for implementation. I think everyone in the team agreed that the time was not wasted; we identified several key points which made our implementation much easier:

  • the core flow wasn’t automated; the operator could pretty much jump between stages of the quiz without any machine decision getting in the way - very handy in case of bugs,
  • a common vocabulary, e.g. the distinction between "participant" & "spectator" that stayed with us for the whole project and made discussions much easier - we didn’t need to invent any terms on the fly,
  • events that happened during the game modelled both user actions ("UserJoined", "QuestionAnswered") and effects of processes ("QuestionSummaryReady", "QuizWinnersReady") - this became quite important where we ran into performance problems,

Deciding on CQRS/CRUD split

After looking at the roadmap and looming deadline, we agreed that implementing full scope using DDD techniques such as Command-Query responsibility segregation and Event sourcing is impossible. What we decided upon was a twofold strategy:

  1. Cut anything that could be done by humans without impacting core UX (e.g. prizes),
  2. Implement game flow using CQRS, but everything else with good ol’ REST API, i.e. "Create-Read-Update-Delete" endpoints for resources such as user accounts,

This turned out to be pretty ingenious because we had a working game within a couple of months (of three Backend and two Android devs). CRUD endpoints can be pretty much fully scaffolded using modern frameworks and interacting with them is well described. The core game flow, which on the other hand was non-standard anyway, used all the DDD tricks.

How DDD gave us superpowers

Have you ever wished you had implemented certain features three months ago? We certainly did. And we shipped several features that way - e.g. leaderboards. That was possible due to event-sourcing - the idea that instead of saving "a resource" you save "a fact". For example, instead of "PATCH /users/:id" modifying a database table, you instead add "UserChanged" (or even better, a semantic description of the user intention like "AvatarAdded"). Then you build your views based on those events. Having events like "ParticipantEliminated" or "ParticipantPassed" meant we could just backfill leaderboards based on these, for all historical quizzes.

Another neat thing was how the processes were modelled - since events like "QuestionSummaryReady" modelled the effects of asynchronous processes, there were no time-outs. This meant that the poor presenter was forced to come up with some impromptu jokes on the spot to keep viewers occupied, but nothing broke. Thanks to Erlang VM, this meant we could even impact live quizzes, by just modifying processes!

The third big thing was the ease of debugging - if something broke for a certain user, we could download all events related to that person and piece together a picture. Much like you’d with logs, except you are guaranteed that everything leaves a meaningful trace. We even had one bug that would certainly give us a headache otherwise - since login was tied to a phone number, several users had multiple devices in each live quiz. Our system accepted the first response for each user - meaning you could choose wrong, but if someone on another device picked the right answer earlier, you still passed. Of course, if the first one was wrong, both devices were out (since it was the same user). We never fixed the bug afterwards - just nicknamed it after our Product Owner who almost turned grey when he first discovered it.


Tags: domain-driven design tooploox quizzpy


Copyright © 2025 L Czaplinski
Powered by Cryogen
Theme by KingMob