Tree Oriented Perspective for Software Architecture and Design

Java Development News:

Tree Oriented Perspective for Software Architecture and Design

By Venkata Reddy

01 Jun 2004 | TheServerSide.com


Disclaimer

This is not a new framework to reduce your code, and by no means, a fully developed concept or a tool to be immediately used. At worst, its a wild thought, and at best, it might provide a new way to inspect your design from the view point of standard roles for each functional unit, independent of its level, resulting in a more modular and maintainable system.

The next few sections will introduce the "unit", the building block of this perspective, by describing its structure, roles and communications. Latter sections discuss a sort of case study and tool support.


Structure

Generally, an object can be assigned with a role by means of class inheritance or interface implementation. But how do we attach a role to a module, a subsystem or a method? How can I inherit a module or subsystem from the other? We need a means to express things in terms of role and organization at all levels inside a software system.

TOP looks at a software system as a hierarchical organization of units. Each node in the tree is a functional unit performing a well-defined and possibly standardized role. As a concrete form, a unit can represent an object, a method, a module, a layer, a tier, a subsystem, or an entire system.

A unit commonly has

  1. Input and output interfaces
  2. Local data and cached data (optional)
  3. Access to its peer units, inner units and container.
  4. Access to an external service provider for common concerns such as logging and security (optional)
  5. Mechanisms such as request queuing, caching, and resource pooling (optional)

Standard roles

A unit performs all the basic roles and a specific role as assigned by its container:

Basic roles

  1. Server, servicing requests from its peer units
  2. Container, managing a set of inner units
  3. User, sending requests to its peer units

Specific roles

These roles are assigned to a unit by its container, for its container

  1. Builder / Dismantler
    • An inner unit, which knows how to build or dismantle its container.
  2. Input controller / Output controller,
    • An input interface of a unit is a set of inbound requests for dispatching to its inner units. An inner unit designated as input controller will handle all the inbound requests.
    • An output interface of a unit is a set of outbound requests from its inner units for dispatching to its peer units and container. An inner unit designated as output controller will handle all the outbound requests.
    • An interface controller can talk to its peer units as wells as its container's peer units. This is an exception since interface controllers represent their container's identity.
    • Examples
      • HTML document loaded in a web browser.
      • Servlet
      • Front controller session beans
      • Data access objects
      • Input validation and internal routing logic inside methods of a class.
    • Common concerns
      • Validation of input
      • Invoking request handlers
      • Routing of request / responses.
  3. Request handler
    • Examples:
      • Shopping cart handler
      • Job scheduler
      • Search engine
    • Common concerns
      • Request validation, delegation and coordination of preparing the response.
  4. Serializer / Deserializer
    • Collaborates with state manager for transformation of the unit state into custom formats and vice-versa.
    • Examples:
      • Database import / export (application state)
      • Object serialization / de-serialization
    • Common concerns
      • Conversion between formats
  5. State manager
    • Manages access requests to persistent and non-persistent data from its peer units. Sometimes it could just be the state without a manager.
    • Examples
      • Data tier (data access components, database drivers and the database)
      • Entity beans (EJB)
      • User's http session data
      • Data local to object instances,
      • Variables with page level scope (JSP)
      • Data stored in cookies
    • Common concerns
      • CRUD operations
      • Managing concurrent access
      • Refresh at intervals
      • Security
      • Auto expiry/update
  6. Error handler
    • Helps request handlers and processors to handle error conditions encountered during processing of a request, in a unit-specific way.
    • Can also be seen as a common concern such as logging and security validation. In such cases, units do not maintain this role.

The terms controller, interface, handler have different concrete forms at different levels. A unit's communications are generally limited to its peer units, its container, its inner units and an external service provider for common concerns.


Communications

Units talk through their interfaces. An interface always means capability to handle a set of valid inbound requests or outbound responses or a combination of them, in a defined format, protocol and sequence, complying with a defined functional specification. An unit's output interface controller (dispatcher unit) can send requests to its container's peer units. Units in all other roles can only talk to their peer units.

A Unit recieves messages from two kinds of sources - its inner units and other units. Inner units talk to its container through the output controller of the container, mostly for sending requests for their container's peer units. The "other" units which are not inner, talk to a unit using its input controller.

Results for an synchronous call are returned to the caller through the input controller, while asynchronous results are routed through its output controller. The ouput controller is also used while processing synchronous calls, for involving its peer units. The return types could references to units.

