|
|
Sample App : Understanding the Storefront Developer SampleFrom $1Table of contents
IntroductionAn online storefront is a web application that lets users browse through a retail catalog and buy products. It generally provides:
Through these common features, the storefront is also an important generic web application design pattern. It is widely used, and has been implemented on many major web platforms. Finally, the storefront is a data-driven application, requiring thorough development of all layers of a three-tier web application—client, middleware, and database. The Bungee Storefront developer sample shows you how Bungee Connect integrates these layers and provides a rich and compelling experience for both users and developers. This wiki entry is a guide to the Storefront application. You can try the latest deployed version at http://testdeploy.bungeeconnect.com/storefront.
You can also import the project as a developer sample to take a closer look at the source code, modify the code, or extend the application with new functionality. To do so, you need a Bungee Developer account, which you can create at https://builder.bungeeconnect.com. In this article, you learn how to do the following in Bungee Connect:
The Requirements for an Online Storefront
The Bungee StorefontBungee Connect provides an elegant way to meet the requirements of an online storefront. Bungee Connect lets you visually design the application's user interface, integrate it with application logic, and connect to data sources—with nothing but a web browser. Some difficult problems in most web application development projects, such as deploying efficiently, ensuring a responsive user experience, and guarding against unplanned downtime, are solved automatically through the Bungee Grid server architecture. Bungee lets you focus on high-level application design. The design of the Storefront application is outlined below, in four layers:
User InterfaceRich, Interactive AJAX InterfaceAJAX varies widely from technology to technology in terms of power and ease of implementation, and the two usually oppose each other. In Bungee Connect, complete interactivity is built into every control. Controls are bound declaratively to data, and updates—across controls, panels, and even browser windows—are fully automatic. Familiar UI Design PatternThe Storefront displays hierarchically structured data with a tree view on the left of the page and data and detail views to the right, implementing a common UI design pattern that many users have experience with.
The general pattern is common to many applications that let users browse large sets of data, including Windows Explorer and Bungee's WideLens reference application. Static and Dynamic Form InterfacesBungee Connect uses forms to visually represent classes. Each class can have many forms, and visual elements displaying instances of a class specify which form to use with a form interface. They can do this either statically or dynamically. With a static form, the elements that the form contains will stay the same, but their content will still update automatically if the class instance that the form is bound to changes, or if the binding changes to a different instance. For example, the Storefront uses a static form interface to display the product detail view. Each time a new product is selected, the view updates, but the view's controls and layout stay the same. With dynamic form interfaces, the form used to display an object is chosen at runtime. For example, the Storefront uses a dynamic form interface to display the tabbed product data view, letting the user switch between browsing by category and searching for products by keyword. The DynamicForm is bound to the currentTab element of MainClass. This element will have either an instance of SearchTab or of CategoryTab, depending on which tab index is selected, and uses a custom DynamicFormInterface to find the appropriate FormInterface. Both SearchTab and CategoryTab contain a DynamicFormAdapter, which interfaces with the DynamicFormInterface and specifies which form to use. The Storefront also uses a DynamicForm to toggle between the "Add to cart" button in the detail view and the "Remove from cart" button with an input controlling the cart quantity. In this case, the DynamicForm is bound to a ProductCartItem instance, and the corresponding DynamicFormInterface returns either the AddToCartInterface or a ChangeQuantityInterface, depending on whether the quantity is greater than zero. Modular UI DesignUI design is simplified by factoring out and re-using common controls. For example, Storefront's SearchTab and CategoryTab both use PaginatedProductList, which provides a form to navigate and view pages of products. Separating complex controls into smaller controls and re-using the parts has several advantages. First, it simplifies development by splitting difficult tasks into more manageable modules. Second, it makes applications more consistent, since the same functionality is always provided by the same controls. Lastly, control re-use makes applications easier to modify, since it usually ensures that a single modification won't have to be implemented in more than one place. CustomizationBungee Connect offers many ways to add custom functionality to controls through its Model-View-Interface UI architecture. In this model, each control is bound to an underlying data field with a control interface. A TextBox, for example, uses a control interface to connect to a string field representing its text. Many controls have interface settings in addition to the one connecting them to their main data source. The drag-and-drop interface, for example, works by defining a custom function interface for MultiColumnList's Drag Function propery, and implementing a method that handles the drag. The custom drag function, ShoppingCart.productDrag(), compares each new item with the existing items in the cart. If an item is already present, it increments the cart quantity instead of adding it again. Support for multiple selection and dragging is enabled by setting the corresponding MultiColumnList properties to true. Core drag-and-drop functionality, customized or not, is specified declaratively using named drag and drop zones. In addition, the shopping cart interface takes advantage of Bungee Connect's cross-window drag-and-drop functionality. The cart is displayed as a pop-out dialog, and products can be added by dragging them from the main window's data view. The cart items can also be reordered by dragging them past one another, or by sorting the cart using MultiColumnList's default sort provider. Clicking a cart item displays that item in the main detail view where the quantity can be changed and the item removed. Items can also be removed from the shopping cart with the keyboard's Delete key, implemented with a custom function interface for the MultiColumnList's Remove property.
Dynamic Data LoadingThe Storefront implements dynamic data loading in three ways:
Many Bungee controls support lazy evaluation, which means properties are evaluated only when they are used. Lazy evaluation makes efficient data-binding easy. For example, the TreeView in the Storefront dynamically loads subcategories when a category is expanded. The underlying DynamicCategoryTreeItem simply defines a TreeItemAdapter with a list property. The code for list (see screenshot) is called only when the item is expanded; it queries the database to get that item's children. Bungee Connect also automates caching; the list property is only called the first time an object gets expanded, or after its children have expired from the cache. Lazy evaluation is one of the features that give the Storefront scalability. By only loading the data that's actually displayed, the application supports an arbitrary number categories.
The Storefront loads product data dynamically as well. The data views (for category browsing and keyword search) are loaded one page at a time. Sorting and pagination happens in the data layer, minimizing the amount of bandwidth that the middleware-database connection needs as well as the resources used by the middleware. The data view is implemented by the PagedProductList control, which loads data through a data connection (an instance of StoreDataWrapper, taken from MainClass) and displays it in a MultiColumnList control. The MultiColumnList control has rich data binding options that let you easily specify sorting, drag-and-drop, and some keyboard functionality (such as handling the Delete key). This works by simply setting the MultiColumnList control's Sort, Drag Function, and Remove interfaces, respectively. The product data view uses a custom sort function. The shopping cart, on the other hand, uses custom drag and remove functions. Function interfaces are defined at the solution level. In this case, they include PaginatedSortInterface, ProductDragInterface, and RemoveCartItemInterface, which correspond to the MultiColumnList control extensions described above. Functions, through their interface list, can "listen" to the events that the interfaces correspond to. In the future, interfaces may be able to specify a method signature. The MultiColumnList control's "sort" event, for example, passes a sort field and sort direction to the functions that connect to it—but PaginatedSortInterface can't specify this yet. At the time of this writing, function interfaces serve, from the developer's point of view, as identifiers that connect functions to events.
Lastly, the Storefront loads product details dynamically whenever a product is selected from the data view. Details are cached programmatically, using ProductItems' productDetailCache field. Planning the data access patterns that user interaction creates, to load as little data as possible, and never in large chunks, has lots of advantages. First, it keeps the user experience responsive. Loading data only when needed, instead of at start up, reduces load times. Second, dynamic loading saves database bandwidth and server resources, which translates into saving Bungee Units. Finally, limiting the amount of data that gets loaded at a time, combined with scalable database design, allows your applications to stay responsive no matter how large the underlying database becomes. Efficient data loading is already made easy by controls such as the TreeView, which uses lazy data binding and automatically caches the results. Other features in the data and detail views use Bungee functionality like custom sort interfaces to implement efficient data access. Even more dynamic data loading and caching automation is on the way! Application LogicThe application (or business) logic defines the functionality that a web application provides. It is part of the middleware layer, and interfaces with both the data wrapper and the user interface. The Bungee Storefront has three main functions: searching for specific products, browsing the product catalog, and saving products in a shopping cart. A real storefront application would also be able to persist shopping carts between sessions, manage user credentials, and interface with external payment sources. This functionality, however, would add complex components outside of Bungee Connect, and is beyond the scope of the Storefront sample. Shopping Cart FunctionalityManaging the shopping cart requires some simple application logic. This logic is encapsulated by the ShoppingCart class, which contains a collection of ProductCartItems. The ProductCartItem class, in turn, is a wrapper around ProductItem that adds a cart-quantity field, as well as methods for changing the quantity or adding or removing that item from the cart. Together, these classes provide an interface that ensures:
Search FunctionalityThe search functionality is implemented as an SQL stored procedure, at the database level. Search compares each product's name and brand to the search string, looking for exact matches as well as words that sound similar. For example, a search for "P-51" will return the "P-51 Mustang" product, and a search for "braddavehobies" will return all products from "Brad & Dave Hobbies." Products can be both searched and browsed by category. The search function provides an option to return items only from the currently selected category, or from all categories. Data WrapperThe data wrapper provides a high-level Bungee interface to the data layer. It handles all communication between the Bungee layer and the underlying data source—for the Storefront it's a Bungee-hosted MySQL instance. The data wrapper is responsible for managing network connections. In this case, the wrapper is connecting to a relational database, which can only return data in rowsets. The wrapper has to turn this "flat" data into hierarchical objects. In other words, it has to bridge the "object-relational impedance mismatch." Centralizing data access in one place with a data wrapper has several advantages:
The Storefront data wrapper is implemented by the StoreDataWrapper class. Switching, for example, between a SOAP service and a direct database connection requires only a few changes in this class. You can switch from one database to another by changing a single method—init(), which defines the database connection information. Data Connection SupportBungee has several tools that make it easy to interface with data sources. Because of this, these tools let you quickly create a data wrapper object. They include embedded MySQL hosting, SQL connection projects, and, for web services, automatic WSDL importing. Bungee's SQL projects let you:
Together, these let you manage databases, define their schemas, load data, test queries, and so on. Currently, Bungee Connect can connect to MySQL and PostgreSQL. The Storefront application uses MySQL. This tutorial explains how to create Bungee database connections.
Querying an SQL DatabaseBungee has an SQL API that lets you connect to and query a database programmatically. The API handles all type conversions from SQL's primitive types to Bungee's automatically, which facilitates fast development. The API is concise and focused on the SQLConnection class. SQLConnections provide three main methods: login(), which takes a Config object that defines the database connection; logout(), which closes the connection; and query(), which executes an SQL query and returns a RecordSet object. You can query a database in two main ways: with a direct SQL SELECT statement and by calling a stored procedure. SELECTs have the advantage of simplicity, but stored procedures have some important advantages as well:
With stored procedures, the database itself can provide more than just a collection of tables; it can provide an API—a collection of methods that define the logical purpose that the data layer serves. The Storefront uses procedure-based database design. For comparison, the getCategory() and getSubcategory() methods of StoreDataWrapper have been implemented in both ways—using simple SQL statements and calling the stored procedures. Simply uncomment one connection.query() call and comment out the other to switch between the two ways of retrieving data. DatabaseThe Bungee Storefront uses a MySQL database with specific engines (MyISAM and InnoDB) for each table—this optimizes each table for the way in which it's most often accessed. Bungee provides hosted MySQL functionality, which makes it easy to create, browse, and modify a database at design-time. The databases are also hosted alongside the middleware, which ensures low connection latency between middleware and database servers. The Storefront database schema is outlined as follows:
Additional Resources
Tags:
|
|||||||