Orchestration Wizard

Takes a bunch of commands and orchestrates a configured flow from them

The problem

Applications deal with flow of control of code. More specifically,in a web application, every user triggered action (such as a click of a button or an AJAX call or the request for a page) can be viewed as a flow. This flow is serviced typically by a controller which in turn delegates to various facades or other classes to accomplish the business logic that is enshrined in the flow requirements. Similar arguments hold good in the Middleware Layer as well. The facades for various services can be viewed as triggering flows individually.

These flows are typically within the same JVM or application server. As such, these should be viewed as micro flows. Micro flows can be distinguished from macro flows which may span across multiple enterprise wide components. Some of the salient features of micro flows are:

  1. Simple flow of control from one class to the other within the same JVM.
  2. Micro flows do not depend on user intervention. They may be triggered by user generated events but would not need user intervention to complete.
  3. Micro flows span the time that it takes to service a "request" to the system. Here request is defined by the type of the application. As mentioned above, in a web app a request may be a HTTP request that requires a HTTP response. In an EJB layer, this may be an RMI request that needs to be serviced by an RMI response.
  4. Micro flows end when a system services the request by providing a response. They don't span multiple requests.

Most applications are structured to implement these flows in a monolithic way. Individual classes impement business logic. One class invokes the other to implement the entire flow. Any change in the flow logic means changing this execution. As micro flows get complex, it makes sense to segment the functionality between components that provide the logic of the flow and those that orchestrate the flow. Thus there is a need for a special orchestrator which is able to call the correct set of components given a flow and accomplish the business logic that is required thereon.

Look at flow orchestrations for further notes.

Simple Commands

Commands implement the command pattern. They are simple and testable units of work that can be implemented by independent teams.

Chains

Implements the chain of responsibility pattern. A Chain is a series of commands that have been stitched together to execute in sequence. The chain terminates if one of the commands throws an exception or when all the commands have been executed.

Router

Routers help the flow of execution to route to one or other chains (or routers) depending on the contents of the Context.

Context

The Context object is the value object that gets passed in the command chain. It is mutable by the commands and is progressively enhanced.

Configuration

All commands and chains are configured using one configuration file. This supports a DSL implementation. Samples of the DSL are shown in the test cases.

Discovery

Several configuration files can exist with the same name (e.g.: META-INF/owiz.xml) in the deployment. They can get discovered and the contents would be merged to form a potentialy complex command chain. This facilitates modularization.

The Command pattern

The best way to segment a complex flow orchestration is to individually write pieces of application functionality in separate classes and "string" these classes together to form a complex flow. This "stringing" process is accomplished using OWIZ. However, the application itself has to provide the individual classes that implement the functionality.

The classes must implement the Command pattern. OWIZ strings them together using some configuration information and voila! we have the required functionality implemented. OWIZ ships with a "configurer" that uses XML based configuration. Other configurations can be trivially possible in OWIZ. In its previous incarnations, I had made a configurer that extended the Spring configuration. OWIZ leverages a bean factory like the spring bean factory and would work best with it.

An Example - TAXWIZ

Let us illustrate OWIZ's features with a web based tax application called TAXWIZ, that needs to compute tax for an individual. The tax computation flow, so to speak, would have specific nuances that need to be taken into account to compute tax. The base application may use the salary information to compute tax. There would be a SalaryCommand for instance that can utilize the salary information to compute tax.

There may be other commands such as RealEstateCommand and InvestmentsCommand. These would compute the tax from alternative sources of income (in addition or in lieu of salary) such as real estate income and income from investments respectively.

Plug In Functionality

OWIZ allows new modules to be "discovered". Module specific configuration files can extend the base configuration files to add module specific functionality for particular flows such as the tax computation flow for instance, in the above example.

This feature would be a great way to launch products that support plug in functionality. As the Real Estate module gets discovered, the RealEstateCommand would get plugged into the tax computation flow. OWIZ's ability to add the tax computation functionality piecemeal, allows TAXWIZ to implement this plug in functionality easily.

The Context Object

The context object encapsulates the information that has to flow from one command to the other. Context objects can be any kind of objects. OWIZ uses a "generic" for a Context object that allows it to support any kind of object. The context, typically, includes request specific information. It is advisable to keep information that is request agnostic (such as domain objects for instance) out of the context. Instead, it makes sense to utilize some other ways of accomplishing the caching of those objects (see Value Object Wizard (VOW) for example). TAXWIZ must not store income related to specific individuals in the context. It must leverage some cache to look up these domain objects.

There are three types of context information that are usually captured.

  1. Information that is used to implement functional requirements. TAXWIZ may include tax as a field in the context object since the tax field needs to be enhanced as the tax computation flow navigates across multiple commands.
  2. Information that is used to determine the correct "route" to take for executing the action. In the case of TAXWIZ, there may be a flag in the context object to indicate if the RealEstateCommand needs to be invoked or not.
  3. Information that is used to implement "horizontal" or non functional requirements. This may include security, privacy etc. This kind of information is usually stored as thread local variables but the context object can be leveraged equally effectively to provide this functionality. OWIZ commands can be easily enhanced to be decorated with "aspects" that leverage this information from the context.

AOP - Aspect Oriented Programming

Since the functionality is implemented in the form of commands that implement the same interface, it is possible to come up with decorators that implement the same Command interface and delegate functionality to AOP alliance like aspects. This is work in progress and would be implemented for future iterations. Meanwhile, the command is perfectly free to benefit from the AOP available from other IOC frameworks such as Spring.

Attachable Command

Attachable Command is an interface that is supported by OWIZ. An attachable command is a command that allows other commands to attach themselves to it. This provides an extensible way of controlling the execution of one command from another. Currently, the following attachable commands exist as part of the framework:

  • Chain: Chain is a command to which other commands can attach with an index that defines the order of execution. When a chain is invoked (just like any other command in the framework), it executes the attached commands in a sequence that is defined by the ascending order of the index. The index does not have to be contiguous.