In the middle of writing the API, I was simultaneously writing the subscribed events model while trying to multiplex the functions that the API needed to perform, as well as cope with both serialised objects coming in as well as field by field changes...
The more I looked at it, the more I was unhappy with it. So, I took a bit of time away from the keyboard and spent a couple of train journeys working on ideas on paper instead.
I started re-working the API yesterday and finished that work today. There is now a base class that encapsulates a bunch of preparation and validation work that was common across most of the API calls. Proper exception handling for invalid API calls has improved the composition of the classes, simplifying the logic and cutting down on screen real-estate. The API calls now support both serialised and non-serialised parameters in a way that is much less complex and messy than what went before. Very pleased with progress.
The MongoDB store has been split up into domain-specific stores based on the interface that they honour, while the conversions to and from BsonDocument objects have been implemented as a static extension class, further reducing the number of lines to cope with in each file.
Subscription event handling has been substantially re-written after some input from a friend about how I had essentially re-implemented condition-action behaviours but in a different way. With that in mind, and after a couple of panics over some bad ideas, a single behaviour has been added to the flow, hooked onto the "api-event" message.
Originally I was trying to add custom behaviours to the flow based on the database that is used, but I fell foul of not being able to modify the message bus's listener collection.
The new behaviour has a standard condition-action pattern, the condition being the usual plus whether the correct items for a subscription event are recorded in the control state. The action is to run the event engine as before. The conditions are still run in a separate way, I guess, but the framework is tidier. Since the event engine runs asynchronously, then I suppose there is no way to run the events as individual behaviours from within the message bus anyway.