Technical Articles by Sony Mathew, Software Architect, Twin Cities, MN


Monday, January 30, 2012


State Driven Design (SDD)

State Driven Design

Object-Oriented Design has been a powerful problem solving paradigm for many years now and it still continues to be a force, but as practiced traditionally it has also been limited in many ways. Any Architecture attempting to distribute objects across multiple nodes and processes while still trying to maintain object semantics soon encounters a scalability nightmare. The reasons for these limitations are a lack of constraints on object design around encapsulation and composition. Traditional Domain Driven Design builds on the principles of Object-Oriented Design and inherits some of these same limitations.

Traditional Object-Oriented Design principles work best when confined to the boundary of the local application context. Objects like String, Number, Lists, Maps, Trees and Graphics are represented well as fully encapsulated objects for consumption within a local application context. These objects are not meant to be distributed and scaled beyond one application process or even one user at a time in some cases. Scalability across many servers spanning many concurrent users requires a different design paradigm altogether.

Scalable object design can be achieved by softening some of these traditional object design constraints that impair scalability and replacing them with new constraints that harden scalability. We can achieve this in part by applying the constraints of REST atop a loose form of object design - REST being a proven scalable architecture of the World Wide Web. I call this design approach State Driven Design (SDD).

Let us start with some traditional object design and apply a few of these key constraints to see where we end up. We'll start with a truly old school object design around an Account.  We don’t see this type of traditional design much in practice any more due to its impracticality.

[Constraint] Separate Caller-Visible State

Applying this first constraint requires that all state a caller of an operation may inspect or modify should be separated into its own state object, while all operations that fulfill the needs of the caller be separated into a service. Caller-visible state is state which the caller must see, understand, and modify to interact with the system correctly. Its important to make a distinction between caller-visible state and internal implementation-specific state which the services still encapsulate and is of no interest to the caller.

Applying this constraint:

State objects should be lightweight, transportable and reconstitutable across the network if necessary without changing its value. Operations that formerly acted on caller-visible properties get separated into service objects that are now parametrized with state objects when necessary. Once a caller obtains a state object from the service, the caller should be able to inspect or modify the state independent of the service, additionally the caller must be able to construct a state object independent of the service.

Callers of operations are naturally other operations that should also be refactored in the same fashion, reverse-recurse outward until reaching the outermost operations acting on behalf of the external actor(s).

[Constraint] Caller-neutral Services

Services should not be caller-specific, they should be caller-neutral and be accessible to any number of concurrent callers, conversely a caller should be able to call any instance of a specific type of service at anytime. If caller-visible state has been externalized as dictated by the previous constraint then it can now be provided to the service as parameters instead of the service maintaining state on behalf of a specific caller. In the case of the AccountService example, it now expects an Account as a parameter and no longer assumes a specific Account or Customer.

This caller-neutral nature of a service is an important scalability measure. Services can now be replicated across nodes as necessary to accommodate varying caller loads.

[Constraint] Uniform Service Operations

We have already achieved a reasonable amount of scalability with just the previous constraints and could stop there. The Uniform Service Operations constraint takes it a step further and provides the framework for cross-cutting concerns such as security, and improved scalability through proxies and caches and in general the ability to nest and chain services to separate concerns.

This constraint requires that all services conform to a uniform set of operations. This is generally accomplished in two parts, first generalize the methods to the universal set of operations, then generalize the parameters and return values to a basic request-response model. Operations that no longer fit the service need to be organized into new states and services as shown in the example.

Applying constraints thus far (and others not yet described):

As you can see, AccountService offers a generalized set of operations which for simplicity's sake were kept to just the basic CRUD operations (create, read, update, delete) but this set can be be expanded to include other types of generalized operations. Also notice as a consequence, use-cases such as withdraw, deposit, and transfer no longer fit and needed to be refactored out to new states and accompanying services.

These generalized operations can now be monitored, proxied and secured in a generalized way e.g. an actor might be able to read AccountTransactions within the system but not update or delete them. Additionally, a generalized request-response model allows for states to be cached for requests that allow as such.

[Constraint] Hold References by ID

If a state object has an accompanying service to manage it then objects should reference this state by its ID and retrieve the state as needed on demand via the service rather than maintaining long-lived object references to the state object. These IDs can be database IDs or other forms of persistence IDs, or they could be UUIDs or GUIDs, or they could even just be plain timestamps or process scoped IDs for short-lived non-persistent state objects. Dependent state objects that don't require services to manage them may be embedded directly within their parent states as object references to form aggregates and managed by the aggregates' services.

This constraint goes a long way towards facilitating distribution and replication of services across many nodes. It allows state to be retrieved on demand in the most efficient and scalable way, be it remote retrieval or a local cache look-up.

[Concern] Business Logic

In all this CRUD one may be confused as to where Business logic would live. They live in the CRUD operations of course! Each state object represents a domain concept and its accompanying service represents how that domain will be managed and processed, hence it must also execute the business rules associated with this specific domain.

For example business rules associated with transferring funds would reside in the update operation of the AccountTransferService since the AccountTransfer state represents the domain around the transfer use-case. These rules may further be distributed across finer services that manage finer aspects of this domain, for example AccountTransferService will likely call on the AccountTransactionService to apply withdraw and deposit transaction rules who in turn will likely call the AccountService to apply the rules around updating an Account.

Additionally, uniform service operations allows for separating concerns by decorating and nesting services of the same type. For example, one could have 2 implementations of an AccountTransferService nested within each other, where one is solely responsible for business rules around transfers while the other solely responsible for its persistence. This nesting would be encapsulated from the caller.

[Concern] I/O optimization

You can choose to decompose state objects into finer and finer states each with their own services to manage them, where the outermost state and its accompanying service might represent a top-level use-case while the smallest state and service might be handling the persistence of a line-item. Now such a fine decomposition may not be optimal for persistence or other I/O concerns depending on your system, and batching or coarse-grained I/O operations may be required, If this is the case you should embed the fine-grained state objects directly as object references into their parent states to form coarse-grained aggregates and scrap the fine-grained services altogether, then optimize I/O directly within the aggregates' services managing aggregate states with batched I/O operations.

Additional I/O concerns may also exist around searches and bulk updates. Though not shown here explicitly, one could extend the universal operations of CRUD with overloaded variations of read and update to accommodate search and bulk updates. For example one could add operations like read(AccountFilter) or update(AccountFilter).

[Conclusion] Applying State Driven Design

For a given actor and use-case the designer of a State Driven Design (SDD) model looks to capture the state the actor would be interested in examining prior to execution of the use-case followed by the capture of the transformed state after execution of the use-case. It is important to capture the pre and post states of the use-case execution purely from the perspective of the actor who will examine these states.

Next create a CRUD service to manage this state. How this service manages or transforms this state internally should be encapsulated from the actor who only cares about the final state that represents the completion of the use-case. Now, apply this rule recursively, the newly created service now becomes the new actor (or caller) requiring the use of finer-grained states and services.

In essence, SDD is about modeling states and their transformations by the System.

[Conclusion] Applying Object-Oriented Design

Finally, apply traditional Object Oriented Design rules of generalization and composition to state and service objects independently to eliminate repetition of properties and operations respectively. Pull-up interfaces and abstract base-classes to generalize commonality between states and between services. Decompose complex states and services into finer-grained states and services then manage dependencies between services through Inversion of Control (IoC). As a final note, once in a while when the problem dictates as such, don't be shy about creating fully encapsulated old-school objects for local application consumption ;-)


Tuesday, April 27, 2010


Checked vs. Runtime Exception

This age old argument has been raised again by many recently and like them I decided to stamp my opinion out on the blog-o-sphere as well. I picked up where Cedric Beust left off.

I used to be a proponent of checked exceptions and had strongly stood my ground for a long time but I am now older and more forgiving. Whether an exception is recoverable or Not is mostly contextual. One call path may decide to recover while another may decide to fail from the same exception just as they might decide independent courses of action based on a return value.  An exception should only be a vehicle for information and abstracted as such (which is why I think interfaces for exceptions should have existed) while allowing callers under different contextual paths to process the exception as they choose best.

Additionally, many classes are implementations of one or more interfaces. We don't always have control over interfaces we implement and even when we do we should treat them as immutable as much as possible - mostly because interfaces exist to adapt various implementations to a general contract. When conforming an implementation to one or more general contracts - checked exceptions just cannot be declared consistently and in most cases not even possible. Given this and and the contextual nature of exceptions - it follows naturally that we only need the RuntimeException - however the traditional meaning of the RuntimeException will no longer be valid under this new paradigm - instead it gets redefined as merely a vehicle for Alternate information.



Wednesday, January 20, 2010


GWT + JEE Blueprint


Update: Java Source available at http://code.google.com/p/xsonymathew/

GWT is a true gem in the world of open-source slosh where most products are shiny gimmicks with little or no added value.  GWT accomplishes this mind-boggling feat of compiling Java source including much of Java's core classes into JavaScript/HTML perfection.  JavaScript/HTML being this world of icky sludge that many (like me) would rather avoid stepping into with our tidy feet, maintaining our elitist staticky Object-Oriented (OO) attitude to everything while building great Rich Internet Applications (RIAs) that can out-rich most other RIAs.  We can now use our IDEs to navigate and refactor across our entire tidy codebase and actually maintain our Apps for the long-term across a large team.  We can now apply a more uniform OO design across our entire application as sharing of Java source across tiers is now possible.  Componentization of UI into reusable widgets is now just a matter of OO design.

Of course, Its not all peaches and cream with this hot and sexy GWT - waiting for GWT to finish can shrivel up most anybody's confidence.  I outlined some of the issues with GWT in an earlier Article - the biggest being the relatively long compile waits - something you learn to compensate for in other ways.  Also to the contrary, you still need to understand HTML and CSS pretty well to use GWT effectively.

Outlined are some of the key Concepts and Stereotypes we employ across our GWT and JEE Application.  Code samples and Diagrams are shown after the outline.

Inversion Of Control (IoC)

IoC is a concept we employ across our JEE Application including within our GWT tier for maximum separation of concerns.  There are many options for IoC available for Java in general but hardly any for GWT where everything is statically analyzed upfront in compilation and Java Reflection cannot be used.  In fact, I would have said that none were available at the time of this writing, except I was corrected that Google GIN based on Google Guice was on its way to being released.  Luckily we rely on a design pattern for IoC which worked perfectly for our GWT application needs as well.

  • Use the Context IoC Design Pattern.
  • Important Constraints:
    • Browser-side bindings must be distinct from Server-side bindings.
      • They may share common Java source e.g. DomainBeans/Models.
    • Browser-side bindings must be GWT compilable.
    • Browser-side bindings must be scoped to a GWT Module.
    • Server-side bindings must be scoped to a User's Session.

Event & EventManager

We initially introduced an EventManager to fire and listen to Events and elegantly manage the Asynchronous nature of Browser-side work.  Later We extended this to also process Events Synchronously as well which could then be leveraged Server-Side.  Finally, tired of the tediousness of GWT-RPC, We decided to just connect the two sides by introducing the RemoteEvent Framework which transparently delivered RemoteRequestEvents sub-types fired locally Browser-side to the Server-Side and returned the response back to the Browser and fired for asynchronous listeners of the response.  This greatly enhanced and simplified our RPC - Panels now merely fired and listened to Events for Server-side processing.  We also now had a unified and universal Event Management approach across all tiers.  Finally, We also introduced a ViewEvent that could be sub-typed for easy tokenization and browser history management when fired.

  • Events capture interesting Actions or Requests or Responses within your System.
  • Events can be fired or listened to for Asynchronous processing using a local EventManager obtained via IoC.
  • Events of type RequestEvent can be processed Synchronously using a local EventManager obtained via IoC.
    • Synchronous processing of an Event provides an immediate ResponseEvent sub-type returned to the caller.
  • Events fired of type RemoteRequestEvent will be remotely processed Asynchronously via the RemoteEvent Framework.
    • The correct sub-type of RemoteResponseEvent is returned and fired locally upon completion of Server-side processing.
    • See section: RemoteControl, RemoteEvent & RemoteEvent Framework.
  • Events can capture UI Actions (e.g. Resize, ViewCart), User Actions (e.g Submit) or Usecase Actions (e.g. PurchaseItems).
    • Can be triggered via a User's action or internally fired as part of Application flow.
  • Events define their Listener as an inner-interface unique to it, to be implemented by interested listeners.
    • The Listener's onEvent() signature must accept the exact type of Event.
  • RequestEvent subtypes define their Processor as an inner-interface unique to it, to be implemented by interested Processors.
    • The Processor's process() signature must accept the exact type of RemoteResponseEvent.
  • Events should NOT access state static or otherwise external to themselves or their composition.
  • Events should NOT use IoC or reference other Stereotypes and should be constructable.
  • Events should NOT fire, listen or process other Events or access the EventManager via IoC.

