Oh dang, yeah that is a very complex problem space.
I can see how just doing deltas would be difficult, especially if the delta you have is not clearly mapped to an action or set of actions that you have to disambiguate such as dragging a workout from one day to another and combining it into a session of several others and potentially then editing them.
I think if I were approaching this I would have a hard a time determining when I stop and persist since you’re at the mercy of the combinations that they want to do and arbitrarily deciding " when this really complicated thing happens we recompute the next state and persist" could arbitrarily limit flexibility.
So I think I would probably either have a library of actions that can be done atomically then clever ux to make it apparent what is and is not possible at any given point in time…
OR I would do that same ux work but let the decision of when to do something about an action that a user takes be separated from the part of the application that ensures what the next actions can be or what the current state of the world is.
That’s a circuitous way of saying if this was my whole product or a significant portion of it, I would probably do an event sourced approach, and likely do CQRS. Based on the current state/aggregate that would determine the kinds of things they could do.
I’m imagining that if they dragged a workout from one day to another. You would look at the current state/aggregate of their calendar to determine what to show on the UI as a hint, but the thing you would emit would be an event saying they dragged an existing item to a new time slot, maybe with some metadata or a more specific event to denote that there was nothing seen in that time slot to the user so you that you can preserve intent in case some event popped into that time right around when they released it.
It would be processed, and a command or commands could be emitted that would later in the projector move it or delete the old and make a new one in the same transaction. The benefit also is that depending on how you structure the pieces, and aggregates, you could have an aggregate that represents a partially completed, edit or merge or whatever of the example from before where you drag it to an existing slot on another day in a way that means “combine these into a workout session but let me edit it first”. With an aggregate representing available time slots you could model that the time slot is not available or is pending an edit but is not finalized until the finalizing event happens. This would be a separate aggregate from the one that would be used to model the series of edits to that workout session itself.
I’m intentionally making something more complicated to show with flexibility, but if I were doing this I would probably go with the “persist on every action” pain (plus I imagine all of the optimistic writes and fallback logic needed if there was multiple calendars involved) while I figured out the right aggregates and events.
I’ve used commanded and liked it but I will say it is difficult to wrap your head around if you are new to CQRS, and I’ve only used it in production a couple of times. Once where the problem did not need it but did need basic event sourcing, and another time where it was absolutely vital and would not have worked without it.
There are also some other good event sourcing libraries, but for me it’s the separation of concerns and the ability to model the true problem that would make CQRS appealing
Sorry for the voice to text wall of text.