Composite UI Application Block (CAB)
[Update November 25, 2005 (ObjectBuilder info) - based on feedback by Edward Jezierski]
One of the things I've been playing around with lately is the Microsoft Composite UI Application Block or CAB. This is the Microsoft Pattern and Practice for developing Windows Forms Smart Client line of business applications.
According to the Microsoft documentation, the CAB has the following benefits:
- It allows you to build clients composed of independent yet cooperating modules.
- It separates the concerns of module builders from the concerns of the shell developer, allowing business units to concentrate on development of domain-specific modules instead of the client architecture.
- It provides an architectural framework for producing a consistent and high quality integrated desktop development.
- It increases productivity and reduces overall development time through consolidating architect and developer efforts.
with the overall design goals of:
At the top level, the CAB has the concept of a shell and one or more loosely coupled plug-in modules.
The shell defines the layout of the application by placing Workspaces on the shell Form. Those Workspaces host SmartParts (defined below).
Each module is added to the application through the applications ProfileCatalog.xml (i.e. plug-in) and contains implementations of WorkItems (code that drives a use case including hooking up the UI to the BL and services), SmartParts (UserControls), Controllers or Presenters (to provide UI and BL decoupling), and services.
In addition, the CAB provides:
- A module loader that loads assemblies based on configuration and processes classes that are attributed with [Service], [WorkItemExtension] and others.
- An ObjectBuilder which uses dependency injection to build up and resolve references to instances of concrete types. The ObjectBuilder also runs developer extensible strategies during build up and tear down. For example, the EventBroker hooks up event publishers and subscribers throughout the application by running an ObjectBuilder strategy.
- An Event Broker in which components publish and subscribe events to through attributes applied to events and to methods which act as event handlers. This allows multiple publishers to raise an event which multiple subscribers can handle (even across modules).
- Command/UIElement infrastructure that allows modules to add/remove elements to the shell's menu, toolbar and/or status bar (or other registered UIExtension site) and associate a command and command handlers to one or more UI events.
As I mentioned before, the CAB was designed specifically for smart client line of business applications such as Online Transaction Processing front-ends or UI intensive information worker applications. With that in mind, I set out to see how the CAB would fare in a product line architecture scenario.
More specifically, how well does the CAB module concept fit the core assets concept of a product line architecture? And how well does the CAB shell and extension concepts fit the product development part of the product line architecture?
Overall, I feel the concepts align well. The CAB provides a lot of the structure and implementation required to implement a software product line architecture and certainly doesn't limit or constrain the development of core assets and products. That said, here's a couple of things to consider:
- Think about how you are going to manage your dependencies between modules. All the great modularization of the CAB can be easily lost with an ill-advised "add reference". It may help to think of modules as independent, selectable pieces a product can choose or not choose to include in their application.
- A core set of assemblies that all of the modules depend on is useful to define controls, types and interfaces that are shared (or provide a means of communication) across modules.
- Think about a strategy to make it easy for products to specialize the core asset implementations. Services and the WorkItem catalog are useful for decoupling the implementation from the interface and allowing the product layer to specify which concrete implementations for an interface is used. Products will also want to specialize their own UI including branding, layout and look and feel. A factory or plug-in model implemented in the modules that the product layer can specialize will work well to solve that problem. The product development layer could also specify product specific ObjectBuilder strategies.
- Think about the kind of work flow you may have through your application and how that could be implemented to aggregate and run different WorkItems depending on the product.
- Make sure you have a conscious separation of product specific code and core asset code.
- Consider the performance impact of using a reflection based dependency injection system and loading all of the modules in your application at startup (even if its only to instantiate and load ModuleInit subclasses). This is a concern I've had in the past with reflection based IOC/Dependency Injection, I wonder if there is a way to delay load modules?
I've been happy with the CAB even though I'm looking to use it in a scenario that (at least on the surface) appears to be different from the main design center. If you are developing a large scale application, have a look at the CAB.