Fractal nature

The most important aspect of this perspective is independence from scale and complexity of systems. The same principles can be possibly applied at any level. Such fractal characteristics are generally displayed by some natural systems such as topographic features like landscapes and rock formations. The large rock formations resemble the crystal structures at micro level. If encapsulation, inheritance, interfaces are too good at object level, they might be good at at other levels as well.

Also, application of standardized hierarchical role patterns in design could lead to defining sets of common concerns applicable to each role and identification of standard ways of addressing these concerns, and finally, standard ways for testing the units based on their roles.


Case study: Pet store application

I'm not trying to build the Sun's Pet store application using the TOP perspective here. This section attempts to identify the hierarchical roles in the application, by applying the description of a "unit", which was discussed so far.

As we all knew, the multi-tiered architecture for the Sun's J2EE Blueprints application is reflected in the following diagram.

Lets examine a mini laboratory model of Pet store - a multi-tiered java class! This is a really tiny model of a generic Petstore application, which allows us to apply TOP perspective to it, though on a tiny scale.

class PetStore extends Unit {
    // ------ Database / State ----------
    private ArrayList inventory = new ArrayList();
    private ArrayList orders = new ArrayList();

    // ------ HTML pages / Application front controller ----------
    public Response processRequest(ArrayList request) { // override of Unit's only method
        switch (getRequestType(request)) {
            case 0: storefrontHandler(request); break;
            case 1: supplierHandler(request);
        }
          return new Response(); // based on results
    }
    // ------ Web tier / Request handlers ----------
    private void storefrontHandler(ArrayList request) {
        switch (getRequestType(request)) {
            case 0: checkoutHandler(request); // checkout
        }
    }
    private void supplierHandler(ArrayList request) {
        switch (getRequestType(request)) {
            case 0: inventoryHelper(request); // add stock
        }
    }
    private void checkoutHandler(ArrayList request) {
        switch (getRequestType(request)) {
            case 0: request.add(0, "0"); // new order
                    storefrontDispatcher(request);
        }
    }
    private void storefrontDispatcher(ArrayList request) {
        switch (getRequestType(request)) {
            case 0: orderHelper(request); // add order
        }
    }
    // ------ EJB tier / Request helpers ----------
    private synchronized void orderHelper(ArrayList request) {
        switch (getRequestType(request)) {
            case 0: addOrder(request);  // new order
                    removeInventory(request);
        }
    }
    private synchronized void inventoryHelper(ArrayList request) {
        switch (getRequestType(request)) {
            case 0: addInventory(request);
        }
    }
    // ------ Data access / State manager ----------
    private synchronized void addOrder(ArrayList request) {
        orders.add(request.get(0));
    }
    private synchronized void addInventory(ArrayList request) {
        inventory.add(request.get(0));
    }
    private synchronized void removeInventory(ArrayList request) {
        inventory.remove(inventory.indexOf(request.get(0)));
    }
    // ------ Utility ----------
    private int getRequestType(ArrayList request) {
        return Integer.parseInt((String) request.remove(0));
    }
}

A discussion of various parts of the above model is in order. The HTML UI is represented by the only public method processRequest(), which means that this method is invoked by the unit "user" by populating the request with form data etc, such as -

// ------- User unit ----------
PetStore app = new PetStore();
// populate inventory using supplier interface
app.processRequest(prepareRequest(new String[] {"1","0","0", "Dog"}));
app.processRequest(prepareRequest(new String[] {"1","0","0", "Cat"}));
// place order using storefront interface
app.processRequest(prepareRequest(new String[] {"0","0","0","0", "Dog"}));

Ofcourse, I've omitted several details. And, most importantly, this does not indicate any implementation pattern for TOP based design

This micro-scale model did a poor job of addressing all the features needed for a Pet store application, but it tries to capture the essential role based division of work. EJB tier represents more generic and frequently used operations compared to Web tier. We could probably host these methods in a separate container with better performance and scalability because they are frequently used. Data tier's major concerns are concurrent access, security and performance. We made the data access methods as private (security), synchronized (isolation) and haven't done anything for performance right now.

It is interesting to note that multi-tiered partitioning, which helps to address the non-functional concerns such as performance, scalability, security, is relevant at micro level as well, and so is modularity. While the modular and multi-tiered partition is quite visible, an equally predominant view is role based hierarchical, recursive perspective (TOP). processRequest() represents the interface controller, while the request handlers and state manager (set of data access methods) are obvious in their roles.