Panel & Widget

  • Panels and Widgets live solely on the Client-side (Browser-side).
  • All Panels concerned with Application flow should be CustomPanels (extends CustomPanel).
  • CustomPanels render rich visual user application flow - built using GWT's Panels and Widgets.
  • CustomPanels can maintain presentation state on behalf of the user, but should refrain from holding domain state.
  • CustomPanels access domain state via Models obtained via IoC. They may also update these Models.
  • CustomPanels may access various Formatters and similar types of utilities via IoC.
  • CustomPanels may access the EventManager via IoC and fire | listen Events.
    • CustomPanel base class provides convenience methods to fire | listen Events.
  • CustomPanels should avoid accessing other Stereotypes via IoC that are not mentioned in this section.
  • CustomPanels should explicitly destroy children that it no longer requires.
    • Rule of thumb: If re-assigning a member field then destroy() it.
    • CustomPanel base class provides convenience methods to destroy(Widget) including children.
    • Proper destruction ensures that all CustomPanels attached to the EventManager are de-registered.
    • A CustomPanel can optionally choose to destroy() itself and then re-initalize itself.
      • Note: CustomPanel can re-register for Events it was listening to before its destruction.
  • Utility components not concerned with Application specific flow may directly extend GWT's Panel, Composite or Widget.
    • Utility components should NOT access the EventManager or other Stereotypes via IoC.
  • CustomPanel designers should define appropriate ViewEvent subtypes to activate their Panels.
    • To activate their Panel listen for their specific ViewEvents and fire an appropriately configured BorderUpdateEvent.
    • ViewEvent base class provides facilities to tokenize an event for Browser History management.

Model

  • Models capture state as a closed composition of DomainBeans, their associations, and operations that apply solely on itself and its children.
    • Models may be composed of other Models and may operate on them as well.
  • Models should NOT access state static or otherwise external to themselves or their composition.
  • Models should NOT use IoC to reference other Stereotypes.
  • Models should NOT fire, listen or process Events nor access the EventManager via IoC.
  • Models should always be transportable and valid/equal on all tiers including Client-tier.
  • Models should be constructable on any tier including Client-side with a life-span determined by its scope.
  • Models may be referenced by other Stereotypes via IoC if it is a shared instance of user session scope.

RemoteControl, RemoteEvent & RemoteEvent Framework

  • RemoteControls live solely on the Server-side and replaces the former stereotype Control.
  • RemoteControls (like the former Control) process domain actions and maintain domain state on behalf of the User.
  • RemoteControls (like the former Control) are Stateful and hold domain state on behalf of the User.
    • Either internally or via one or more shared Models.
  • RemoteControls can be accessed by other Server-Side Stereotypes via IoC.
  • RemoteControls can process RemoteRequestEvents fired Client-side and delivered Server-side via the RemoteEvent Framework.
    • They must register as a Processor for the exact sub-type of a RemoteRequestEvent with their local EventManager.
    • Events extending RemoteRequestEvent when fired Client-side will automatically be serialized and delivered to the registered Processor Server-side.
  • RemoteControls produce RemoteReponseEvents sub-types to be delivered via the RemoteEvent Framework back to Listeners Client-side.
    • Events extending RemoteReponseEvent will be automatically serialized and delivered to the registered Listeners Client-side.
    • CustomPanels interested in responses to fired events should register themselves as listeners with their local EventManager.

Service, Store, DomainBean

  • Services and Stores live solely on the Server-side.
  • Services are always Stateless and implement transaction-level Usecases.
  • Stores are always Stateless and implement CRUD operations for DomainBeans residing in a Repository.
  • DomainBeans should always be transportable and valid/equal on all tiers including Client-side.
  • DomainBeans should be constructable on any tier including Client-side with a life-span determined by its scope.

Diagrams & Code Samples

Disclaimer: Diagrams & Code Samples are a mashup of actual content slashed up and modified to use the mocked sample of Shopping. If it doesn't all make sense, I apologize, the parts are there to convey the approach even if the sample as a whole doesn't always come together.


Local Events & Panels



Remote Events & RemoteControls



Code Samples:

Disclaimer: Diagrams & Code Samples are a mashup of actual content slashed up and modified to use the mocked sample of Shopping. If it doesn't all make sense, I apologize, the parts are there to convey the approach even if the sample as a whole doesn't always come together.



/**
 * Sample ViewEvent
 * 
 * Fired and used Browser-side only.
 * Note: Extend Event.class directly for non-history managed events.
 */
public class ViewProductSearchEvent extends ViewEvent {

    /**
     * Define a Listener for your event.
     * Always cookie-cutter, just change event class name.
     */
    public interface Listener extends Event.Listener {
        public void onEvent(ViewProductSearchEvent e);
    }

    /**
     * Define a method to delegate to your Listener by exact type.
     * Always cookie-cutter, just change event class name.
     */    
    @Override
    public void fire(Event.Listener listener) {
        ((ViewProductSearchEvent.Listener)listener).onEvent(this);
    }

    //optionally override methods set/getTokenProps() 
    //for additional history token state.                
}


/**
 * Sample RemoteRequestEvent.
 * 
 * Fired Browser-side and serialized to Server-side.
 */
public class FindProductsEvent extends RemoteRequestEvent {

   /**
    * Define a Listener for your event.
    * Always cookie-cutter, just change event class name.
    */
    public interface Listener extends Event.Listener {
        public void onEvent(FindProductsEvent e);
    }

   /**
    * Define a method to delegate to your Listener by exact type.
    * Always cookie-cutter, just change event class name.
    */      
    @Override
    public void fire(Event.Listener listener) {
        ((FindProductsEvent.Listener)listener).onEvent(this);
    }

   /**
    * Define a Processor for your event (for synchronous processing).
    * Specify exact type of your request and response events.
    * Always cookie-cutter, just change event class names.
    */  
    public interface Processor extends RequestEvent.Processor {
        public FindProductsDoneEvent process(FindProductsEvent e);
    }

   /**
    * Define a method to delegate to your Processor by exact type 
    *     and return the exact type of your response.
    * Note: Mediator is a simple interface to access the Processor of an Event type.
    *     Your Local EventManager implements this Mediator interface. 
    * Always cookie-cutter, just change event class names.
    */      
    @Override
    public FindProductsDoneEvent process(RequestEvent.Mediator mediator) {
        return ((FindProductsEvent.Processor)(mediator.getProcessor(this))).process(this);
    }
  
    private String searchText = null;
    
   /**
    * Not shown: GWT Serialization also requires a default constructor.
    */ 
    public FindProductsEvent(String searchText) {
        this.searchText = searchText;
    }
    
    public String getSearchText() {
        return searchText;
    }
  
   /**
    * RequestEvents must implement, used for cache-able responses.
    */
    @Override
    public String getKey() {
        return new ToStringBuilder(getClass())
            .append("searchText", searchText)
            .toString();
    }
    
}


/**
 * Sample RemoteResponseEvent.
 * 
 * Serialized after Server-side processing back to Browser and fired.
 */
public class FindProductsDoneEvent extends RemoteResponseEvent {

   /**
    * Define a Listener for your event.
    * Always cookie-cutter, just change event class name.
    */
    public interface Listener extends Event.Listener {
        public void onEvent(FindProductsDoneEvent e);
    }

   /**
    * Define a method to delegate to your Listener by exact type.
    * Always cookie-cutter, just change event class name.
    */
    @Override
    public void fire(Event.Listener listener) {
        ((FindProductsDoneEvent.Listener)listener).onEvent(this);
    }

    private List<Products> result = null;
    private ProductSearchException ex = null;

   /**
    * Success with result.
    * Not shown: GWT Serialization also requires a default constructor.
    */
    public FindProductsDoneEvent(String requestKey, List<Product> result) {
        super(requestKey, Status.Success);
        this.results = results;
    }

   /**
    * Failed with exception.
    */
    public FindProductsDoneEvent(String requestKey, ProductSearchException x) {
        super(requestKey, Status.Fail);
        this.ex = x;
    }

    public List<Product> getResult() {
        return results;
    }

    public ProductSearchException getException() {
        return ex;
    }
}


/**
 * Sample Panel for shopping.
 * 
 * Base CustomPanel class provides following shortcuts:
 *   listen(eventClass) =
 *     cxt.getEventManager().addListener(eventClass, this);
 *   fire(event) =
 *     cxt.getEventManager().fire(event);
 */
