Pramod Jain

Subscribe to Pramod Jain: eMailAlertsEmail Alerts
Get Pramod Jain: homepageHomepage mobileMobile rssRSS facebookFacebook twitterTwitter linkedinLinkedIn


Related Topics: ColdFusion on Ulitzer

CFDJ: Article

Model-View-Controller based Plug-able Declarative Framework

Simplify the development of scalable, maintainable, and robust Web applications

A Model-View-Controller based Plug-able Declarative Framework (MVC-PDF) will be described, in which developers use the full power of ColdFusion MX (CFMX) components to create MVC applications. The declarative nature of the framework will allow the controller and the model to be specified declaratively in configuration files, versus custom coded in ColdFusion.

A MVC-PDF will allow developers in a team to write no ColdFusion components; instead, these developers will code the business logic in controller and model XML configuration files.

The pluggable nature of the framework will allow developers to create components for custom business logic that will then be called declaratively. These model components extend an abstract model component provided by a MVC-PDF.

The benefits of a MVC-PDF are higher productivity and quality when building large, scalable, robust, and maintainable ColdFusion MX Web applications. This is because in most situations, MVC-PDF developers will write code at the two ends of an application: the front end (CFM pages with only presentation logic) and the back end (for instance, SQL, stored procedure, and conditional logic). A robust declarative and pluggable middle tier takes care of the rest. A MVC-PDF will lead to a very high degree of component reuse in both the model and controller layers. In an extreme case of reuse, developers will (a)write only CFM files that contain only the presentation logic, and (b) write the desired business logic in two configuration XML files. Note that developers are not writing business logic in CFM files and in most cases are not writing components (CFC files).

Background
ColdFusion MX provides the capability to create applications that use the MVC paradigm - separation of presentation from business logic. One method of using the MVC paradigm is to encapsulate business logic in model components, and to call these components from submit events of CFM pages. An example of business logic implemented in a component is (a) execute a select statement; (b) based on the data returned by select, execute an insert or update statement; and (c) run another select to retrieve data for sending to the next page.

Mach-II (www.mach-ii.com) is a popular component-based framework that uses the implicit invocation architecture for developing Web applications in CFMX. Its focus is on creating reusable listeners. As the name suggests, listeners are components that (a) listen to events submitted by CFM pages; (b) connect the events to a sequence of actions that must be performed by models to process an event; and (c) return a view page with dynamic data from a model. Mach-II provides a reusable listener component that is configured using an XML file. However, developers still write model components to execute business logic. MVC-PDF uses the Mach-II framework as a listener; however, it significantly extends Mach-II by providing reusable model components for business logic.

As an illustration, consider an application that has 10 CFM pages, each requiring business logic to be executed on submit. If the Web application requires 30 separate SQL statements (10 submit events and 3 SQL statements per event), in Mach-II then developers would either write 30 components each with one method, or 10 components each with 3 methods. In either case, developers would be writing a substantial amount of ColdFusion code to manipulate variables, run a query, and return data. This code would be largely repetitive, but subject to developer experience, whims, and styles.

The MVC-PDF offers high reusability. For instance, in the above example, developers would configure prebuilt SQLModel and ConditionModel components of the MVC-PDF. The entire Web application is built without writing any components or methods. Writing of components is replaced by configuration, using XML files of two prebuilt components provided by the MVC-PDF.

Developers can write additional reusable components and plug them into the MVC-PDF to accomplish business logic that cannot be implemented with the available components. This approach eliminates the proliferation and management of large numbers of business components and methods.

Another aspect of the MVC-PDF is a single Hierarchical Data Structure (HDS) that is used by all MVC-PDF components to retrieve and store data, and is used by the presentation tier to extract and render dynamic data. This significantly simplifies the programming of CFM pages, because the developer works with a single data structure that contains all the data required by the presentation page.

