Decoupled Communication with Prism (Shared Services)

One common method of cross module communication is through shared services. Let's say our application is composed of a shell application and various module assemblies. Our shell app provides several core services such as logging, authentication, and service location. When the modules are loaded by the shell app, the modules add their services to the service locator. Typically services are registered and retrieved from service locator by common interface types. This allows modules to leverage services provided by other modules.

In the Prism Stock Trader reference implementation, the News module provides an implementation of INewsFeedService and the Market module provides an implementation of IMarketFeedService. The Position module consumes these services by leveraging the shell application's dependency injection container which provides service location and resolution.

Take a look at NewsModule.cs and MarketModule.cs to see how these modules register their services into the Unity DI container. The Position module's PositionSummaryPresenter receives these services via constructor injection.

NewsModule.cs:

 protected void RegisterViewsAndServices()
{
   _container.RegisterType<INewsFeedService, NewsFeedService>(new ContainerControlledLifetimeManager());
   _container.RegisterType<INewsController, NewsController>(new ContainerControlledLifetimeManager());
   _container.RegisterType<IArticleView, ArticleView>();
   _container.RegisterType<IArticlePresenter, ArticlePresenter>();
   _container.RegisterType<INewsReaderView, NewsReader>();
   _container.RegisterType<INewsReaderPresenter, NewsReaderPresenter>();
}

The News module adds six interface to concrete class mappings into the container, however only the INewsFeedService is meant to be consumed by other modules and therefore can be found in the StockTraderRI.Infrastructure common assembly. The remaining five mappings allow the container to resolve instances internal to the NewsModule.

The Unity ContainerControlledLifetimeManager instances passed into the first two RegisterType methods cause the container to treat instances NewsFeedService and NewsController as singletons, thus always return the same instances when the container is asked to resolve INewsFeedService and INewsController respectively. When the container is asked to resolve an instance of IArticlePresenter, which does not have a ContainerControlledLifetimeManager, a new instance is returned every time.

MarketModule.cs

 protected void RegisterViewsAndServices()
{
    _container.RegisterType<IMarketHistoryService, MarketHistoryService>();
    _container.RegisterType<IMarketFeedService, MarketFeedService>(new ContainerControlledLifetimeManager());
    _container.RegisterType<ITrendLineView, TrendLineView>();
    _container.RegisterType<ITrendLinePresenter, TrendLinePresenter>();
}

All four interfaces registered by the MarketModule are found in the StockTraderRI.Infrastructure common assembly and are meant to be consumed by other modules.

PositionSummaryPresenter.cs

 public PositionSummaryPresenter(IPositionSummaryView view, IAccountPositionService accountPositionService
                                        , IMarketFeedService marketFeedSvc
                                        , IMarketHistoryService marketHistorySvc
                                        , ITrendLinePresenter trendLinePresenter
                                        , IOrdersController ordersController
                                        , IEventAggregator eventAggregator)

The PositionSummaryPresenter's constructor defines seven parameters which are interpreted as dependencies by the DI container. When PositionSummaryPresenter is resolved through the DI container, the container will first resolve the seven dependencies listed above and pass them into the constructor. Implementations of IMarketFeedService, IMarketHistoryService, and ITrendLinePresenter come from the Market module as mentioned above. Implementations of IPositionSummaryView, IAccountPositionService, and IOrdersController come from the Position module. An implementation of IEventAggregator is provided by the shell application's bootstrapper.

The important thing to notice is that these three modules do not have static references to each other but are able to share services.

With service instances being shared across modules, you could easily imagine how it is possible to share data and pass messages between modules.