public class ShoppingPanel extends CustomPanel implements 
    ViewShoppingEvent.Listener, ViewProductSearchEvent.Listener
        LoginDoneEvent.Listener, FindProductsDoneEvent.Listener, 
            AddToCartDoneEvent.Listener, PurchaseDoneEvent.Listener
{
    /**
     * Define my IoC dependencies.
     * Note CustomPanel.Context already specifies a dependency to EventManager.
     */
    public interface Context extends CustomPanel.Context {
        public ShoppingModel getShoppingModel();
    }

    private final Context cxt;
    private Widgets widgets = null;

    /**
     * Construct with my IoC Context defined above.
     */
    public ShoppingPanel(Context cxt) {
        super(cxt);
        this.cxt = cxt;
    }

    /**
     * Build my panel.
     */
    @Override
    public CustomPanel build() {
        if (isBuilt()) {
            return this;
        }

        //use base class shortcuts to register as listeners of events.
        listen(ViewShoppingEvent.class);
        listen(ViewProductSearchEvent.class);
        listen(LoginDoneEvent.class);
        listen(FindProductsDoneEvent.class);
        listen(AddToCartDoneEvent.class);
        listen(PurchaseDoneEvent.class);

        //I've put my widgets into an inner class for easy manage.
        widgets = new Widgets();

        //Will be built later as product results are added
        widgets.productResults.table.pad(5).space(0).width("100%").css("shoppingTable");

        //x(),y() are fluent api for Horizontal/Vertical layout (= x/y axis).
        widgets.main.width("100%").css("shoppingMain")
            .x(widgets.breadcrumb).id("breadCrumb").width("100%").q()
            .y().width("100%").css("shoppingSearch")
                .x().stretch().align(-1,0).space(5)
                    .put(widgets.productSearch.searchText)
                    .put(widgets.productSearch.search)
                    .q()
                .x().stretch().align(1,0).space(5)
                    .put(widgets.newSearch)
                    .put(widgets.print)
                    .q()
                .q()
            .x(horizontalLine()).width("100%").style("margin", "1em 0").q()
            .x(widgets.productResults.table).width("100%").q()
            .x(widgets.productResults.viewAlternatives).width("100%").align(1,0).space(6).q()
        ;//end

        add(new CSSPanel().css("shoppingDecor").wrap(widgets.main));

        //Sample fire of RemoteRequestEvent for Server-side processing.
        widgets.productSearch.search.addClickHandler(new ClickHandler() {
            public void onClick(ClickEvent e) {
                fire(new FindProductsEvent(widgets.productSearch.searchText.getText());
                fire(new ShowWorkingPopupEvent("Searching Products"));
            }
        });

        return this;
    }

    /**
     * Sample: handle Event to display myself.
     */
    public void onEvent(ViewShoppingEvent e) {
        fire(new BorderUpdateEvent(this));
    }

    /**
     * Sample: handle Event to display self
     *   with product search.
     */
    public void onEvent(ViewProductSearchEvent e) {
        if (e.getSearchText() != null) {
            fire(new FindProductsEvent(e.getSearchText()));
            fire(new ShowWorkingPopupEvent("Searching Products"));
        }
        fire(new BorderUpdateEvent(this));
    }

    /**
     * Sample: handle server response to product search.
     */
    public void onEvent(FindProductsDoneEvent e) {
        fire(new HideWorkingPopupEvent());

        if (e.isFail()) {
            errorAlert(e);
            return;
        }

        cxt.getShoppingModel().addProductsSearched(e.getSearchText(), e.getResult());
        updateProductResultsView();
    }

    /**
     * Sample: handle server response to cart add.
     */
    public void onEvent(AddToCartDoneEvent e) {
        if (e.isFail()) {
            errorAlert(e);
            return;
        }
        cxt.getShoppingModel().addToCart(e.getResult());
        updateCartView();
    }

    /**
     * Sample: handle server response to purchasing.
     */
    public void onEvent(PurchaseDoneEvent e) {
        if (e.isFail()) {
            errorAlert(e);
            return;
        }
        cxt.getShoppingModel().setOrder(e.getOrder());
        cxt.getShoppingModel().clearCart();
        updateOrderView();
    }

    /**
     * Sample: handle server response to login.
     */
    public void onEvent(LoginDoneEvent e) {
        if (e.isFail()) {
            errorAlert(e);
            return;
        }
        cxt.getShoppingModel().resetCart();
    }

    //
    //Collecting my widgets into inner classes for easy create/destroy/manage.
    //These widgets are spread across a giant table due to biz requirements.
    //Refactor into independent panels if can standalone. 
    //

    private class Widgets {
        BreadCrumbPanel breadcrumb = new BreadCrumbPanel();
        Button newSearch = new Button("NEW SEARCH");
        Button print = new Button("PRINT");
        ProductSearchWidgets productSearch = new ProductSearchWidgets();
        List<ProductResultWidgets> productResults = new ProductResultWidgets();
        AxisPanel main = new AxisPanel();
    }

    private class ProductSearchWidgets {
        TextBox searchText = new TextBox();
        Button search = new Button();
    }

    private class ProductResultWidgets {
        ProductWidgets mainProduct = new ProductWidgets();
        ProductWidgets similarProduct = new ProductWidgets();
        Button viewAlternatives = new Button("VIEW ALTERNATIVES");
        AxisTable table = new AxisTable();
    }

    private class ProductWidgets {
        ProductDescWidgets desc = new ProductDescWidgets();
        ProductCostsWidgets pricing = new ProductPricingWidgets();
    }

    private class ProductDescWidgets {
        Label name = new Label("", false);
        Label brand = new Label("", false);

        void setDesc(Product product) {
          name.setText(product.getName());
          brand.setText(product.getBrand());
        }
    }

    private class ProductCostsWidgets {
        CSSPanel productCost = new CSSPanel();
        CSSPanel shippingCost = new CSSPanel();
        CSSPanel savings = new CSSPanel();

        void setCosts(Product product, Pricing pricing) {
            if (pricing != null) {
                productCost.put(new Label("$" + pricing.getProductCost()));
                shippingCost.put(new Label("$" + pricing.getShippingCost()));
                savings.put(new Label("$" + pricing.getSavings()));
            }
        }
    }

    //
    //More code..more widgets, updating widgets etc.
    //
}


/**
 * A Sample RemoteControl.
 *
 * Base RemoteControl class provides the following shortcuts:
 *   process(eventClass) =
 *     cxt.getEventManager().addProcessor(eventClass, this);
 *   listen(eventClass) =
 *     cxt.getEventManager().addListener(eventClass, this);
 *   fire(event) =
 *     cxt.getEventManager().fire(event);
 */
public class ShoppingRemoteControlImpl extends RemoteControl implements ShoppingRemoteControl, 
    FindProductsEvent.Processor, AddToCartEvent.Processor, PurchaseEvent.Processor, 
        LoginDoneEvent.Listener
{
    /**
     * Define my IoC dependencies.
     * Note RemoteControl.Context already specifies a dependency to EventManager.
     */
    public interface Context extends RemoteControl.Context {
        public UserRemoteControl getUserRemoteControl();
        public ProductSearchService getProductSearchService();
        public OrderService getOrderService();
        public CartService getCartService();
    }

    private final Context cxt;
    private Cart cart = null;

    /**
     * Construct with the IoC context defined above.
     */
    public ShoppingRemoteControlImpl(Context cxt) {
        super(ctx);
        this.cxt = cxt;
        this.cart = new Cart();

        //register as processor of events using base class shortcuts.
        process(FindProductsEvent.class);
        process(AddToCartEvent.class);
        process(PurchaseEvent.class);

        //register as listener of events using base class shortcuts.
        listen(LoginDoneEvent.class);
    }

   /**
    * process request arriving remotely.
    */
    public AddToCartDoneEvent process(AddToCartEvent e) {
        this.addToCart(e.getProduct(), e.getQuantity());
        return new AddToCartDoneEvent(e.getKey());
    }

   /**
    * process request arriving remotely.
    */
    public FindProductsDoneEvent process(FindProductsEvent e) {
        List<Products> products = this.findProducts(e.getSearchText());
        return new FindProductsDoneEvent(e.getKey(), products);
    }

   /**
    * process request arriving remotely.
    */
    public PurchaseDoneEvent process(PurchaseEvent e) {
        Order order = this.purchaseCart();
        return new PurchaseDoneEvent(e.getKey(), order);
    }

   /**
    * listen (not process) a request arriving remotely.
    */
    public void onEvent(LoginDoneEvent e) {
        UserKey userKey = cxt.getUserRemoteControl().getUser().getKey();
        this.cart = cxt.getCartService().getLastSavedCart(userKey);
    }

   /**
    * impl of biz interface.
    */
    public List<Products> findProducts(String text) {
        UserKey userKey = cxt.getUserRemoteControl().getUser().getKey();
        List<Products> results = cxt.getProductSearchService().findProducts(userKey, text);
        return results;
    }

   /**
    * impl of biz interface.
    */
    public Order purchaseCart() {
        UserKey userKey = cxt.getUserRemoteControl().getUser().getKey();
        Order order = cxt.getOrderService().purchase(userKey, this.cart);
        cxt.getCartService().clearSavedCart(userKey);
        this.cart = new Cart();
        return order;
    }

   /**
    * impl of biz interface.
    */
    public void addToCart(Product product, BigDecimal quantity) {
        UserKey userKey = cxt.getUserRemoteControl().getUser().getKey();
        cxt.getCartService().addToSavedCart(userKey, product, quantity);
        this.cart.add(product,quantity);
    }

   /**
    * impl of biz interface.
    */
    public void removeFromCart(Product p) {
        UserKey userKey = cxt.getUserRemoteControl().getUser().getKey();
        cxt.getCartService().removeFromSavedCart(userKey, product);
        this.cart.remove(product);
    }
}


Conclusion:
Draw your own conclusions!!



Friday, November 27, 2009


Context IoC Revisted


I wrote about the Context IoC Design Pattern a while back now (Feb-2005) and some people picked up on it right away.  I even caught a C++ equivalent of Context IoC on the google testing blog site. I have also seen similar patterns used in Scala and other languages.  IoC containers are still popular (mostly for Java) and new ones have come on the scene like Guice which use Annotations to address many of the concerns I had initially outlined.

Here are some of the arguments for Context IoC I've made in the past:

1) There simply isn't a better language for describing and instantiating Java objects and invoking Java methods than Java itself !!!  Use of a container for IoC means all construction and dependency semantics have to be described using secondary configurations (XML or Annotations) which are then interpreted and implemented at runtime.  They are inherently going to be slower and more complex than direct Java construction and wiring.

