views:

236

answers:

2

using classic asp, over the years we have developed a framework to handle some fairly complex web crud pages.

the crud class is designed as some kind of "finite-state machine"

the state is preserved between posts using hidden fields, and every event on the pages raises a post with a certain action. according to the action triggered, and the previous state, the crud class performs certain operation (like running a query, saving a record, edit a record, etc...) an goes to another state.

these are some of the states we are handling so far

query: lets the user enter query criteria to filter data.

browse: shows the data in a table form. with pagination, ordering, etc...

new, edit, delete: well, pretty obvious

browse-single: like edit but read-only

etc...

in order to achieve the desired functionality we keep in hidden texts info like: actual action, actual mode, previous action, previous mode, id of the selected record, order criteria, etc...

now we have implemented some master-detail functionality but this approach is getting a bit too complex.

we have to keep in hidden fields the id of the parent record, the parent field, the mode of the parent, the mode of the child, the current tab, the previous tab (we divided the info among several tabs)...

things get even more complicated when you have to handle "custom" actions (like approving an invoice), or warnings, errors, custom "workflows" and thing like that...

on the end we see that with this approach things get too complicated as soon as we deviate from the standard crud case...

what do you think of this approach?

how do you handle these kind of things?

any pattern you can advice me to look?

how do you keep tracks of the form's state?

+1  A: 

This sounds reasonable.

It sounds like you have a tight coupling between all your code, so that when you deviate from your standard pattern, it doesn't work well. You might consider breaking this down into layers. Having layers will separate out the concerns, so that you can replace one part of it, but not the other.

Figure out where you need flexibility:

  • if you need flexibility about displaying forms, you could look for a form file, and if it's not there, use your default. So you can figure out how to override the form in reasonable ways.
  • or perhaps the forms are fine, but you need a slightly different flow (a preview after the edit), for some entities. Figure out how to just override this controller logic.

I once built a similar thing. It would automatically build CRUD behavior for any entity. But you could override the control any action in the controller, if you wanted a preview behavior or had some weird editing flow or different views for different situations. It was done by overriding a base class. You could create your own high-level list of fields to control the order and appearance of fields. Or you could create a low-level form, replacing all the standard stuff, and control all the display for a given form.

You might also look at Rails Active Scaffold, (yes, I know not ASP) which is solving the problem a similar way, providing several extensions points.

ndp
yes, the problem is specifically with the flow (the class doesn't generate the whole form, the layout is specified with asp / html), yours is a fine example, a "preview" mode, or some specific flow. I have to find a way to emulate "inheritance" in classic asp, or some other way to customize the class's behavior...
opensas
+1  A: 

I have used this approach myself and been quite succesful.

This is one of the easiest ways to acheive scalability as you can easily balance over several servers. However unless you update the database on every action you can easily lose user input. Where the transaction is complex involving several screens and tables you can easily leave your database in an inconsistant state.

A possible varation on this are to store the data in browser cookies so they are not quite so visable.

The other alternative is to store everything by session on the server side (ala Spring framework). This makes the whole thing more secure as there is no oppertunity for a malicious script to play with the key and state values, but, in a high traffic site storing and retrieving the session state quickly becomes a bottle neck, and, in a load balanced site you need to keep a user session tied to a particular server.

The heavy wieght solution is to store the session data in a database so it is readily available from several servers.

For complex updates you could also consider moving away from a CRUD pattern to a more sophisticated WorkFlow pattern. In a workflow pattern you view the users input more as a traditonal form (think multi section tax form with appendixes etc.) which must pass several processes (creation, verification, approval, etc.etc.) before it is actioned.

You can store the input as a reasonably complex xml with both the requested updates and the current state. Updates for several tables could be stored in a single xml, and, only in the final phase after all changes have been entered, verified and confirmed are the underlying tables updated. You associate a screen with each state, the user actions on the screen lead to new states as in a classic Finite State Machine.

A big advantage of this approach is that the "form" is linked to the user and not the session, so, the input can be entered and edited by many sessions over several days (this may also be considered a disadvantage!).

James Anderson
in fact, we try to reduce every interaction with the DB to single record operation. for example, if i have to manage a master invoice with several details, I save the invoice to the db in a "draft" state, or something like that. I let the user edit, suspend, resume, do whatever he likes, saving record by record to the db. When the invoice is "closed" I apply every validation to it... That way my db layer is pretty simpler, and my web pages just have to save one record at the time...
opensas
that is, instead of saving the user data in an xml on the db, I just save the data with a "draft", "work in progress" or something of the sort...
opensas
One adavantage of having a specific WorkInProgress store is you dont have to have lots of "AND STATUS NOT = 'draft'" predicates all over you application.
James Anderson