Communication

Retired Content

This content is outdated and is no longer being maintained. It is provided as a courtesy for individuals who are still using these technologies. This page may contain URLs that were valid when originally published, but now link to sites or pages that no longer exist.

When building large complex applications, a common approach is to divide the functionality into discrete module assemblies. It is also desirable to minimize the use of static references between these modules. This allows the modules to be developed, tested, deployed, and updated independently and forces loosely coupled communication.

When communicating between modules, you can use commanding, event aggregation, or shared services. Use the following to help decide which approach to use:

  • Commanding. Use this in response to user gestures and custom enablement.
  • Event aggregator. Use this to publish an event across modules.
  • Shared services. Use this if neither of the preceding is applicable.

Commanding

If you need to respond to a user gesture, such as clicking on a command invoker (for example, a button or menu item), and you want the invoker to be enabled based on business logic, use commanding**.**

Windows Presentation Foundation (WPF) provides RoutedCommand, which is good at connecting command invokers, such as menu items and buttons, with command handlers that are associated with the current item in the visual tree that has keyboard focus.

However, in a composite scenario, the command handler is often a controller that does not have any associated elements in the visual tree or is not the focused element. To support this scenario, the Composite Application Library provides CompositeCommand and DelegateCommand, which has a direct routing mechanism, compared to RoutedCommand, which uses tunneling and bubbling.

The CompositeCommand is an implementation of ICommand so that it can be bound to invokers. CompositeCommands can be connected to several child commands and when the CompositeCommand is invoked, the child commands are also invoked.

CompositeCommands support the notion of enablement. CompositeCommands listen to the CanExecuteChanged event of each one of its connected commands. It then raises this event notifying its invoker(s). The invoker(s) reacts to this event by calling CanExecute on the CompositeCommand. The CompositeCommand then again polls all its child commands by calling CanExecute on each child command. If any call to CanExecute returns false, the CompositeCommand will return false, thus disabling the invoker(s).

How does this help me with cross module communication? Applications based on the Composite Application Library may have global CompositeCommands that are defined in the shell that have meaning across modules, such as Save, Save All, Cancel. Modules can then register their local commands with these global commands and participate in their execution.

For more information about using composite commands, see the following:

Event Aggregator

If you need to publish an event across modules and do not need a response, use EventAggregator.

Consider using EventAggregator when sending a message between business logic code, such as controllers and presenters. An example is when the Process Order button has been clicked and the order successfully processed; in this case, other modules need to know the order is successfully processed so they can update their views.

EventAggregator provides multicast publish/subscribe functionality. This means there can be multiple publishers that raise the same event and there can be multiple subscribers listening to the same event.

This helps with cross module communication because events can be defined in a shared assembly in a way that publishers and subscribes can reside in entirely separate modules.

For more information about using EventAggregator, see the following:

Shared Services

Another method of cross-module communication is through shared services. When the modules are loaded, modules add their services to the service locator. Typically, services are registered and retrieved from a service locator by common interface types. This allows modules to use services provided by other modules without requiring a static reference to the module. Service instances are shared across modules, so you can share data and pass messages between modules.

In the Stock Trader Reference Implementation, the News module provides an implementation of INewsFeedService. The Position module consumes these services by using the shell application's dependency injection container, which provides service location and resolution. The INewsFeedService is meant to be consumed by other modules, so it can be found in the StockTraderRI.Infrastructure common assembly.

To see how these modules register their services into the Unity dependency injection container, see the files NewsModule.cs, as shown in the following code, and MarketModule.cs. The Position module's PositionSummaryPresentationModel receives these services through constructor dependency injection. For more information about the Dependency Injection pattern, see Dependency Injection.

protected void RegisterViewsAndServices()
{
_container.RegisterType<INewsFeedService, NewsFeedService>(new ContainerControlledLifetimeManager());
}

This helps with cross-module communication because service consumers do not need a static reference to modules providing the service. This service can be used to send or receive data between modules.

For more information about services and containers, see the following:

Retired Content

This content is outdated and is no longer being maintained. It is provided as a courtesy for individuals who are still using these technologies. This page may contain URLs that were valid when originally published, but now link to sites or pages that no longer exist.