views:

1235

answers:

5

I have a requirement to build 'versioning' into an application and was wondering how best to approach it.

I have this general pattern:

Model A has many B's

Where on update the attributes of A need to be versioned and its associated objects (B's) also need to be versioned. So the application will display the current version of A, but it must also be possible to view previous versions of A and its associated objects.

I would like to use a document store however this is only a portion of the application and having a doc store and a relation database would introduce more complexity.

I have considered using a star schema, but before I progress I was wondering if there is a design pattern floating around tackling this problem?

This question is slanted towards resolving the issue of storing the versions of an associated object in a relational database. Where there is an inherent need to be able to effectively query the data (ie serializing object won't suffice).

Update: What I was thinking/have implemented but want to see if the is "a better way"

,---------. 1      * ,--------.
| Model A |----------| Model B|
`---------'          `--------'
|PK       |          | a_id   |
|b_version|          |version |
|version  |          `--------'
`---------'

Where I would be duplicating model A and all the associated B's and incrementing the version attribute. Then doing a select to join the B's via b_version and b.version. Just wondering if this can be done better.

+1  A: 

A combination of the Memento pattern with the Observer pattern should fit your needs. Also have a look at the Visitor pattern for possible application in your case...

Kosi2801
Sounds like it could help I guess my question pertains to the design at the DB level. How do you go about persisting the Memento when they have multiple associated objects, which also need to be persisted.
MatthewFord
Each object is responsible for storing its state into the according database table.When each object also holds an attribute "version", which is synchronized via the Observer on every change, storing a consistent and reproducible state should be possible.
Kosi2801
+2  A: 

I don't think there is no specific GoF design pattern per se for versioning because there exists many implementations of it.

The most simple implementation of versioning is a linked list of objects. Where each node in the list is a new revision of whatever the versionable object is. To save space you also implement some kind of a diff that shows what the difference is between the revisions. That way you can store diffs in the database, but also the final version of the versionable object since the version control system should be able to derive the versions in between.

The database schema could principally look something like this (you can see this pattern in most wiki systems):

+--------------------+ 1     * +-----------------------------+
| VersionableObject  |---------| Diff                        |
+--------------------+         +-----------------------------+
| lastStateContent   |         | difference                  |
| originalAuthor     |         | revision                    |
| #dates and whatnot |         | # userId, dates and whatnot |      
+--------------------+         +-----------------------------+

If you want to go hardcore with branching and stuff you might want to consider have a look at DAG which is what modern distributed version control systems use.

Now if we talk about your example a whole slew of objects that needs to be saved in configurations. I.e. we have to pick out the revisions of objects that we want for the model. It means we have a many to many relationship (which is solved with an intermediary table), sort of like this:

+---+ 1   * +---------------+ 1   * +-----------------+ *   1 +-------+
| B |-------| Diff          |-------| ModelSelection  |-------| Model |
+---+       +---------------+       +-----------------+       +-------+
            | revisionNo    |       | {PK} configId   |
            | {FK} configId |       | {FK} modelId    |
            +---------------+       +-----------------+

I hope this helps.

Spoike
This implementation won't solve the problem of maintaining the relations between versions and their associated objects
MatthewFord
deimos1986: of course it does, updated with an dbschema example. you can see this pop up in wiki implementations. I suggest you look at MediaWiki or other opensource wiki systems and have a look at their database models to get some inspiration
Spoike
Oh you mean you have a whole configuration of objects that needs to be versioned...
Spoike
Yes, an object and all their associated objects need to be versioned
MatthewFord
In this wiki example it would be similar if each wiki page also had comments tied to a version and when you go back to an old version you can the appropriate comments for that version
MatthewFord
@deimos1986: updated a little bit regarding configuration handling in a database. think Model as your A, and ModelSelection as your association towards the B objects (but selecting specific revisions of those).
Spoike
@Spoike I'm not sure if another join model is strictly necessary (see my update) but would you say this solution is 'good enough'?
MatthewFord
+1  A: 

I've solved this problem in rails by using the acts_as_versioned plugin. When you apply it to a model, it assumes there is a model_name_version in addition to the model_name table. Every time a model is saved, the old version along with a timestamp is copied into the model_name_version table.

This approach keeps the size of the model table manageable while still allowing search on previous versions. I'm not sure the plugin handles the chaining you want out of the box, but it wouldn't be hard to add.

Sarah Mei
I've taken a look at acts_as_versioned and other existing plugins, none seem to handle this problem of versioning associations.the AAV wiki page has this open question: Has anybody done any work with versioning foreign key relationships...
MatthewFord
Right - I expect it doesn't. However, plugins are remarkably easy to modify, and this particular change would not be difficult.
Sarah Mei
I've written a plugin for Sequel that implements this along the lines described in the question. I'd like to take a stab at it again in AR and do it the best way possible. If take a look at the AAV plugin code, extending it to handle n..n associations is non trivial.
MatthewFord
Perhaps acts_as_versioned_associations would be a better starting point? http://github.com/rlivsey/acts_as_versioned_association/tree/
Sarah Mei
Will take a look at it, thanks Sarah
MatthewFord
+2  A: 

Martin Fowler has some good articles on time/versioning-based design patterns - definitely worth a look:

http://martinfowler.com/eaaDev/timeNarrative.html

Martin Dow
This link has been good for affirming my ideas, as he is using time as a dimension where I propose using a version number.
MatthewFord
A: 

What about saving a XML snapshot of the database shema you want to version? And then be able to change the state of the database?

etienno
to be honest looking back I sort of dodged the problem by just getting all the data I needed and combining it into one big json doc and dumping it into couchdb when I needed to take a version (of a collection of tables). The issues isn't so much with the schema, but with spanning multiple tables.
MatthewFord