How I Broke My Database Addiction: Part II – The Solution
This is a continuation from a previous entry How I Broke My Database Addiction: Part I – The Problem
In refactoring this application, we have turned the architecture on its head and have completely redesigned the application from the ground up, but have had to keep the original design requirements intact. We are using Mach-II for our application framework, and using Reactor for ColdFusion as our data abstraction layer and ORM framework. There is far more to the rewrite than was discussed in the previous post, but for the purposes of staying on topic, I will cover how we have eliminated some of the excessive round trips to the server, and in turn eliminate the excessive trips from the server to the database.
The first and easiest thing to address was the custom settings that belong to each Company (our customer). These properties rarely change, so the obvious answer was to place them in the application scope. Our solution was to create a structure like this:
application.CompanyStruct[“CompanyId1”]
application.CompanyStruct[“CompanyId2”]
etc…
Each item in the structure is a Company Object, containing all the properties of a Company. Then in our Mach-II application, we have created a plugin to make sure sure that a Company is loaded on each end-user request. If a Company Object has not been loaded, the plugin creates the object in our application.CompanyStruct. When a customer administrator changes something about their company in the system, they simply mutate the value in the structure object and save, which both alters the application scoped Company Object, and persists it to the database.
Now, instead of retrieving this information on every single request of every single user, this Company is pulled only one time on the first request by the Company’s applicant.
Our application server and our database server nod in approval.
Next, we address the application form itself. In order to make the designers happy, we still have 9 tabbed “pages” in the application, but rather than continually make pointless round trips to the server, it is now a single page request to load the form, and the tabs are managed client-side. Now clicks between tabs are instantaneous, and our customers will hopefully stop wondering if the system has just forgotten about them. This obviously adds faster load times, but it also creates a new issue:
This is no small form!
Even if the user knew exactly what they needed for every form field it might take them 20 minutes to walk through it even if they knew what they were doing. Couple that with the fact that they might have to retrieve documents and records to use as reference, and you have to consider what might happen if they have gotten all the way through to the 4th or 5th tab and the walk away. Of course they could hit “save” and all would be well, but logic soon brings the obvious answer that many Users will not think to do so. After much deliberation, we came up with what we feel is a sound solution.
In the new system, a User will already be logged in by the time that they reach the application form page. This gives us a handle on them by use of a User Facade. In our model we have defined that a User hasMany Application (as in “he/she being an applicant”, not as in “ColdFusion application”). That way as we make changes, by adding/modifying/deleting values of properties of a particular User’s application, we do so only in the Application object that is a child of their User Object.
As a User walks through the form, we are using AjaxCFC to update the User’s Application Object with an onChange() event on each form field. What this means is that when a User has filled out the first 10 form elements, those 10 properties in their Application object have automatically been updated behind the scenes to reflect this. One important thing to note is that up through this point, the database is not updated in relation to this User’s.
So how do we update the database?
One was is when the User submits their Application. Thanks to cascading saves in Reactor, this is as simple as calling: User.save(), which then trees down through the User hierarchy saving dirty (changed) objects. Then we begin the submission process which is outside the scope of this post.
So, we still have the issue of what would happen if they just walked away. This brings us to what I believe is an under used gift that we received with CFMX7, that being OnSessionEnd().
(note: I started to make and reference a seperate blog post on the OnSessionEnd() method, but after a quick Google search, I see that Mark Kruger, Todd Sharp and others have this well covered. If you are not familiar with how OnSessionEnd() works, I urge you to check out those blog entries.)
Using the combination of cascading saves in Reactor and OnSessionEnd() we make solving this problem almost embarrassingly easy.
In our OnSessionEnd() method, we take the following approach:
(warning: pseudo code ahead)
if (User has an Application) {
User.save();
}
Yes, it is that simple!
So if our User has entered 20 form fields and his house is suddenly struck with a small meteorite, forcing him to leave while his house and computer burst into flames causing a total loss, never fear! When he logs in from his friend’s house the next day, his Application will still be in process, and he will not have to start over. Well… he won’t have to start the application over anyway.
Of course this begs the question – But Does It Work in Production?
Keep your fingers crossed!
