The Data Storefront "A Data Access Layer Framework"

Geoffrey Slinker

March 24, 2006 v1.5

October 20, 2005 v1.4

August 10, 2005 v1.3

August 8, 2005 v1.2

August 7, 2005 v1.1

July 31, 2005 v1.0

 

Accesses: 
Maverick Development

Introduction

This is a framework for organizing a Data Access Layer (DAL). This example is implemented in Java.

It is generally accepted that a DAL can benefit a tiered software architecture. How to organize a DAL is not commonly discussed. The Data Storefront framework is a way to organize a DAL. It removes duplicate code that is typically found in many DAL implementations and provides a metaphor or way to think about the internals of the DAL and where components should be placed.

Intent

The intent of the Data Storefront Framework is to provide a concept/framework of how to lay out your DAL components. The use of the storefront metaphor is to help the software developer understand where new code and logic fits into your DAL.

Motivation

The motivation for making the Data Storefore Framework was to encapsulate all of the low level specifics (such as Sql Database specifics) so that the writer of the DAL would not be concerned with the underlying implementation. Removing duplicate code, as found in many DALs, was an important motivating factor. Such duplicate code is managing the connection, generating the Sql statement or command, executing the query, and using transactions.

Figure 1 shows a typical three tier system. The business tier intefaces with the data access tier typically through subcomponents with public interfaces or a fascade on top of these subcomponents. Each subcomponent has its own connection code, query execution code, and transaction code. This creates a lot of duplicate and similar code.

Typical Tiered System

Figure 1

Figure 2 shows the same system as before with the addition of data storefronts. These storefronts, through the framework, share the same code for connections, query execution, and transactions. This eliminates a large amount of duplicate code found in many DAL implementations.

Storefront DAL

Figure 2

Application

Use this framework when you want to organize your data access layer. This tutorial is specific to accessing Sql databases. The abstract concepts of this framework can be used for any request/response system that can be described with a storefront analogy. For example: I have implemented the data store front on top of XML data files using XPath queries.

The Storefront Metaphor

An example of a storefront can be found in an automotive parts store. Ignore the items for sale that you can get yourself. What we are interesting in are the items behind the counter. The area behind the counter where the items are stacked on rows and rows of shelves, that area is the store. The counter and the clerk with whom you interact is your interface to the store, or what I call the storefront.

The storefront is where you make your requests. The clerk retrieves the items you request. You are not allowed to go into the store (shelves) and get the items yourself. As a matter of fact you don't know or even care if the items are stored on shelves or in boxes. All you know is that you make a request of the clerk and he executes the request. While he is in the store you don't know what he does, how he does it, nor do you care.

The clerk does care about the policies of the store. There are tasks that must be done and they must be done in particular order. In an automotive parts store the clerk must be trained in the procedures and policies of the store. After such training the clerk is trusted to interface with customers and to work within the store.

The metaphor, like any used in software development, only takes us so far. This metaphor is extremely powerful and provides us a framework for implementing request/response systems. The following implementation of the storefront metaphor provides a means of accessing an SQL database without exposing the fact that the store is an SQL database. Remember that what follows is a framework. The framework provides you a place to put your queries, query result handlers, connection information, and transaction information.

Getting Started

Note that this example is developed in Java 1.4 and is implemented to work with MySQL. This implementation was written using Eclipse on Mac OS X. I use the driver "org.gjt.mm.mysql.Driver".

Be aware of these naming convetions:

The first thing you will need is a Data Store. The Data Store provides part of the behavior of the metaphorical clerk. One of the things the Data Store will do is maintain the connection information. Frameworks give you a way to do things. By using this aspect of the framework you will have all of your database connection information encapsulated into a set of well defined classes. Here is an example:

public class MySqlDataStore extends BasicSqlDataStore { 
	/** 
	* 
	*/ 
	public MySqlDataStore() { 
		super("jdbc:mysql://localhost/test","root", "password"); 
	} 
}

A Data Store is not very interesting on its own. A Data Storefront is needed. Data Store and Data Storefront complete the clerk and counter in the automotive parts store metaphor.

public class MyStoreFront extends BaseSqlStoreFront { 
	/** 
	* 
	*/ 
	public MyStoreFront() { 
		super(new MySqlDataStore()); 
	} 
	/* (non-Javadoc) 
	* @see www.skunkworkssoftware.com.dataaccess.store.sql.BaseSqlStoreFront#GenerateRequest(int, java.lang.Object[]) 
	*/ 
	public AbstractRequest GenerateRequest(int requestType, Object[] objs) { 
		return null; 
	}
}
			

With the Data Store and Data Storefront in place you will now want to query your store, or in terms of the metaphor, approach the counter and ask the clerk to get something for you. This framework gives you a logical place to put your queries; it does not provide them for you. This allows you to have all of your "query code" in places that logically fit the framework.

