Reputation: 22858
In a system I am writing, I have the concept of package and service objects.
A package is a container for a collection of services, and has an intrinsic set of configurable restrictions. These restrictions include:
There is a need to be able to upgrade and downgrade between compatible packages. By doing so, the services within the package need to be transferred, modified and potentially created or deleted to meet the constraints of the new package.
This needs to be done before any money is taken to ensure that certain unique resources are allocated to the services (and ring-fenced).
The problem comes if payment fails and the upgrade is cancelled - how do we rollback the modifications and return to the previous state?
The following (non-exhaustive) list of issues could arise:
The way this is handled at the moment is through a package-specific set of steps that store objects out of the way (rather than deleting them), and hold a list of steps to restore state back in the event of rollback. However, this is done on a per-case basis, is error-prone and feels clunky.
I was wondering if any of you had encountered anything similar and knew of a pattern or methodology that could be employed here. Basically we need to be able to update the state of a set of objects, commit the changes, but have the option to rollback to a previous version. NB: Within the system the state of objects can be serialised and stored.
Any "I wouldn't do it like that!" comments gratefully received with alternatives.
Here is an example:
Before upgrade:
After upgrade:
Upvotes: 2
Views: 99
Reputation: 4143
Done something similar, but, very quickly.
In order to perform an "undo" / "rollback", you need to do some things:
[1] To store / register / serialize the value of the given objects ("Package" and "Service" (s) ), before performing the operation.
If the operation succedes, that info could be discarted, or just stored as historical data. Otherwise, use it to return the objects to the previous state.
You may also want to register the new changes.
[2] You have to register a menu or collection of operations that can be done by your app, and eventually rollback.
+----------------+ +------------------+ |................| |..................| |................| |..................| |.......App......|/\ |.....Operation....| |................| ----------|..................| |................|\/ |..................| +----------------+ 1 * +------------------+
For example, if instead of the "package-and-services" application, you where doing a Paint program.
In your application, you'll have a collection of actions or operations, and one of that operations is the "Paint filled square".
[3] Each time you execute one of those operations, even, if duplicated, you have a register or log of each operation that can be done, or rollback.
+----------------+ +------------------+ |................| |..................| |................| |..................|/\ 1 |.......App......|/\ |....Operation.....| --------+ |................| ----------|..................|\/ | |................|\/ |..................| | +----------------+ 1 * +------------------+ | /\ | \/ | | +------------------+ | 1 | |..................| | +--------------------|..................| | * |.......Log........|----------+ |..................| * (The same operation can be |..................| registered in the log, +------------------+ several times)
When you execute you Paint app., you use the "paint filled squared", several times.
[4] You need, in your list a opposite operation, that restores the matching one.
+----------------+ +------------------+ |................| |..................|-------+ 1 |................| |..................| | |.......App......|/\ |.....Operation....| | |................| ----------|..................|-------+ 1 |................|\/ |..................| +----------------+ 1 * +------------------+
Using the previous Paint app. example, you should have an "restore filled squared" operation, that leaves the area, as before the user painted the filled squared.
Don't care how its done, care about that there should be a matching one.
[5] The matching opposite operation must be registered in the log, also.
Upvotes: 1