2) The big advantage of Context IoC is type safety.  Context IoC Design Pattern does NOT use string identifiers to wire dependencies together.  Annotations can eliminate this problem as well if done right by an IoC Container Framework.

Comparing against Annotation based Dependency Injection

I'm still mixed about Annotation based Dependency wiring.  Annotations are a fabulous feature and I had fully expected them to change the landscape of containers - but its intent was for containers to post-process code as an after-thought to provide deployment level mappings rather than be used as the central engine for wiring our code.  Annotations can be processed differently environment to environment and left to container interpretations which is not appropriate for application flow in my opinion (unless of course you sell your soul to a single container maker).


In any case Annotations do address some of the issues I outlined against XML|String wired IoC containers - so Let us compare Context IoC and Annotation based Dependency Injections (DI).

Annotation based Dependency Injection:

  public class MyService implements SomeService { 
 
    private CommonService blue = null;
    private CommonService red = null;

    @Inject //imported from com.third.party.ioc.container
     public void setBlue(@Blue CommonService blue) {
       this.blue = blue;
     }
     
     @Inject //imported from com.third.party.ioc.container
     public void setRed(@Red CommonService red) {
       this.red = red;
     }

  }

Context IoC based Dependency Injection:

  public class MyService implements SomeService {    
    
    public interface Context {
       CommonService getBlue();
       CommonService getRed();
    }

    private final Context cxt;

    public MyService(Context cxt) {
      this.cxt = cxt;
    }
         
  }

Not much different in expressiveness.  With the Annotation based framework however you create a binding using the framework's proprietary classes that read the annotations at runtime and interpret your intentions to create the appropriate wirings.  With Context IoC you own your bindings - just implement your Contexts. You can create your bindings per package or per module or per application.  Your bindings merely implement all the Contexts you choose to assemble as a module or application.  With this approach you can refactor your Contexts and Bindings,  generalize them or decompose them - it is just Java, it is just OO and best of all it is your code, not extending some proprietary framework sprinkled with proprietary annotations.  

Annotation based Binding: 

com.third.party.ioc.container.ProprietaryBinding binding = new com.third.party.ioc.container.ProprietaryBinding();

binding.bind(SomeService.class)
     .to(MyService.class);

binding.bind(CommonService.class)
    .annotatedWith(Blue.class)
    .to(BlueService.class);

binding.bind(CommonService.class)
  .annotatedWith(Red.class)
  .to(RedService.class);

Context IoC Binding:

public class MyBinding implements MyService.Context {  
  private final SomeService main;
  private final CommonService blue;
  private final CommonService red;
  
  public MyBinding() {
    main = new MyService(
this); //pass 'this' as context.
    blue = new BlueService();
    red = new RedService();
  }
 
  public SomeService getMain() {
    return main;
  }

  public CommonService getBlue() {
    return blue;
  }
  
  public CommonService getRed() {
    return red;
  }  
}

Both bindings can be compile safe in Java, however, compile safety in the Annotation based binding is provided via the 3rd-party container's ProprietaryBinding whose method signatures enforce compile safety via generics, while the Context IoC binding is pure and simple Java and reliant only on your IDE or Java compiler for safety and not on an API to enforce compile safety (or for that matter bind your objects).

How about if additional dependencies were needed - say BlueService needs yet another CommonService?

Add a dependency via Annotation: 

  public class BlueService implements CommonService {  
      private CommonService green = null;

    @Inject //imported from com.third.party.ioc.container
     public void setGreen(@Green CommonService green) {
        this.green = green;
     }
    
  }

Add a dependency via Context IoC: 

  public class BlueService implements CommonService {    

    public interface Context {
       CommonService getGreen();
    }

      private final Context cxt;

    public BlueService(Context cxt) {
        this.cxt = cxt;
    }
         
  }