A query is a Strategy. The idea of a strategy (in computer science) is to define a family of algorithms, encapsulate each one, and make them interchangeable. That is the very idea that is behind the AbstractStrategy.

So now you will need a Strategy, and here is an example:

public class MySqlStrategy extends BaseSqlStrategy { 
	/** 
	* 
	*/ 
	public MySqlStrategy() { 
		super(); 
		this.query = "select * from test"; 
	} 
}

Notice that this Strategy is a simple "SELECT *" from my "test" table in my database. You will need to substitute this query for one meaningful for your database or create a "test" table.

As you can image there would be many queries you might need for your database. There will probably be many tables in your database. Each query will be different and the results of each query will require different processing and parsing of the results.

The different ways to process a Strategy are specific to the underlying means of communicating with a database. This example is based on JDBC and ran with a driver for MySQL. The "way" you want to process a Strategy is by specifying the Request. The Request encapsulates the specific way in which to communicate to the database. Here is an example Request:

public class MySqlQueryRequest extends BaseSqlQueryRequest {

	/**
	 * 
	 */
	protected MySqlQueryRequest() {
		super();
		// TODO Auto-generated constructor stub
	}
	
	public MySqlQueryRequest(AbstractStrategy strat) {
		super(strat);
	}

	/* (non-Javadoc)
	 * @see www.skunkworkssoftware.com.dataaccess.store.sql.BaseSqlQueryRequest#GenerateParseDelegate(www.skunkworkssoftware.com.dataaccess.store.AbstractStrategy)
	 */
	protected BaseSqlParser GenerateParseDelegate(AbstractStrategy strategy) {
		BaseSqlParser parser = null;
		
		if(strategy.getClass() == MySqlStrategy.class) {
		    parser = new MyRequestParser();
		}
		
		return parser;
	}

}

Recall what was previously stated, "Each query will be different and the results of each query will require different processing and parsing of the results." The Request handles the processing. In this case the processing algorithm is from the class BaseSqlQueryRequest. The parsing the results of the request are specified by making a ParseDelegate. You will usually subclass some base Sql request class so that you can then specify each Strategy's specific parsing needs.

Here is an example of a "Parser":

public class MyRequestParser extends BaseSqlParser {

	/**
	 * 
	 */
	public MyRequestParser() {
		super();
		// TODO Auto-generated constructor stub
	}

	/* (non-Javadoc)
	 * @see www.skunkworkssoftware.com.dataaccess.store.sql.BaseSqlParser#Parse(java.sql.Statement, www.skunkworkssoftware.com.dataaccess.store.AbstractStrategy, java.lang.Object)
	 */
	public Object Parse(Statement dbStatement, AbstractStrategy strategy, ResultSet resultSet) {
		
		StringBuffer stringBuff = new StringBuffer();
		
		try {
			while(resultSet.next()) {
				int columnCount = resultSet.getMetaData().getColumnCount();
				for(int i = 1; i <= columnCount; i++) {
					stringBuff.append(resultSet.getObject(i).toString() + " ");
				}		
			}
		} catch (SQLException e) {

		}
		
		return stringBuff.toString();
	}

}
		

A Strategy usually specifies an Sql statement. Since the "SELECT" portion of any Sql statement can be different you must parse the results of each query differently. That is what the Parser handles, the specific needs for each Strategy.

Putting it all Together

Here is what you should do to "use" the Data Storefront:

public class MainClass {

	public static void main(String[] args) {

		AbstractStoreFront storeFront = new MyStoreFront();

		AbstractStrategy strat = new MySqlStrategy();
		AbstractRequest req = new MySqlQueryRequest(strat);

		try {
			Object results = storeFront.Execute(req);
			System.out.println(results.toString());
		} catch (Exception e) {
			e.printStackTrace();
		}
	}
}		
		

First thing you will want to do is figure out which Storefront you will need. Then you will determine which Strategy you need and how you want it processed. Then you will ask the Storefront to execute your request.

Notice how the classes build upon each other. A Storefront depends on a Store. The Store is typically encapsulated inside of the Storefront. A Request needs a Strategy. To execute something the Storefront needs a request.

Things to Consider

When you first use this framework based on the Storefront metaphor you will have to write several classes: a Store, Storefront, Request, Strategy, and Parser. After you have the Store, Storefront, and Request classes are in place you will find that you don't have to be concerned with their functionality anymore. You will find that from that point forward you will spend your time writing new Strategies and new Parsers.

Advanced Storefront Behaviors and Classes

The storefront metaphor only takes us so far. We don't want to restrict the "computer world" storefront just because the real world storefront in an automotive parts store can't do everything that can be done in software.

One of the extensions to the metaphor is the idea of a Token. The Token helps model the computer realm and specifically the need for connections and transactions. Loosely you can think of a token like this. Suppose you go to the counter and ask the clerk for several items from the store. You give your list of requests to the clerk and the clerk gives you a token in return. Then you wait. Finally the clerk returns with several items. You present your token. If all of the items were found by the clerk then you can commit the transaction. If items are missing you can rollback the transaction. The Token holds the necessary information to do these things.

