开发者

Architecture for handling both Undo and Notifications

I'm currently designing a complex website that needs to support Undo and Notifications for CRUD operations.

By Undo, I mean that the user may create/modify/delete an item and then decide to cancel what he did (therefore removing the created item, restoring the previous state of the modified item, or bringing back the deleted item). He can do so for a short while after doing that operation (the timeout is 2 minutes, because the system is fairly complex and it may take some time for the user to notice that the modification was incorrect).

By Notifications, I mean that when an item is created/modified/deleted, other users that are interested in that item will receive an on-site notification (the next time they load a new page) and will also receive an e-mail or SMS notification if they asked for it.

Getting either feature done is simple: Undo involves keeping undo information around (old version of modified or deleted it开发者_如何学JAVAems, mostly) while Notifications are as simple as pushing a notification object into the appropriate tables and sending an e-mail/SMS on the fly.

Getting them both to work together creates a lot of additional complexity because Notifications must remain in stasis until the Undo becomes impossible, and I don't have enough hindsight on the matter to design it in a sane way.

What would be patterns, best practices or pitfalls to avoid when building such a system?


I'd consider using the Command Pattern for Undo (possibly not persisting until the timeout expired) and the Observer Pattern for Notifications.

EDIT1: Rethinking that, I'd probably persist immediately. Otherwise you have the issue of the user's session ending before the timeout expires.

EDIT2: Another option would be to use Windows Workflow Foundation 4 (WF4) and Compensation. Set up a long-running, persistent workflow as a WCF service. The workflow could just be a WCF Receive activity to start it, a Delay activity, and a custom activity to send notifications; you might not even need a CompensableActivity (if the delay hasn't expired and the notification hasn't been sent, there's nothing to undo). Each notification request makes a WCF call to start a new workflow instance. If the user cancels, abort the instance. (It might be easier to do a Pick activity with a Delay and another WCF Receive activity that cancels the notification if called.) Caveat: I haven't implemented a system like this.


Based on a bit of experimenting both in thought and in code, I managed to extract a series of principles.

  • The user may cancel the operation by clicking the cancel button, or by performing the reverse operation (such as deleting his comment). From a notification standpoint, there's no reason to treat these two situations differently.
  • Notifications for the same object that happen at about the same time should, as much as possible, be grouped together: there's no point in displaying "A commented on your post" ... "Z commented on your post" instead of "A, B ... and Z commented on your post". As such, creation and deletion notifications should be grouped together according to grouping rules: if they're sufficiently close together, they cancel each other.
  • Because of the possibility of cancellation, never send notification e-mails straight away. Wait for two minutes instead. If the operation is canceled, the creation-and-deletion group rules will delete the unsent notification while it's still pending. Of course, if the user is currently connected, you may display notifications in real-time because unlike e-mail, real-time notifications can be "taken back".

Based on these principles, I decided to architecture two distinct layers on top of the data access layer:

  • A set of semantically significant operations such as "post a new comment" or "delete a comment" which are then wired to send out notifications.
  • A definition of undo-enabled operation pairs : the "undo" aspect of "post a new comment" is "delete a comment".

This way, operations always pass through the basic set of operations and thus trigger notifications that are agnostic of whether they are caused by canceling an earlier operation or running its inverse operation manually — the system cleverly handles multiple notifications about the same object by grouping them together and letting them cancel each other out.

Conversely, the "undo" facilities merely call the code from the semantic layer, without requiring any knowledge about the notifications being sent out.

Obviously, there will still be a little amount of coupling, because it sometimes is necessary to add a semantic operation only because it's the "undo" inverse of an existing operation. For instance, one can cancel the creation of a discussion, but cannot delete the message once it is sent (because there might be replies on it). So, a cancelDiscussion function might exist where a deleteDiscussion is unavailable. I suspect such situations are rare enough to safely accept.

0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