Friday, 15 January 2010

Transaction handling

Short post today: I need to sort out the transaction handling on this form. It currently commits the posted record and then goes on to insert/post records into other tables, committing at each stage. There is a rather amusing comment/rant by someone who had already spotted this:

// This is WRONG! The whole transaction should commit or rollback as a whole (i.e ATOMIC).
// What this will do is commit the [main record], but if we have any error
// inserting the data in [additional records] below that data will not be committed.
// There is too much risk of breaking something related at the moment, but this should
// be fixed or it will come back and bite you.


Absolutely! So it looks like it has fallen on me to sort this out... nice :)

Thursday, 14 January 2010

Observing the data layer

I've been busy separating out the data layer from the rest of the form code. First of all, I had the entity object owned by the data module. and holding a representation of the current record in when in browse mode. This entity object was exposed to the form, so it could be updated when the form was in insert/edit mode. After a while working with this I decided that it was the wrong way round. The entity object should be owned by the form and act like a 'jsp style backing bean'. When the form creates the datamodule, it passes a reference to the entity object so that the datamodule can use it as a source for data persistence.

This approach has a few benefits:
  • If I introduce a controller object, this can 'own' the entity object and pass it through to the form (view) and datamodule (model)... very MVC!
  • The form has greater separation from the Model.
Anyway, now onto the topic of the post: 'Observing the data layer'. Now that I have the data components separated from the form, I needed a mechanism to allow the form to refresh to the state of the datamodule. In the spirit of 'do the simplest thing that will work', I simply created an interface on the form class, exposing the existing event handlers for the handled data component events. Then, when the form creates the datamodule, I pass through a reference to this interface so the datamodule can 'call back' when it raises these events. This go things working. The next step was to then move code from these handlers that should be in the datamodule. This is the bit that has take me a while as the form and datacomponent code was nicely mixed!
Through this process I was able to remove some of the calls in this interface, where the code was entire database side. The remaining callbacks I renamed to give them meaningful form operation names such as 'ChangeViewState' and 'DisplayCurrentRecord'.

So now I have a form that doesn't contain any direct references to the database components and a datamodule that only operates the form via the designated interface. To further separate these two objects, I could go fully down the MVC route and implement the 'Observer' pattern. This would involve the form registering with the datamodule for data event changes. However, at the moment I don't think this is going to be necessary.

Wednesday, 6 January 2010

Entity object table-tennis

Today I've created an entity class that represents the data shown on the form. The purpose of this entity class is to act as a layer of separation between the form (View) and persistence mechanism.

It this point I think it is a good idea to clarify the responsibilities of each component:

Form (View):
  • Manage all the controls on the form
  • Present the data on the entity object
  • Update the entity object with users edits

Entity (Model):
  • Hold the data
  • Manage default states (blank, keep data etc)
  • Domain logic relating to this entity
Datamodule (Model):
  • Persist data on entity object
  • Populate entity object to contain data for currently selected record.
As you can see this is still violating the principle of single responsibility, but it is a big improvement on the starting position where all of this was on the form. The introduction of a controller class will further help, but that is for a later day.

I'm keen to move as much domain level logic out of the form, into the entity class as possible. This is for two reasons: the forms should be for presentation of the data only and the entity objcet should be more than just a data-holder. I'm going to focus more on this second reason.

Classes that do not have any methods, just data-properties, are child-like classes... they haven't grown-up yet. They are a good starting point, but if they stay as data-holders, there is something wrong with the overall design. Something is doing the work that this class should be doing.

Tuesday, 5 January 2010

TDBLookupComboBox

Yesterday I converted TDBEdit controls to TEdit. Today, I'm going to tackle the TDBLookupComboBox components on the form. These are a little more tricky as they have two associated datasources: one for the value and one for the list. I'm only really interested in removing the link to the main datasource as this is the one that updates data. The list datasource is read-only.

I'm now going to do a bit of R&D. I don't know if you can use a TDBLookupComboBox and in a non-data-aware way for the value, but data-ware for the list. If I can, this will make things much easier.

...

Cool, this can be done with little fuss.

Steps:
  1. Remove the datasource and DataField property values from the TDBLookupComboBox.
  2. Use the KeyValue property in lieu of the Text property to access and set the chosen selection
This is really neat as it means I do not have to manually populate the drop down list or do any ListField to KeyField lookups when writing to the DB.

Next up is to do some refactoring on the form code. It's all a bit mixed up at the moment. I want a clear separation between my user interface controls and the database component fields. I'm going to achieve this by creating an entity class which I can use to pass data between the Form and Datamodule (View and Model).

Monday, 4 January 2010

Breaking away the data layer

I've decided, somewhat randomly, to start by breaking away the data layer. Time will tell if this is the best approach. It just seems the most logical to me as I'm looking at the form with data access components cluttering it up. I'll move all of these onto a new datamodule and refactor the form to use the data module. The datamodule will be the starting point for the model part of the MVC.

Steps:
  1. Create a new data module unit
  2. Remove datamodule from list of auto-create forms in project options | Forms
  3. Remove global variable in the datamodule unit
  4. Select all the data components including data source components
  5. Copy the selected components onto the new data module
  6. Add a private member reference to the datamodule on the form
  7. Add create datamodule on form create and free datamodule on form destroy
  8. Save and build
  9. Remove data components from form.
  10. Save and build - this will give errors for each instance where the data component can now non longer be found.
  11. Go through this list of errors, correcting the reference to the components on the datamodule.
  12. Relink the data aware controls to the relevant datasource components on the data module.
  13. Relink the data component event handlers to the components. I exposed the event handlers on the form as an interface so that the data module could delegate the handling back to the form.
  14. Save and Build.
Next, I need to convert the data-aware controls into non-data-aware controls. This will allow me to separate and take control of the data-capture and data-posting. I don't want the data-aware controls to post directly to the edit buffer of the query component that is used for posting new records. Instead I want to build an object representing the record(s) and then send these to the datamodule to post.

First off I'll convert a TDBEdit to TEdit. This should be done in two parts. The first part is to re-type the actual control. The second part will be replace functionality that would have been done by the data-aware events.

Retype steps:
  1. Make a note of the field that the data-aware control uses.
  2. View Form as Text
  3. Find the declaration of the TDBEdit to convert
  4. Change the type to TEdit.
  5. View form as Form
  6. Save. At this point the IDE will display messages saying that some of the additional property values of TDBEdit will be lost if you click 'Ignore'. This is what we want, so click 'Ignore All'.
Replace lost data-aware functionality steps:
  1. On Model change: enable/disable and populate control with current data field value
  2. On Post: populate insert/update query buffer data field value with the editor value.
Just be a little clearer here: On Model Change means any event on the data module that should trigger the form to be refreshed. On Post means committing the data on the form to either an update or insert.