And the bindings would be updated to the following:

Annotation based Binding:

  ProprietaryBinding binding = new ProprietaryBinding();

  binding.bind(SomeService.class)
      .to(MyService.class);

  binding.bind(CommonService.class)
      .annotatedWith(Blue.class)
      .to(BlueService.class);

  binding.bind(CommonService.class)
    .annotatedWith(Red.class)
    .to(RedService.class);

  binding.bind(CommonService.class)
    .annotatedWith(Green.class)
    .to(GreenService.class);

Context IoC based Binding:

public class MyBinding implements MyService.Context, BlueService.Context {  
  private final SomeService main;
  private final CommonService blue;
  private final CommonService red;
  private final CommonService green;
  
  public MyModule() {
    main = new MyService(this);
    blue = new BlueService(
this); //pass 'this' as context.
    red = new RedService();
    green = new GreenService();
  }

  public SomeService getMain() {
    return main;
  }
 
  public CommonService getBlue() {
    return blue;
  }
  
  public CommonService getRed() {
    return red;
  }

  public CommonService getGreen() {
    return green;
  } 

}

Your friendly neighborhood IDE makes Context IoC clearly better!
Your IDE is your friend, and with Context IoC, as soon as you add a dependency to any Object's Context - your IDE lets you know all the bindings that need to be updated and fixed.  You may have one or more bindings in your application depending on how you modularize and of course one or more Unit-Test bindings as well that may need to be updated and fixed.  Here Context IoC is clearly better since an Annotation or XML based binding will not help identify these mistakes until you execute your App.  Annotation based bindings cannot provide compile safety if you never made a binding entry in the first place!  But your good old Java compiler and IDE will come to your rescue with Context IoC and tell you where all you have forgotten to provide a binding entry.

Wrappers|Decorators|Interceptors

Annotations should be used for what they are designed - provide external environmental binding points.  Annotations can now be post-processed by Wrappers|Decorators|Interceptors of your choosing based on how an application should be configured and deployed to an environment.  All forms of IoC Bindings (be it Context IoC or Annotation based or XML based) can easily allow for Wrappers or Decorators or Interceptors since any of these Bindings manage Object construction.  Here again, If you are using a 3rd party IoC Container to manage your IoC Bindings - you will need to use their proprietary API or proprietary declaration to hook in your Decorator or Interceptor. (Note: Standardization can help alleviate some of this).



Tuesday, February 17, 2009


Comparing RIA Frameworks

Giving a Rich face-lift to an organization's websites can deliver such a superior user experience that quite possibly it can give the organization a clear edge over its competitors, but choosing the right RIA framework for your team is never an easy task. The frameworks, categories, and details presented in this article are not exhaustive by any means.

Reviewer: Sony Mathew: GWT 1.5, Laszlo 4.2, JavaScript Raw
Reviewer: Mark Ulmer: Flex 3.2