There may be a small glitch in the Blueprints Petstore application. If you close the browser, your shopping cart is lost. Shopping cart can be stored as cached state for the application interface controller units (html pages), and TOP reminds you to about this concern during design of this unit.

If you noticed, the method storefrontDispacther() is the output controller for storefront unit. The storefront's inner unit checkoutHandler() talks to orderHelper() through storefrontDispacther(), because orderHelper() is not a peer unit for checkoutHandler().

Now its time for an interesting step - growing up by cell division. Spin-off separate classes (units) to handle roles of interface controller, request handlers and state manager. Each of these units will again have their own internal units (methods) performing the standard roles, just like the PetStore class had in its childhood days. The units can choose their own implementation strategy ' Java class, JSP page, EJB bean or even XML etc and their protocols to communicate. Following is the code for a little grown up Storefront handler unit.

class Storefront extends Unit {
    // ------ Unit state ----------
    private ArrayList userSession = new ArrayList();
    private ArrayList catalogCache = new ArrayList();
    private ArrayList shoppingCart = new ArrayList();

    // ------ Container unit reference ---
    private Unit container;

    public Storefront(Unit store) { // A Petstore object is passed
        this.container = store;
    }
    // ------ Front controller ----------
    public Response processRequest(ArrayList request) {
        switch (getRequestType(request)) {
            case 0: checkoutHandler(request); break;
            case 1: catalogHandler(request); break;
            case 2: shoppingCartHandler(request); break;
            case 3: signonHandler(request); break;
        }
          return new Response(); // based on results
    }
    // ------ Request handlers ----------
    private void checkoutHandler(ArrayList request) {
        switch (getRequestType(request)) {
            case 0: request.add(0, "0"); // new order
                    storefrontDispatcher(request);
        }
    }
    private void catalogHandler(ArrayList request) {
        switch (getRequestType(request)) {
            case 0: getCatalog(request); // view items in a category
        }
    }
    private void shoppingCartHandler(ArrayList request) {
        switch (getRequestType(request)) {
            case 0: addCartItem(request); // add item to cart
        }
    }
    private void signonHandler(ArrayList request) {
        switch (getRequestType(request)) {
            case 0: request.add(0, "1"); // authorize
                    storefrontDispatcher(request);
        }
    }
    // ------ Output interface controller ----------
    private void storefrontDispatcher(ArrayList request) {
        switch (getRequestType(request)) {
            case 0: container.getOrderHelper.processRequest(request); break;
            case 1: container.getSignonHelper.processRequest(request); break;
        }
    }
    // ------ State manager ----------
    private synchronized Object getCatalog(ArrayList request) {
        return catalogCache.get(catalogCache.indexOf(request.get(0)));
    }
    private synchronized void addCartItem(ArrayList request) {
        shoppingCart.add(request.get(0));
    }
    // ------ Utility ----------
    private int getRequestType(ArrayList request) {
        return Integer.parseInt((String) request.remove(0));
    }
}


Tool support

The concept is still in its infancy. While it gains momentum and gets refined through corrections, tool support would naturally follow it. Currently an XSL based viewer is available to visualize the perspectives. The tool, including its source code is available for download. The viewer renders an XML formatted perspective in a browser using XSL, Javascript and DHTML, allowing a user to navigate through the perspective. I'm also working on a Swing based full-fledged visualizer, with lot more features for working with perspectives. Following is a screenshot of the TOP Viewer loaded with a sample perspective for Petstore application.


Conclusion

This article does not tell you to start a project by writing a mini Java class and grow it up by cell division. Nor does it mean to say that almost the entire application could be built using XML files, except for the low-level leaf units, which need to be implemented in a non-XML programming language. It also doesn't mean that one has to use ArrayLists and switch case statements to implement a TOP based designs; it's just an example I chose to fit space constraints here.

The article does attempt to provide a new perspective on the architecture. Applying the perspective on existing applications might let you find missing roles and optimize the performance of standardized roles. The perspective will help new applications to be conscious of the standard internal roles and their hierarchical division. This reduces complexity of the design and provides high degree of modularity and encapsulation and fights against crisscross references between objects, Thus, we just tried to visualize a role based tree perspective that can be recursively applied using standardized roles that share common concerns.