Application Design 201

Granularity is a well-known topic in software development. Another common concept is encapsulation. There’s books, magazine articles, and blog posts about these topics going back decades (well, not the blog posts).

One challenge we’ve had recently is trying to apply these concepts to a Flex application that uses modules. We want a global data model that modules can access, but have no way to access its internal mechanisms (i.e. no public variables).

The Shell

The core idea is that we want a common data model loaded into a basic Flex app – the Shell – which has no functionality, in and of itself, other than to load and unload modules that contain the business functionality.

This common data would then need to be accessible to every module that got loaded into the Shell – regardless of whether the module needed it or not.

module_communication

The Interface

First thing we need is establish an interface that each module can use to identify itself so that when the Shell loads it, the Shell recognizes that it should send its global data model to the module.

People and places have had their names changed to protect their identity…

package com.widget.common.interfaces
{
   public interface ICoolModule
   {
      function getModuleController():Class
      function setGlobalModel(model:Class):void
   }
}

The key method defined for this mechanism is the setGlobalModel() method. This is the method that it called by the Shell (shown below) when it loads a module to ensure that each module has a reference to the global data model.

The CoolModule

I then extended the basic Flex Module class, so that I could implement the ICoolModule interface and provide basic functionality for accessing the global data model

CoolModule.as

package com.widget.common.components
{
   import com.widget.common.interfaces.ICoolModule;

   import mx.core.Singleton;
   import mx.modules.Module;

   public class CoolModule extends Module implements ICoolModule
   {
      [Bindable] public var _globalModel:*;

      public function CoolModule()
      {
         super();
      }

      public function getModuleController():Class
      {
         return null;
      }

      public function setGlobalModel(globalModelLocator:Class):void
      {
          _globalModel = Singleton.getInstance("IModelLocator") as globalModelLocator;
      }
   }
}

Now when I create a new module, it’s a CoolModule instead of the base Module.

Module1.mxml

<component:CoolModule xmlns:mx="http://www.adobe.com/2006/mxml"
    xmlns:component="com.widget.common.components.*">

Loading the Module

So now that we’ve set up things on the Module end, let’s see how the Shell actually injects its model into each Module. I’ve got a command that handles loading modules with a commandSuccess() method that handles the coupling (significantly stripped down version here just to show the relevent code).

LoadModuleCommand.as

package com.widget.thisApp.business.commands
{
   import com.widget.thisApp.model.ModelLocator;

   public class ModuleLoaderCommand extends AbstractCommand implements ICustomCommand
   {
      ...
      public function commandSuccess(event:Event):void
      {
         if(_component is ICoolModule)
         {
            var coolModule:ICoolModule = _component as ICoolModule;
            ...
            coolModule.setGlobalModel(ModelLocator);
         }
      }
      ...
   }
}

Now, for those who were paying attention, you may have figured out the key to all of this is that the ModelLocator is a singleton. As long as the module gets loaded into the current application and security domain…

module.load(ApplicationDomain.currentDomain, SecurityDomain.currentDomain);

…then it can access the Model singleton.

Conclusion

We’ve found that while module-parent, and module-module, communication is certainly not elegantly built into the Flex framework, it is possible with a little bit of grunt work. I’m hoping that as the Flex SDK matures and becomes more feature-rich that this feature become more robust.