Modules have knowledge of shell? A good thing?

Jun 5, 2008 at 11:09 PM
This is my first day with PRISM and I'm really impressed (and somewhat overwhelmed)! One thing bugged me, though. When I look at the code, I see the modules "looking in" to the shell to position themselves:

Find all "MainRegion", Subfolders, Find Results 1, "Entire Solution"

C:\OSI\Lab\Visual Studio 2008\Downloaded\Prism\2008-05-29Drop\Source\QuickStarts\Modularity\ConfigurationModularity\ConfigurationModularity\Shell.xaml(6): <ItemsControl Name="MainRegion" prism:RegionManager.RegionName="MainRegion" />

C:\OSI\Lab\Visual Studio 2008\Downloaded\Prism\2008-05-29Drop\Source\QuickStarts\Modularity\ConfigurationModularity\ModuleA\ModuleA.cs(33): _regionManager.GetRegion("MainRegion").Add(new DefaultViewA());

C:\OSI\Lab\Visual Studio 2008\Downloaded\Prism\2008-05-29Drop\Source\QuickStarts\Modularity\ConfigurationModularity\ModuleB\ModuleB.cs(35): _regionManager.GetRegion("MainRegion").Add(_defaultViewB);

C:\OSI\Lab\Visual Studio 2008\Downloaded\Prism\2008-05-29Drop\Source\QuickStarts\Modularity\ConfigurationModularity\ModuleC\ModuleC.cs(33): _regionManager.GetRegion("MainRegion").Add(new DefaultViewC());

C:\OSI\Lab\Visual Studio 2008\Downloaded\Prism\2008-05-29Drop\Source\QuickStarts\Modularity\ConfigurationModularity\ModuleD\ModuleD.cs(33): _regionManager.GetRegion("MainRegion").Add(new DefaultViewD());

C:\OSI\Lab\Visual Studio 2008\Downloaded\Prism\2008-05-29Drop\Source\Prism\Prism.Tests\Regions\RegionManagerFixture.cs(38): regionManager.Register("MainRegion", region1);

C:\OSI\Lab\Visual Studio 2008\Downloaded\Prism\2008-05-29Drop\Source\Prism\Prism.Tests\Regions\RegionManagerFixture.cs(40): IRegion region2 = regionManager.GetRegion("MainRegion");

Matching lines: 7 Matching files: 6 Total files searched: 143

Maybe I'm being a moron and misunderstanding things, but this doesn't seem good. Seems like modules should have no knowledge of the shell. Someone please help me understand!

Oh, I'm also interested in the PRISM road map. I saw a couple of posts about that today. I'll follow those posts for any updates.

Thanks!