The MVC-PDF is based on a design pattern for building large systems that consist of:

  • Assembling and declaratively configuring a small number of robust reusable components
  • Single hierarchical data structure that is used by these components to retrieve and store data, and used by the presentation tier to retrieve dynamic data and put into a CFM page

    What Is MVC-PDF?
    To understand the MVC-PDF and how it changes the development of Web applications, consider the examples shown in the listings that follow.

    In the first example, a Web page is submitted with <form action="index.cfm?event=getAllProjects&userid=2" method="post">. An event listener of the Mach-II framework is configured as shown in Listing 1. It specifies that the event getAllProjects will be processed by component com.indent.controller.IndentController and method execute. This method will call a business component specified as bc1=getProjectsForUser. After the business component is executed, control will pass to the projectDisplay view page. This view page is specified in the <page-views> element.

    Next, getProjectsForUser is specified in business-components.xml.

    
    <business-component-defs>
    	<bc name="SQL" type="com.indent.model.SQLModel"/>
    </business-component-defs>
    
    <business-components>
       <bc name="getProjectsForUser" type="SQL" parent="getAllProjects.bc1">
       	<parameter name="desc" value="Illustration of SQL business component"/>
       	<parameter name="db" value="DBAlias"/>
       	<parameter name="method" value="select pName as projectName from projects
                where uid=#hds.userid#" />
       </bc>
    </business-components>
    

    getProjectsForUser is specified as a component of type SQL with three parameters: description, database alias, and select statement. Notice, the userid is passed in from the URL and replaced in the select as #hds.userid#. hds is single data structure that contains all the URL parameters and data created by the business components. Business components of type=SQL are processed by the com.indent.model.SQLModel business component, as specified in business-component-defs; the attribute parent contains a reference to the Mach-II events.

    The SQL business components may contain select, insert, update, or delete statements. The output of the SQL statement will be stored in hds.getProjectsForUser, where getProjectsForUser is the name of the business component. In the following snippet, the output of select is stored in hds.getProjectsForUser.projectName.

    ProjectDisplay.cfm, the view page specified above will look like this:

    
    ...
    <table><cfoutput query="hds.getProjectsForUser">
    <tr><td>#projectName#</td></tr>
    </cfoutput></table>
    ...
    

    In Listing 2, the following example will be used: user is submitting a project name, task name, and task description that need to be inserted or updated into a database. If the project does not exist, then project and task are inserted in respective tables; otherwise they are updated.

    The submitted page looks like this:

    
    <form action="index.cfm?event=updateProject&projectName=Sample1&userid=2"
     method="post">.
    	<input type="text" name="taskName" value="Project kickoff">
    	<textarea name="taskDesc">Project Kickoff contains several
    	 activities</textarea>
    </form>
    

    The listener is configured for the updateProject event as shown in Listing 2.

    If any of the business components, bc1 to bc9, throws an exception, then all subsequent executions are stopped and a failure event is executed.

    Business components - bc1 to bc9 - inside <event-handler> element are executed in sequence, until one of the business components changes the sequence. The first business component is getProject, followed by checkSelectProject. As specified in the business-components.xml, checkSelectProject checks if getProject returned any rows. If true, then bc4, bc1, bc8, and bc9 are run. Otherwise, bc3, bc6, and bc7 are run. The complete flow is shown in Figure 2.

    Two types of business components are used in Listing 3: SQL and Condition. Compared to Listing 1, the SQL definition here contains the database alias and the class that will handle the exception. The database alias in definition removes the need to specify it in each <bc> element. However, if one is specified in <bc>, then it overrides the database alias parameter in the business component definition. By default, transactions are rolled back if any of the business components in the event fail.

    Each model component can be associated with its own exception class. If none is specified, then "any" exception is handled by the class com.indent.exception.AbstractException. An example of the exception class is provided in the next section.

    The Condition business component type provides an "if ... else if ... else" type of logic. If a condition is satisfied, then the corresponding bclist provides the list and sequence of business components to be executed next. The value attribute of the condition uses the same syntax as condition in <cfif>.

    Notice, URL parameter "projectName" is replaced in the where clause of the first select. The result of the query is stored in hds.getProject. In the next business component, checkSelectProject, #hds.getproject.recordcount# is used to find the number of rows returned. This business component has two conditions that correspond to the existence of projectName in the project table. If the recordcount is 0, then the sequence of business components is changed to the one contained in bclist1. Notice how URL parameters and values returned by getProject are used in the update statement in bc4 (updateProject) and others. hds, therefore, is the repository of all the URL parameters, form fields, and results of the business components that can be used in subsequent business components and in the output CFM page.

    If there are no exceptions, then the success event is called and it outputs projectDisplay2.cfm. A simple projectDisplay2.cfm will look like:

    
    ...
    <table>
        <tr><td colspan="3">List of Projects and Tasks for
    	 #hds.userid#</td></tr>
        <cfoutput query="hds. getTasks">
    <tr><td> #projectName#</td>
    <td>#taskName#</td>
    <td>#startDate#</td></tr>
        </cfoutput>
    </table>
    ...
    

    Model Components in MVC-PDF
    The pluggable aspect of a MVC-PDF allows business component developers to create custom model components that can be reused. A MVC-PDF provides an abstract class, com.indent.model.AbstractModel, from which all model components must extend. The AbstractModel provides the following facilities to a model component:

    • Access to bcParams object: This object contains the XML object of the model's business component. This is the XML contained inside <bc> ... </bc> in business-component.xml
    • Access to bcDefParams object: This object contains the XML object of the model definition. This is the XML contained in <bc-def>...</bc-def> in business-component.xml
    • Access to hds
    A developer of a business component can therefore access the configuration parameters as bcParams.method, bcDefParams.db, hds.variable. The developer can now focus on writing the business logic, while using all the configuration parameters and input variables (URL parameters, form fields, and results of prior business components). In return, the developer must put all the data he or she wishes to return into hds with the key as bcParams.bcName. As an example, the SQL model code is shown in Listing 4.

    Sample code for SQLException is listed below. session.hds.errormsg is a string with the message in <cfthrow > and any other messages specified in the SQLException component. This string is output in the exception.cfm page.

    
    <cfcomponent displayname="SQLException" hint="SQL Model Error Handler"
     extends="com.indent.exception.AbstractException">
    	<cffunction name="execute" access="public">
    		<cfargument name="errormsg" type="string" required="true"/>
    		<cfargument name="detailmsg" type="string" required="false" default=""/>
    		<cfset session.hds.errormsg = errormsg & "<br> SQLException" &
    		 detailmsg/>
    	</cffunction>
    </cfcomponent>
    

    The framework and the code presented here may be obtained from www.indent.org/cfmxdj.htm. Such reusable model components that are plugged into a MVC-PDF make the task of developing even complex Web applications a matter of configuration of business-components.xml.

    Hierarchical Data Sets in MVC-PDF
    A common data structure called hds (hierarchical data set) of type struct is shared by all the business components. hds is stored as a session variable, and the architecture is akin to a pipeline. This pipeline starts with a data structure that contains the URL arguments and submitted form field elements. If the URL contains parameters like ¶m1=value1¶m2=value2, then these are stored in a struct hds like: hds.param1 = value1 and hds.param2 = value2. These URL parameters are then available for use through #hds.param1# and #hds.param2#.

    If a business component is a SQL with a select statement, for example getProjectsForUser of illustration 1: select pName as projectName from projects where uid=#hds.userid#", then the records returned by the SQL are inserted into hds like:
    hds.getProjectFromName=query. Values of projectName may then be accessed as hds.getProjectFromName.projectName[i].

    The MVC-PDF provides a method for storing variables in hds that persist over the entire session. Keys in hds that start with "global_" are saved and persist for the entire session, while other keys in hds are destroyed after the display page is processed.

    Conclusion
    The MVC-based Plug-able Declarative Framework provides:

    • A library of reusable model components that are configured using XML files
    • A pluggable infrastructure that allows developers to build custom model components
    • A unified hierarchical data structure (HDS) for returning data from the middle-tier to CFM pages; HDS contains all the dynamic data required by the outbound CFM page
    The MVC-PDF should significantly simplify the development of scalable, maintainable, and robust Web applications.
  • More Stories By Pramod Jain

    Pramod Jain is president of Innovative Decision Technologies, Inc. (INDENT, www.indent.org), in Jacksonville, FL. Their clients include Recruitmax, NASA, and NIH. Pramod has a PhD from the University of California, Berkeley.

    More Stories By Yayati Kasralikar

    Yayati Kasralikar is lead programmer at INDENT. He has an MS from the University of Central Florida, Orlando.

    Comments (0)

    Share your thoughts on this story.

    Add your comment
    You must be signed in to add a comment. Sign-in | Register

    In accordance with our Comment Policy, we encourage comments that are on topic, relevant and to-the-point. We will remove comments that include profanity, personal attacks, racial slurs, threats of violence, or other inappropriate material that violates our Terms and Conditions, and will block users who make repeated violations. We ask all readers to expect diversity of opinion and to treat one another with dignity and respect.