Here is an example on how you would use a transaction:

AbstractStoreFront storeFront = new MyStoreFront();
AbstractStoreToken customerToken = storeFront.GetToken();
customerToken.getConnection().Open();
customerToken.AddTransaction();

Object obj = null;

try {

	AbstractStrategy strat = new MySqlStrategy();
	AbstractRequest req = new MySqlQueryRequest(strat);

	obj = storeFront.Execute(req,customerToken);
	customerToken.getTransaction().Commit();

}

catch(Exception ex) {
	customerToken.getTransaction().Rollback();
	throw ex;
}

finally {
	customerToken.getConnection().Close();
}
		

First instantiate a Storefront. Then from the Storefront get a Token. Through the Token open the Connection. After the Connection is open add a Transaction to the Token.

If you want to perform several requests then you can get a Token from the Store Front and open the Connection and leave it open until all of the Requests have been made.

AbstractStoreFront storeFront = new MyStoreFront();
AbstractStoreToken customerToken = storeFront.GetToken();
customerToken.getConnection().Open();

Object obj = null;

try {

	AbstractStrategy strat = new MySqlStrategy();
	AbstractRequest req = new MySqlQueryRequest(strat);
	obj = storeFront.Execute(req,customerToken);

	strat = new AnotherSqlStrategy();
	req = new MySqlQueryRequest(strat);
	obj = storeFront.Execute(req,customerToken);

	strat = new YetAnotherSqlStrategy();
	req = new AnotherSqlQueryRequest(strat);
	obj = storeFront.Execute(req,customerToken);
}

catch(Exception ex) {
	throw ex;
}

finally {
	customerToken.getConnection().Close();
}
		

The Token exposes some concepts that are important to the Storefront. It also exposes that fact that a Token can not be added to a Token until a Connection has been opened. I had thought about making this more transparent and then decided it is better to have things work explicitly, especially for advanced operations.

If you want to execute a Request in a Transaction you can let the frame work do the work for you. It would be done like this:

public class MainClass {

	public static void main(String[] args) {

		AbstractStoreFront storeFront = new MyStoreFront();

		AbstractStrategy strat = new MySqlStrategy();
		AbstractRequest req = new MySqlQueryRequest(strat);

		try {
			Object results = storeFront.ExecuteTransacted(req);
			System.out.println(results.toString());
		} catch (Exception e) {
			e.printStackTrace();
		}
	}
}		
		

If you have a Strategy that requires a Transaction before you request its execution then you can set a flag (needsTransaction) in the class like this:

public class MySqlStrategy extends BaseSqlStrategy {

/**
*  
*/
public MySqlStrategy() {
	super();

	this.needsTransaction = true;
	this.query = "select * from test";
	}
}		
		

By setting this flag other users will become aware that the strategy needs a transaction because during the execution of the request there will be an exception thrown.

Why Use the Data Access Storefront?

Use the storefront framework to organize your data access code and where you want to model request/response behavior. This example is concerned with a store that is "backed" by an Sql database. I have used the same abstract classes from the storefront to implement a store that was "backed" by XML files.

Often we feel it is necessary to create a Data Access Layer (DAL). However little is said on how to organize this layer. Typically the DAL is implemented by dumping all of the database code into a package or library. Little is done to organize the code. Typically this dumping of code results in lots of duplicate code that is never cleaned up. Because many DALs are implemented in such a poor fashion many people can't understand the benefits of a DAL and honestly I don't blame them for questioning their usefulness. This framework addresses the organization of a DAL.

The Java source is made available. If you use the ideas or concepts all I ask is that you reference me: Geoffrey Slinker.

Down load and use the tutorial code at your own risk. I am not responsible for anything resulting from downloading this file and make no claims concerning the use, quality, or intent of the software, the file, or anything.

If you would like a version that would be appropriate for business use please contact me.

A Note about Metaphors

During software development metaphors can be very useful by helping conceptualize abstract ideas. I have found that making a framework for request/response systems to be helpful. This framework was specifically created for Sql database access. During the creation of the framework I generalized the request/response behavior which lead to the storefront metaphor.

As a note of caution don't let metaphors overpower the implementation. We do many things in software development that are common to "us developers". These common things can be used to extend and empower our metaphors. Design Patterns are included in the "common things us developers use." If you find yourself saying, "We use the automotive store as our metaphor. In an automotive store you can not replicate all of the parts inventory. Therefore I refuse to add a replicate method to my Storefront class" then you are letting the metaphor overpower everything to the detriment of the possible capability of the system.

About the Author

Geoffrey Slinker received his Masters Degree in Computer Science from Brigham Young University.

Please feel free to contact Geoffrey at:

geoffrey_slinker@yahoo.com