Jun 12, 2008 at 5:42 PM
I'm really surprised that nobody else seems bothered by this. If what I'm seeing is correct, it totally breaks encapsulation and decreases the possibility for reuse. For example, suppose I have a General Ledger Accounting module. Based on what I'm seeing, that module is to be hardcoded to register itself in "MainRegion". That's nice, provided the shell has a "MainRegion". What if I want to use this module in various applications, some of which have no "MainRegion"? This seems totally backwards to me, having the module look in to the hosting shell. Am I misunderstanding something, or is everyone so impressed with this technology (I admit it's impressive) that we're willing to overlook this big architectural no-no? Is this a temporary hack? Someone please explain!
Jun 12, 2008 at 6:04 PM
Hi Idiot (man I feel bad saying that, lol)

I think it's as you say, just a temporary thing, I don't think it was intended as a design model and purely as a RegionManager exmaple, even though most of the samples have a reference to the modules for each project.

Some of the examples where the shell does not have knowledge of the modules are:

    Prism\Source\QuickStarts\Modularity\ConfigurationModularity
    Prism\Source\QuickStarts\Modularity\DirectoryLookupModularity

Actually, now I look they're the only two. Take a look at my saple if you like (Composite WPF samples), I never refer to the shell in modules and the shell will never refer to modules. Any interaction the modules need to perform with the shell they use services exposed as interfaces through the "Infrastructure" model. I think this is a pattern I'm inherent from CAB, but it's a good practice all the same.

I hope the sample I have is simple enough and helps you out with how I've acheived the modularity concept.

-Brett
Jun 12, 2008 at 8:43 PM


ID10T wrote:
I'm really surprised that nobody else seems bothered by this. If what I'm seeing is correct, it totally breaks encapsulation and decreases the possibility for reuse. For example, suppose I have a General Ledger Accounting module. Based on what I'm seeing, that module is to be hardcoded to register itself in "MainRegion". That's nice, provided the shell has a "MainRegion". What if I want to use this module in various applications, some of which have no "MainRegion"? This seems totally backwards to me, having the module look in to the hosting shell. Am I misunderstanding something, or is everyone so impressed with this technology (I admit it's impressive) that we're willing to overlook this big architectural no-no? Is this a temporary hack? Someone please explain!


Just  to keep the conversation going, because it is a good question. ( and to stir the pot a bit )

Where would you suggest the "glue" exist?  Something has to understand what region to place a modules views into.  You dont want to have to change the code in the shell to add a new module, so it cant go there.  Would you want a "mother of all controllers" that knew about every module and where it should go?  The module init seems to be the right place to me. The app has to have well known places for things to plug into.  One is usually not building a generic app.  The modules need to understand what they are pluggin into.  The views should not know where they go, but I think that the module is where the glue should go.

Strings like "MainRegion" are wrong IMO, but there have to be well known names or lookups that allow the module to plug views in.

( good question, IMO )
Paul

Jun 12, 2008 at 9:02 PM
Edited Jun 12, 2008 at 9:04 PM
Hi ID10T

This is one approach for UI Composition, that is the modules inject views into the shell. The modules themselves do not have direct knowledge of the shell. The region provides an abstraction that allows a module to grab a named area which it can inject into. This level of abstraction is similar to when one retrieves a type instance from an IOC container by a key. Encapsulation is not broken in that the module only knows the name, it does not have any other information about how the region is implemented or where it is ACTUALLY located. Regions can also be localized within the same module, in either case the name is the moniker for accessign it.

If there is a concern, you can easily further abstract the region through a custom service that accesses the region manager itself. So for example if you want your Order module to be reused in several applications, you can have a LayoutService that the Order Module calls instead of calling the region manager directly. You can also have a service that simply returns a region name based on configuration, and have the module call that service in order to figure out which region to use. 

Finally a third option, is to not have modules have any access to regions period. In this model you can have a service at the shell that queries the container for views based on an interface, and then displays them itself. If you look in our new Family Show spike (in our recent drop), we used a similar approach. Each module registers views that implement an interface. The shell queries the container based on this.
Jun 12, 2008 at 9:10 PM
Edited Jun 12, 2008 at 9:13 PM
Good comments - this is the kind of feedback I was hoping to get. [UPDATE: I wrote this before I saw gblock's reply.]

Yes, if the shell has to place the modules then this violates the "Open - Closed" principle. But if the modules attach themselves to the shell by breaking encapsulation (e.g., looking for a "Main Region" to attach to), well, that's also bad. How about some sort of configurable mediator that softens the tie between the shell and the attaching modules? For example, the GLA module could be configured to attach to "Main Region" in application/shell A, but it could be configured to attach to "External Modules" region in application/shell B. That seems to be a better solution to me than relying on well known regions or magic strings, and it allows the GLA module to be reused. If others agree, perhaps this is something that can be baked into the PRISM framework so we all don't have to write our own...?

Brett, thanks for the code. I was easily able to add a new module with AbstractDocument content; I like what you did. I did notice mention of RegionNames.ViewMenu in the WelcomeModule, though. I think that this mediator idea would eliminate the need for that.

Thanks guys. Maybe we'll get more discussion on this...
Jun 13, 2008 at 4:46 AM
@ID10T

No worries, yeah I think I mentioned either in my svn commits or some comments in the code that I don't like regions and will try to avoid them at all costs. The only place where (I think) I've used the pattern was in the menu, oh that's right, I actually put the comment in the RegionNames file itself.

My idea would be to have a menu service where you register the menus instead of adding them, this was the service can control how the menu items are added and allow greater flexibility (position, what it must come before/after).

Service contracts are my flavour of choice, where both shell and modules know nothing about each other, both have a contract mechanism through the infrastructure project.

I've not thought of module reuse through other programs even though I've taken mixed modules from different apps in CAB, but now that's made the old light bulb go on.

-Brett