2007-02-17

Transactions in user-interfaces

As you may know, the OpenSwarm SDK already imports specific UML models to generate Python apps that use PostgreSQL databases to store persistent business objects. Somehow this prototype even works, at least for the server side components (business logic).

As described in the introduction it is planned that a developer can also add user-interface (UI) components, which are either generated by the OpenSwarm SDK or manually implemented (or mixed). User-interfaces should of course support the transaction concept, but I wonder how you people design UIs with transaction feature that span over a single call (e.g. in the UI clicking "Delete" button of a specific record).

In the unlikely case that the ORM may irritate you, here is a brief description how it works in this project (else you can ignore this):
The back-ends are using PostgreSQL transactions represented as Operation objects. Typical use would be (with short Python examples and most important SQL representations -note that the SQL queries are executed instantly, no big cache/repository is used in the app layer):

  1. Start transaction on a component:
    Python: oOp = myComponent.createOp()
    SQL: BEGIN
  2. get the object with unique id 1:
    Py: numberOne = myComponent.getEntityByEID(oOp, 1)
    SQL: a "SELECT ..." to get records in some specific table
  3. call method on object #1 to create some new Foo object #2
    Py: numberOne.newFoo(oOp, 'test value')
    SQL: some "INSERT..." on several tables
  4. commit the transaction
    Py: oOp.commit()
    SQL: "COMMIT"
In principle there are calls that are creating new objects, reading or modifying them, deleting them, adding and removing them from collections (objects linked with another, which is mapped in an association table).


So, given a simple case, in the UI the end-user may browse through a list of Foo objects (records), retrieves one of them to view in detail and wants to change the one or other attribute. Assume that the Foo class has a method for changing the name attribute, Foo::update(self, oOp, name:string)E.g. by clicking on "Edit" button (transaction begins here) a form pops up (or the dialog changes to a editable form), expecting all the fields for the update method to be called if end-user clicks on "Save" (transaction commit, so records in database are changed) - or the end-user clicks on "Cancel" and the transaction is rolled back and closed with-out any changes to the database (update method would not be called of course).

I think, this is a rather common case and doesn't make me any head-ache. Now let make things a little bit more complicated: The same as the above, but it is this Foo object (or record) detail view with some attribute values of the Foo object - plus a link to another object, e.g. of the class Person. Assume the end-user clicks "Edit" and this magic update method associated with this form expects exactly one linked object, e.g. Foo::update(self, oOp, name:string, author:Person). So, the edit view shows one text input widget and some object browse & select widget, so the end-user can browse through the list of Person objects and assign the current object to that. Probably the end-user even has even the option to create and assign a new Person object in this very same object browse & select dialog, before he is saving the updated Foo object.

So, is the creation of the new Person object part of the same transaction as the update of the Foo object? Or is it better for usability and UI creation if the Person object is created in a first transaction and committed, before a second transaction takes over the update of the Foo object when end-user clicks on "Save". In the case of two different transactions clicking the "Cancel" button in the Foo edit view, the Person object would already be persistently in the database, while in a single transaction this would not be the case (the Person object creation would be rolled-back with the entire transaction).

Well, this example is still rather simple. I can imagine use-cases that are much more complicated (envolving objects of many classes, or even from different components, think of 2-phase commmit and "nested" transactions).

How would an user-interface support different handling (either a single transaction or multiple transactions) and on behalf of a good usability: How would this be transparent to the user?

I guess, as long as there is no concrete and satisfying answer to these questions there is no sense in defining the way how developers can specify UI components in OpenSwarm application models and how those specifications can be transformed into runtime implementations.

So, any ideas?