I wanted to be a good girl and use some programming language that was "made for" making web applications, but the more I tried I realized I just wanted to go back to C++. I've been having fun reinventing the wheel creating my own Intent and Result types, representation objects, using those to generate a document structure and finally passing that as a HTTP response. It feels really good to own most of that pipeline (besides the async handler).
Here you can see the basic structure. Parse request into an intent, which is either a command or a query. Each intent has a modeled result type (in this case, the changed pet representation after applying the paintbrush, or a command error), and the Result can be unwrapped into the representation or the error for branching. The handler will determine if the actor is allowed to act on the pet, if the brush exists in the store and can be applied. If it can be applied, an event is appended in an event store for the pet. Then the events are played back and used to create the representation, which is stored in the representation store until the next event. The updated representation is passed to the response which swaps the contents of the DOM using htmx.
That's the idea anyway.

















