Recently, I've tried my hand at building a notification system, but I quickly found that notifications are tricky things, especially in the context of building a general model. The diversity of what triggers them (from a clicking a button to something timing out; "actions" as I will call them), who is to be notified, and even what kind of message to send mandates an extremely flexible system to define and manage them.
I've seen many solutions advocating for an "active" approach, where the events that spawn notifications themselves create them. However, in my mind, this is the equivalent of a faulty toaster telling emergency services that it started a fire.
The metaphor applies both in terms of active intervention on the part of the actor, which decentralizes notifications (wait, which parts of the code create notifications again, and when do these happen?), as well as muddying the responsibilities of parts of the overall system, giving a lot of power to the actions which should focus on, well, acting.
On the other hand, it is completely intractable to analyse every side effects of every action, and decide what notification to spawn. Furthermore, the relationship isn't necessarily one-to-one: actions could conceivably spawn multiple notifications of varying types, destined to multiple users.
However, persistent state modification — in most cases, a database, and in mine, MySQL — is the crux of many of these actions, and I've tried to leverage this to create a notification system that can be both passive and general. The gist is as follows:
- All actions inherit from an
Actionclass that serves solely to package the "acting" part and trigger the processing of notifications. - During the execution of the action, all database queries are recorded.
- After the execution has ended, the notifications, each of which list their dependencies in the database (what values changed could cause them to spawn), are first filtered with a boolean expression specific to the notification on the list of tables/columns that have changed.
- The set of notifications coming out of initial filtering then query the database and each determine if they should be issued.
In case the definition of "action" extends beyond what is conveniently packageable, step 3 could execute at the end of the entire program execution, and all queries could be logged — the feasibility of which is determinable on a case-per-case basis.
The system is in the process of being fully implemented for testing in my project. However, I am concerned that relying solely on parsed queries is a rather Bad Idea. Notably, the initial filtering assumes to some extent that the queries are predictable up to the specificity of the notifications' dependencies (even if these are only specific up to tables changed, triggers pose a problem). 100% accuracy is a top priority, but at the same time, the efficiency of the system is paramount as well, especially if there is a prohibitive number of notification definitions.
Is my concern well-founded? Does there exist a [more] reliable way to achieve what I have outlined?
BuyProductfires an eventOnProductBoughtwhich has a listener that fires some notification (e.g. your friend has just bought a watermelon), then what exactly is this action doing more than acting? I mean the thing that knows what's happening is the action. If you want to throw this knowledge away, you'll need to do some crazy stuff to get it back - e.g. parse the executed SQL - but the SQL is still part of the action so you're not getting away from it, just complicating things. And the reliability and performance of such a system is going to be questionable at least.IEventListener<TEvent>interface). Also, an action can have multiple events on it. I don't see any tihght coupling between the action and the listener.