GWT 1.5 | AJAX Flex 3.2 | FLASH Laszlo 4.2 | AJAX Laszlo 4.2 | FLASH JavaScript Raw | AJAX
Platform/Technology Standard Browser
(HTML, JavaScript, AJAX).
FLASH
(Browser plug-in by Adobe)
Standard Browser
(HTML, JavaScript, AJAX).
FLASH
(Browser plug-in by Adobe)
Standard Browser
(HTML, JavaScript, AJAX).
User Experience Modest Rich
Approaching Desktop App feel
No Page refreshes, all data-access & rendering in the background.
Very Rich
Is exactly like a Desktop App
No Page refreshes, all data-access & rendering in the background.
Modest Rich
Approaching Desktop App feel
No Page refreshes, all data-access & rendering in the background.
Very Rich
Is exactly like a Desktop App
No Page refreshes, all data-access & rendering in the background.
Modest Rich
Approaching Desktop App feel
No Page refreshes, all data-access & rendering in the background.
Browser Support Relies on WWW standards. Supports all relevant browsers including IPhone & Android browsers. Incompatibility issues are minimized but still present e.g. with CSS or custom components. Supports all relevant browsers including future Android browsers but not the IPhone. Zero incompatibility issues, but unlike standards plug-ins compete for user base. Relies on WWW standards. Supports all relevant browsers including IPhone & Android browsers. Incompatibility issues are minimized but still present. Supports all relevant browsers including future Android browsers but not the IPhone. Zero incompatibility issues, but unlike standards plug-ins compete for user base. Relies on WWW standards. Can support all relevant browsers including IPhone & Android browsers. Incompatibility issues can be major unless using a 3rd party library.
UI Code Java, Compiled to JavaScript, Styled by standard CSS, Code HTML for Custom components. Some JRE not simulated e.g. BigDecimal, Calendar, DateFormat, NumberFormat but can fix. XML markup & ActionScript (Javascript with OO & static typing). Compiled to Flash SWF. Styled by custom CSS. XML markup & JavaScript, Compiled to JavaScript. Styled by custom CSS. XML markup & JavaScript, Compiled to Flash SWF. Styled by custom CSS. JavaScript & HTML DOM Manipulation. Styled by standard CSS.
Access Remote Services & Data. Invoke remote Java services and transport Java beans like RMI (Declare remote Java interfaces + Mark transported beans as Serializable + Java Servlet to process RPC requests). Can also do direct HTTP, transport XML or JSON to/from Servlet & use via their API for XPATH or JSON. Configure access to any Java bean & transport beans as ActionScript objects. Can also do HTTP or SOAP, transport XML to/from Servlet & & use via easy XPATH bindings to views. Can do HTTP or SOAP, transport XML to/from Servlet & use via easy XPATH bindings to views. Can also configure RPC directly to a Java bean & access transported beans as JavaScript objects. Can do HTTP or SOAP, transport XML to/from Servlet & use via easy XPATH bindings to views. Can also configure RPC directly to a Java bean & access transported beans as JavaScript objects. Can do HTTP, transport XML or JSON to/from Servlet & use via XPATH or JavaScript objects.
Code Complexity Management Java files, Compile-time type safety, Organize into Class/Object hierarchies, Packages, Modules. XML Files & ActionScript files, Compile-time type safety, Organize into Class/Object hierarchies, Packages, Modules & Libraries. XML Files, Organize into Object/Prototype hierarchies & Libraries. XML Files, Organize into Object/Prototype hierarchies & Libraries. JavaScript Files, Organize into Object/Prototype hierarchies.
Tool Support / Eclipse Integration Use all Java Tools, Free command-line Compile/Build or custom Eclipse plugin for Compile/Build. Run/Debug from Eclipse like regular Java. Navigate to API docs from Java Editor. Use all XML Tools, Free command-line Compile/Build or Adobe Eclipse plugin ($250) for Compile/Build & Run/Debug & good graphical WYSIWYG editor. Navigate to API docs from XML or ActionScript Editor via Eclipse plugin. Use all XML Tools, DTD/Schema not kept current & not usable if creating new object tags. Run from Browser and use interactive Debug console in Browser (console had some issues with IE). Switch between DHTML & FLASH 8 & 9. View separate online docs in browser - no navigation shortcut. Use all XML Tools, DTD/Schema not kept current & not usable if creating new object tags. Run from Browser and use interactive Debug console in Browser. Switch between DHTML & FLASH 8 & 9. View separate online docs in browser - no navigation shortcut. Edit with a Text or JavaScript Editor. Run from Browser - use FireBug and similar tools to debug. View separate online docs in browser - no navigation shortcut.
Refactoring & Code-Completion Support All Java Refactoring & Code Completion. Eclipse plugin provides standard refactoring operations (rename, move, etc.) & Code-Completion. Some XML Element/Attribute Completion but DTD/Schema not kept current & not usable if creating new object tags Some XML Element/Attribute Completion but DTD/Schema not kept current & not usable if creating new object tags None.
JEE Integration Lives in any WAR project. Generates JS files for inclusion in WAR. Lives in any WAR project. Generates SWF files for inclusion in WAR or compile just-in-time at runtime. Lives in any WAR project. Generates JS files for inclusion in WAR or compile just-in-time at runtime. Many jars (some unnecessary). Lives in any WAR project. Generates SWF files for inclusion in WAR or compile just-in-time at runtime. Many jars (some unnecessary). Lives in any WAR project. Include & Edit JS files in your WAR.
Migration Can integrate with existing Web-Apps as new pages or part of a page, Allows deep links into workflows. Can reuse existing beans & can remote access to existing Controls & Services including sharing of session data. Can integrate with existing Web-Apps as new pages only, Allows deep links into workflows. Can reuse existing beans & can remote access to existing Controls & Services including sharing of session data. Can integrate with existing Web-Apps as new pages only, Allows deep links into workflows. Can reuse existing beans & can remote access to existing Controls & Services including sharing of session data. Can integrate with existing Web-Apps as new pages only, Allows deep links into workflows. Can reuse existing beans & can remote access to existing Controls & Services including sharing of session data. Can integrate with existing Web-Apps as new pages or part of a page, Allows deep links into workflows. Can reuse existing beans (with explicit JSON conversion) & can remote access to existing Controls & Services including sharing of session data.
Performance Fast download time to Browser, Fast startup time, Modest Browser Responsiveness (All relative to size of App). Can modularize to decrease download/startup times. Low bandwidth usage compared to traditional web apps. Modest download time to Browser, Modest startup time, Fast Browser Responsiveness (All relative to size of App). Can modularize to decrease download/startup times. Low bandwidth usage compared to traditional web apps. Modest download time to Browser, Modest startup time, Modest Browser Responsiveness (All relative to size of App). Can modularize to decrease download/startup times. Low bandwidth usage compared to traditional web apps. Modest download time to Browser, Modest startup time, Fast Browser Responsiveness (All relative to size of App). Can modularize to decrease download/startup times. Low bandwidth usage compared to traditional web apps. Modest download time to Browser, Modest startup time, Modest Browser Responsiveness (All relative to size of App). Can modularize to decrease download/startup times. Low bandwidth usage compared to traditional web apps.
Static-Content (Externally Managed) Can mixin HTML & CSS as-is or modified. Can parse XML content or plain-text. Can parse XML content or plain-text. Can mixin basic HTML text (Not CSS) as-is. Can parse XML content or plain-text. Can mixin basic HTML text (Not CSS) as-is. Can parse XML content or plain-text. Can mixin basic HTML text (Not CSS) as-is. Can mixin HTML & CSS as-is or modified. Can parse XML content or plain-text.
Testing Supports Unit Tests. There are various Record/Playback/Scripted tools e.g. Selenium. Load Testing ?? (Script with HtmlUnit?) Adobe provides FlexUnit for unit testing. There are various Record/Playback/Scripted tools: FlexMonkey, FlashSelenium, Mercury QuickTest. Load Testing ?? There are various Record/Playback/Scripted tools e.g. Selenium. Load Testing ?? (Script with HtmlUnit?) There are various Record/Playback/Scripted tools: FlexMonkey, FlashSelenium, Mercury QuickTest. Load Testing ?? There are various Record/Playback/Scripted tools e.g. Selenium. Load Testing ?? (Script with HtmlUnit?)
Accessibility Supports the W3C Initiative for Accessibility for Rich Internet Apps (WAI-RIA). Supports Microsoft Active Accessibility (MSAA) for Internet Explorer. None (custom build?) Supports Microsoft Active Accessibility (MSAA) for Internet Explorer. None (custom build?)
Printing Can print as with HTML pages. Can print with additional page setup. Can print as with HTML pages. Can print with additional page setup. Can print as with HTML pages.
Search Engine Optimization Not directly compatible (embed host pages with search terms). Not directly compatible (embed host pages with search terms). Not directly compatible (embed host pages with search terms). Not directly compatible (embed host pages with search terms). Not directly compatible (embed host pages with search terms).