Windows Phone 7 Developer Launch - Learn More
kick it on DotNetKicks.com   Shout it  

MVVM with Prism 101 – Part 4: Modules

Source Code

Welcome back to my series on implementing the Model View View-Model pattern with Prism (aka Composite Application Library For WPF/Silverlight). I can by the response (votes and traffic) that many are really enjoying it so far. As I continue I hope not to disappoint!

This installment is about modules. Honestly, I’ve been putting this one off for a couple days because of a bit of writer’s block around the concept of “Modules” as put forth by the Prism framework and modules as a general concept in programming. I was always taught in elementary school English that when you define a word you shouldn’t use the word being defined in the definition. I generally try to follow that. Plus, it really seems a bit silly to define the concept of a Module when it is so common to programming. In Prism a Module is a module or modular block of code that can be registered at design time or discovered at runtime. (I can hear my elementary school teachers collectively screaming – or turning in their graves as the case may be).

But here’s a better way to put it – Modules are classes which implement the IModule interface. That’s it.

using System;
 
namespace Microsoft.Practices.Composite.Modularity
{
    // Summary:
    //     Defines the contract for the modules deployed in the application.
    public interface IModule
    {
        // Summary:
        //     Notifies the module that it has be initialized.
        void Initialize();
    }
}

You can bake 'em, fry ‘em or grill ‘em but it boils down to that. Your entire Silverlight solution could be contained in a single project which contained all the implementations of IModule you needed. Most often (or at least as it is prescribed) each module will have its own project. But you can also mix and match between the two strategies as needed. For example, if you have a large number of modules and you would like to reduce the size of your initial .xap file you can compile all the module projects required at startup together and then create a second silverlight application project which references the remaining projects to be packaged as a secondary .xap (rinse and repeat for as many separate .xap files as you like) and then when the initial .xap is loaded and runs it can download and import the remaining .xap files in the background or even on demand. I won’t be demonstrating this more advanced scenario – for that you just need to look it up in the docs or someday I may write an additional post addressing it specifically.

As for this series I’ll be addressing the prescribed method. Each module has its own project. Within that project you will have all the views, models and other supporting implementations. “Module” projects will not reference each other because you do not want to create circular references when things start getting complex and you want to keep dependencies to a minimum. If you need to reference something between projects the recommended practice is to create an “infrastructure” or “common” project, create an interface for the implementation which needs to be referenced and store the reference in your infrastructure project and reference that. Here’s what the final solution looks like:

SolutionScreenshot

  • Common: (Silverlight Class Library Project) - This is where interfaces, model classes, custom controls, value converters and utility classes are stored.
  • Shell: (Silverlight Application Project) - This is where the Shell, Bootstrapper and App.xaml classes are kept. This is your startup project.
  • History/Editor: (Silverlight Class Library Project) – These are both “Module Projects” which contain a single IModule implementation as well as the Views, ViewModels and (where applicable) Controllers. Also, if I am using the ServiceLocator pattern (usually when I want design-time databinding) each of these projects will have a single ServiceLocator class.
  • Services: (Silverlight Class Library Project) – This is where I keep *all* concrete service implementations. You can, if you so choose, keep service client implementations in the same project where they are used most. However, my own practice is to always have a single Services project with all concrete implementations contained in it. The reason is that your startup project can only have a single .ClientConfig file. If I need to create multiple startup projects (different versions using the same services) then I can link the .ClientConfig from the Services project to any other Silverlight Application Project I need to.
  • Model: (Windows Class Library Project) – The server-side version of my model. This is where my domain/object model and service interfaces are stored. Then in the Common project I create a link to the files I need on the client-side.
  • Web: (Web Application Project) – This is just what it sounds like. The web application which hosts the Silverlight application as well as the WCF/ASMX service host.

Implementing IModule

“That’s nice”, you’re saying. “But that doesn’t tell me anything much about what I do or how I use IModule. Well, I did skip a few details I guess. Let’s go back to Bootstrapper for a minute.

Module Lifecycle

If you’ll remember from my first post in this series Bootstrapper has a method called GetModuleCatalog. Bootstrapper is your startup class and is located in your startup project. This means that it knows about each project in your solution as well as the concrete implementations of IModule in each project. So when your Bootstrapper runs and your GetModuleCatalog method is called this is where you will register each instance of IModule. Like this:

public class Bootstrapper : UnityBootstrapper
{
    protected override IModuleCatalog GetModuleCatalog()
    {
        ModuleCatalog catalog = new ModuleCatalog();
 
        catalog.AddModule(typeof(ServicesModule));
        catalog.AddModule(typeof(EditorModule));
        catalog.AddModule(typeof(HistoryModule));
 
        return catalog;
    }
}

These will be loaded and their IModule.Initialize() method will be called in the order you register them here. So if your dependency structure is simple (like we have here) you can just load them in dependency order (both Editor and History depend only upon Services). However, later on when we talk about Event Aggregation we’ll be creating a dependency between Editor and History. We could either load them in order or explicitly define the dependency chain, like this:

protected override IModuleCatalog GetModuleCatalog()
{
    ModuleCatalog catalog = new ModuleCatalog();
 
    catalog.AddModule(typeof(ServicesModule));
    catalog.AddModule(typeof(EditorModule), "ServicesModule");
    catalog.AddModule(typeof(HistoryModule), "ServicesModule", "EditorModule");
 
    return catalog;
}

This way you don’t have to worry about the order, because just like the way Visual Studio will determine build order of projects based upon dependencies, Prism will load the modules in the order required by the dependency chain.

The responsibility of IModule is to load and register all the classes required for the application. This is where you will register your View-Models with your IoC container and your Views with the RegionManager. Basically any concrete implementations for which your Module project is responsible, they will be loaded/registered here. Here’s an example of what your typical IModule implementation will look like:

using CodeCamp.Common.ViewModel;
using Microsoft.Practices.Composite.Modularity;
using Microsoft.Practices.Composite.Regions;
using Microsoft.Practices.Unity;
 
namespace CodeCamp.History
{
    public class HistoryModule : IModule
    {
        IUnityContainer m_Container;
        IRegionManager m_Manager;
 
        public HistoryModule(IUnityContainer container, IRegionManager manager)
        {
            m_Container = container;
            m_Manager = manager;
        }
 
        #region IModule Members
 
        public void Initialize()
        {
            m_Container.RegisterType<IHistoryViewModel, HistoryViewModel>();
            ServiceLocator.Container = m_Container;
            m_Manager.RegisterViewWithRegion("HistoryRegion", typeof(HistoryView));
        }
 
        #endregion
    }
}

Here we have an example of several different tasks performed by the HistoryModule. First, the concrete type for our View-Model is registered with the IoC container. Second, we are wiring up the ServiceLocator (more on that in another post coming soon) with our IoC container. Last, we register our View with the RegionManager. Each is a task for which IModule is responsible. To demonstrate a second example, let’s look at our ServicesModule class:

using System.ServiceModel;
using CodeCamp.Model;
using Microsoft.Practices.Composite.Modularity;
using Microsoft.Practices.Unity;
using CodeCamp.Common.Services;
 
namespace CodeCamp.Services
{
    public class ServicesModule : IModule
    {
        private IUnityContainer m_Container;
 
        public ServicesModule(IUnityContainer container)
        {
            m_Container = container;
        }
 
        #region IModule Members
 
        public void Initialize()
        {
            IMessageServiceAsync service = new ChannelFactory<IMessageServiceAsync>("BasicHttpBinding_IMessageEndpoint").CreateChannel();
            m_Container.RegisterInstance<IMessageServiceAsync>(service);
        }
 
        #endregion
    }
}

Here the only thing we are concerned with is initializing our service(s) and registering them with the IoC container. There are no Views or View-Models, so you see that a Module is more than just a way to load up Views.

Once the Initialize() method completes for all the Modules the Shell will be loaded and any Views which were registered will be loaded into their respective regions.

Conclusion

So far we’ve discussed Bootstrapper, Shell, Regions and Modules. Each is a discrete piece in our application. Each has a well defined responsibility. What I like about this is I end up writing very modular code in small single-responsibility units naturally and without having to think about it too much. Sure I could create a monster method if I tried, but who’s trying?

Next in my series I’m going to finally be addressing the View-Model. I’m excited about it because it is really my favorite part and it’s what I like most about working with Silverlight and WPF.

Source Code

tags: , , , ,

kick it on DotNetKicks.com   Shout it  

Feedback

# re: MVVM with Prism 101 – Part 4: Modules

Gravatar Will you be going over your ClientChannelWrapper class and why you don't just do an "Add Service Reference"?

I'm really enjoying the series. 10/27/2009 12:42 PM | Jason Diamond

# re: MVVM with Prism 101 – Part 4: Modules

Gravatar Jason, I am planning on talking about ClientChannelWrapper very soon. I'm pretty excited about it because I'm curious to get some good feedback on it. Glad you're enjoying the series. 10/27/2009 3:27 PM | MarkJMiller

# re: MVVM with Prism 101 – Part 4: Modules

Gravatar Hey This is a great series I have been floundering over MVVM and i think yoru clearing things up for me.
THanks
11/9/2009 12:09 PM | Rico

# re: MVVM with Prism 101 – Part 4: Modules

Gravatar I am new to Prism. I am looking at so many samples. Every one pretty much create one view == one project .Do you guys create module(Project) for each view. I feel like at the end of year we may end up with 100's project. If we are not saying each view is a module how do we define a module .Is it some thing like interrelated views together.

We have lot of common look up values like Country / State/ and some business objects it has caching and hold it in a client project. If i start spilting for prism i am quite sure where to keep those objects.Because some are shared across between views some are for few views. How would you resolve all those problems 1/24/2010 7:10 PM | senthil

# re: MVVM with Prism 101 – Part 4: Modules

Gravatar You do not have to separate modules into separate projects, but the common convention is that you will have a single IModule per project. But you don't separate views into separate projects. The views will be grouped into projects logically according to function. A module will define how you want your views within the project mapped to the regions in your shell. How you group your views together will depend upon your project and what makes sense for you.

As for your common lookup values, if you're using generated proxy classes to communicate with a server (a la WCF client proxy) then you can create a client-side wrapper which uses the proxy to get the data if it doesn't exist, and caches the data after the first request. Your wrapper would be registered with your IOC container as a singleton instance. It would then be accessable globally via your IOC container as a service and the data caching would then work as expected. 1/25/2010 3:44 PM | MarkJMiller

# re: MVVM with Prism 101 – Part 4: Modules

Gravatar The module concept can be taken too far pretty easily in terms of physical separation. As you correctly state, modules are a unit of logical separation. This implies that physical separation via separate assemblies might be overkill. I find that this point needs to be emphasized because most samples I've seen use the 1 module per project approach.

In applications in which you have dozens of screens, it might make sense for each screen to be implemented via modules. But creating 20 projects will be overkill. It will increase compile time, may complicate deployment and may increase load time because each assembly needs to be loaded from disk.

Proper organization will certainly depend on the application in question but a good example is to physically isolate business screens from debug screens and from settings screens. 3/11/2010 8:44 PM | Szymon Rozga

# re: MVVM with Prism 101 – Part 4: Modules

Gravatar I recommend that you only have one module for every large portion of your application/data model. For some people each main use case might be a module. Typically the create, read, update, delete functions for tightly related data go into a single module. That module stands up finder services, central to its data, for the other modules to access. Another consideration of this architecture is that, given developers who understand the architecture, each module can be assigned to another small team. You can have some modules being done in house and some modules being done remotely by contractors. And they are loosely coupled so they might have few or no dependencies. Sometimes you create mock services while waiting for a module to be built. 5/11/2010 2:22 PM | Joe

# my website

Gravatar MVVM with Prism 101 - Part 4: Modules 11/24/2014 6:19 PM | my website

Comments have been closed on this topic.
 

 

Copyright © Mark J. Miller