Composite Application Library Baseline Architecture
The architecture the Composite Application Library seeks to address primarily consists of a shell application that defines regions for hosting content with views and services offered by multiple modules—possibly dynamically loaded. Underlying the application, and the Composite Application Library, is a service layer to provide access to the application services based on the Composite Application Library, as illustrated in Figure 1.
Figure 1
A composite application architecture with the Composite Application Library
The architectural pieces of a composite application are the following:
- Shell. This is the top-level window or visual element to host different user interface components. The shell itself defines the layout structure, but it is typically unaware of the exact contents it will contain. It usually has minimal capability, so most of the application's functionality and content is provided by modules.
- Shell presenter. Any logic for the shell presentation is handled by the shell presenter. This follows the separated presentation pattern and helps separate the display of content from the user interface logic. This separation improves testability and maintainability.
- Regions. These are placeholders for content and host visual elements in the shell. These can be located by other components through the RegionManager to add content to those regions. Regions can also be hosted in individual views to create discoverable content placeholders.
- Modules. These are separate sets of views and services, frequently logically related, that can be independently developed, tested, and optionally deployed. In many situations, these can be developed and maintained by separate teams. In a composite application, modules must be discovered and loaded. In the Composite Application Library, this process consists of populating the module catalog, retrieving the modules if they are remote, loading assemblies into the application domain, and initializing the modules as follows:
- Populating the module catalog. You can populate the module catalog in the following ways:
- Code. This specifies the modules to load in code.
- XAML. This specifies the modules to load in a XAML file.
- Configuration Module Catalog. This specifies the modules to load in a configuration file.
- Directory Module Catalog. This examines a folder for the modules to load.
- Loading the modules. This is the process of retrieving the module locally and loading into the application domain. The timing of module retrieval and loading depends on whether the module is loaded on demand or when the application starts. Any module that is not marked OnDemand is retrieved and loaded as soon as possible. OnDemand modules are only retrieved and loaded when the application explicitly requests those applications to be retrieved and loaded.
- Initializing the modules. Modules are initialized as soon as they are available.
- Populating the module catalog. You can populate the module catalog in the following ways:
- Views. Views are responsible for displaying content on the screen. In a composite application, views are frequently the element of composition by the shell or other views. The following are considerations for use when developing views in WPF or Silverlight:
- The user interface should be testable and implement appropriate design patterns for separations of concerns. Typically, this involves some type of binding-oriented design pattern, such as Supervising Presenter or Presentation Model.
- Data binding should be used wherever possible.
- Generally, user interface actions should be bound to underlying presentation models using WPF commands, for WPF, or attached behaviors in Silverlight. For Silverlight, the Composite Application Library provides a Click.Command attached behavior for binding one of the most common user interface actions in an application.
- Communication. Different components in the application may need to communicate with one another whether they reside in the same module or different modules. This needs to happen without the modules requiring hard dependencies on one another. The Composite Application Library provides mechanisms to do this with the CompositeCommand and EventAggregator. Communication strategies may need to consider thread-safety issues. The following describes the CompositeCommand and EventAggregator:
- CompositeCommand. Frequently, when compositing views, commands those views support must also be composited. The composite command is a strategy to combine the execution of commands. This allows the command invoker to interact with a single command that affects multiple commands.
- EventAggregator. In views that need to send an event to other views or components and do not require a response, use the EventAggregator. Multiple components can publish an event, and multiple subscribers can receive the event.
- Services. The application and modules expose services for their own and shared use. These are exposed through a service container that locates and, often, constructs the services. By default, the Composite Application Library uses the Unity container for this service location.
When to Use the Composite Application Library
The Composite Application Library is most useful when building brand-new composite applications on WPF or Silverlight or applications that target both WPF and Silverlight. The Composite Application Library provides core services for building composite views and loading modules, and it helps solve challenges in communication across decoupled components.
Discrete services offered by the Composite Application Library also help when seeking to upgrade an existing WPF or Silverlight application to a composite WPF or Silverlight application.
A New Application Based on the Composite Application Library
Depending on application complexity, a composite application can consist of a shell project with a number of module projects. The activity diagram in Figure 2 illustrates activities needed to develop a composite application using the Composite Application Library.
Figure 2
Activities for creating a composite application
The following are the core activities needed when starting a new composite application:
- Define the shell.
- Create the bootstrapper.
- Create a module.
- Add a module view to the shell.
Define the Shell
The application shell provides the basic layout for the application. This layout is defined using regions that modules can use to place views. Views, like shells, can use regions to define discoverable areas that content can be added to, as illustrated in Figure 3. Shells typically set the appearance for the entire application and contain the styles that are used throughout the application. For more information about the shell, see the Shell and View technical concept.
Figure 3
Shells, views, and regions
Create the Bootstrapper
The bootstrapper is the glue that connects the application with the Composite Application Library services and the default Unity container. Each application creates an application-specific bootstrapper inheriting from UnityBootstrapper, as illustrated in Figure 4. You will need to decide the approach you want to use to populate the module catalog. Minimally, each application will provide a module catalog and a shell.
By default, the bootstrapper logs events using the .NET Framework Trace class. Most applications will want to supply their own logging services, such as Enterprise Library logging. Applications can supply their logging service in their bootstrapper.
By default, the UnityBootstrapper enables all the Composite Application Library services. These can be disabled or replaced in your application-specific bootstrapper.
Figure 4
Diagram demonstrating connecting to the Composite Application Library
For more information about the bootstrapper, see the Bootstrapper technical concept or the Container and Services technical concept.
Create the Module
The module contains the views and services specific to a piece of the application's functionality. Frequently, these are contained in separate assemblies and developed by separate teams. A module is denoted by a class that implements the IModule interface. These modules, during initialization, register their views and services and may add one or more views to the shell.
Depending on your module discovery approach, you may need to apply attributes to your module classes or define dependencies between your modules. For more information about modules, see the Module technical concept.
Add a Module View to the Shell
Modules take advantage of the shell's regions for placing content. During initialization, modules use the RegionManager to locate regions in the shell and add one or more views to those regions or register one or more view types to be created within those regions. The RegionManager is responsible for keeping track of regions throughout the application and is a core service initialized from the bootstrapper. For more information about shells and views, see the following topics:
- Shell and View technical concept
- UI Composition design concept
More Information
To learn more about the design and use of the Composite Application Library, see the following topics: