Modular Applications (part 2)

So, back to where we were going before the digression on ApplicationDomain. One effective way of partitioning an application is to create an application shell that dynamically loads modules.

What exactly should a module look like?


There are a couple choices here. One possibility would be to just build a SWC, and extract out the SWF, and use it as a “bag of classes” via getDefinition. Not particularly elegant.Another possibility would be to build the module as an application. This is probably somewhat wasteful, because most applications carry some runtime infrastructure with them.Or, perhaps just build something extending a Flash class like Sprite or MovieClip. Not a bad possibility, but you’ll probably need to just use it as a bag of classes again, unless you intend to use your modules as pure visual elements.My preference is to build a new base class (lets call it “ModuleBase”!) that takes advantage of the semi-secret and bizarre [Frame] metadata that the Flex compiler uses to build multiframe Flash movies.What? You’ve never heard of [Frame]? Take a peek in mx.core.Application’s source, and you’ll see:

[Frame(factoryClass="mx.managers.SystemManager")]

or take a look in mx.core.SimpleApplication, and you’ll see:

[Frame(factoryClass="mx.core.FlexApplicationBootstrap")]

This metadata is a hint to the compiler that the class containing the metadata would like to be bootstrapped by the cited factory.In other words, when you build an application based on mx.core.Application, that isn’t actually the “root” class of the SWF. The root class is actually a compiler-generated subclass of mx.managers.SystemManager that overrides the info() method. Turn on “keep-generated-actionscript” during your compile, and you can see what the compiler generates.The rules are a bit weird, but basically: if the [Frame] metadata exists on the base class of your application, a subclass of the factory will be generated. If the metadata is directly on your application class, it will be honored, but no subclass will be generated; its assumed that you’ve already written the appropriate factory.When a frame factory class has been specified, the compiler will basically create a multi-frame movie, where the factory class is on the previous frame. It can arbitrarily daisy-chain these frames; you could (although why?) build a 10-frame bootstrapped movie this way. Note that the metadata is actually just an inline alias for the “frames” compiler configuration option, which lets you explicitly specify the frame classes.I personally like the notion of module SWFs being multiframe, because (a) it allows the module to use RSLs (which must be loaded before the main class), (b) it allows some interaction with the loaded content as soon as the INIT event fires, and (c) the generated factory subclass is how the compiler generates some information like the list of embedded fonts.Its really just personal preference. The one thing I do believe is useful, though, is to have the root class of the SWF be a factory class. So, if you do extend Sprite or MovieClip, consider implementing some sort of interface that can be used to create the classes within your module.To explore this concept, I’ve written the beginnings of a module system, with a demo that exercises it.Here is a ZIP file containing the demo.The demo uses a small utility library built around a ModuleManager singleton that handles the complexities of loading modules. Any given module URL is either in an unloaded or loaded state. After requesting that a module be loaded, an IModuleInfo interface is available. Once the module is loaded, a factory can be obtained that can be used to create an instance of whatever the module implements. The modules rely on a factory class to issue a “ready” event when it is safe to invoke the create() function.The demo exploits this system by having a shell application that loads an XML file containing elements that correspond to SWF file names; for example, if a “square” element is encountered, it tries to find “square.swf”, load it as a module, and then invoke the IRenderer interface on the module. Each module is simply expected to implement this small interface to draw itself.The code for this demo is pretty simple, and will evolve over time, hopefully into an actual supported library. For now, its just a small skunkworks project, so enjoy your advanced peek!

18 Responses to Modular Applications (part 2)

  1. James Lyon says:

    Awesome work, Roger. I was wondering what happened with your runtime module loading ideas.Quick question: Is this GMC6 or Beta3?

  2. lidija says:

    Thanks, nice approach. Could you post or send installation istructions for dummies? I can’t import it in FB2… Lidija[As far as I know, FlexBuilder doesn’t support “portable” importable projects, unfortunately. I suggest creating several projects; a SWC project that includes the library/src hierarchy, and AS projects for samples/demo/demoapp (demo.as), samples/modcircle (circle.as), and samples/modsquare (square.as). You’ll need to add the demoapp directory to the classpath in order to grab the common interface. Output to modules/samples/bin/ which is where a demo.xml file lives. –rg]

  3. lidija says:

    Thanks! L.

  4. Tom Baggett says:

    This is really good stuff! I definitely see the need for this kind of thing.Thanks for sharing and please, do continue in this direction.

  5. mike says:

    Thanks for taking the time to discuss aplication structure within flex. It’s an important topic sincs true RIA’s are much larger and more complex than the current examples.Taking a step back could you offer some insight into organization and structuring a group of flex projects and libraries. As I get more comfortable with flex I have started to pay close attention to file sizes and how flex manages merged code in a project. Here are some thoughts.The main flex library is just over 1 meg, if you add in charting it’s close to 2 megs.When you create a project and start adding applications to it even small ones are around 200 – 400 K each and they seem to each contain copies of the dependencies, so it appears that each app in the project contains merged code, the needed parts of the flex framework.So in a medium size app why not load / cache the framework libraries once and have apps that range from 10 – 20 K.In a project you would normally have a main app that is a loader for the child apps, so the child apps don’t need to contain the dependencies already loaded when the main app loads, however based on the file size it appears that each app contains it’s own copy of the parts of the framework it needs.So even better would be a way to have flex know that a main app will always be loaded first so it does not embed identical resources into each child app.[Keep in mind that SWC size is much larger than SWF size, and that the application will only end up linking in definitions that are needed.Having said that, you can build runtime shared libraries (RSLs) to hold definitions common to multiple applications. And, if you know that a main application is loaded first (as a parent) then you can tweak things such that the child applications use the definitions from the parent.The configuration options that you will care about are include-libraries, external-library-path, externs, and runtime-shared-libraries. Cheers!–rg]

  6. Mike says:

    I started creating libraries and linking as an RSL, and specifying the RSL URL to a root folder so the swf created from the swc is only loaded once, this is a bit of a pain since I cant seem to get the library compile to create the swf for me. The app compile aill do it with the auto extract, but it places the swf in the apps directory, and this is where it gets interesting…The app I am working on will have over 100 sub apps running in a main application dashboard, so I need to break the app into child apps each representing a module of related functionality.My thought is to structure an app as follows, in the webroot:[3 Library Projects]/RIA/LibraryMain/Library.swf used by all apps/RIA/Library1/Library1.swf used by child app 1/RIA/Library2/Library2.swf used by child app 2[Main Project]/RIA/App/main.swf/RIA/App/main-a.swf/RIA/App/main-b.swf[Child App 1 Project]/RIA/ChildApp1/childapp1.swf/RIA/ChildApp1/childapp1-a.swf/RIA/ChildApp1/childapp1-b.swf[Child App 2 Project]/RIA/ChildApp2/childapp2.swf/RIA/ChildApp2/childapp2-a.swf/RIA/ChildApp2/childapp2-b.swfOne issue is Flex Builder only allows applications to reside in the root, this is ok but inhibits organizing apps into folders, apps contain custom components which can be organized in folders and this is good but then each app gets a copy of all dependant custom components so the swf’s are larger and take longer to load.The second issue is in a very large app such as this it appears that it would be much more efficient to load the entire framework once, I did a test and changed framework.swc into an RSL link and the file sizes dropped from 200-400k to 10-20K, that was the eye opener.Since I am new to Flex maybe I haven’t seen the light here but I have read up on linking many times, and can’t get a handle on organizing a huge project.

  7. Max Rozenoer says:

    Great example, Roger!I came across this just as I was tackling the problem of a dynamically loadable custom UIComponent. The problem seems to be that when, in a Flex Project, in a you specify the top-level tag/class as something different from the Application, the Style classes for the children of the tag (e.g., if you have a Panel as a child in your custom component) do not get generated. This same problem seems to occur in the part of your example in which “app.mxml” attempts to load the custom “mymodule” from mymodule.swf: there is a null-reference crash in Panel.layoutChrome, because the Panel style is missing. Is this indeed a problem, or is my environment somehow setup incorrectly, and the example actually works fine?What is it that tells the compiler to generate/include the Style classes in the case when the top-level class is an Application? I just couldn’t find this… it’s not hard-coded into the compiler, is it? :)If the Panel is explicitly (“artificially”) included in the main application, then everything works. I have, however, ran into further problems in the dynamically loaded components, e.g., when a scrollbar appears, and the scrollbar tab is pressed, there is a crash in the Button.onMouseDown for the scrollbar tab that seems to result from the fact that the button cannot find the SystemManager.

  8. Hez says:

    This is a really great blog Roger, I’ve been tracking it with interest. I’m currently working on an extremely large flex project, 30 developers, 1000’s classes and a 1.5MB SWF file 🙁 We’ve been looking at various combinations of Loaders, RSls and other ways to reduce this size and felt for a time that we were ‘on our own’ with this, but its extremely encouraging to know that interal Flex people like yourselves have this in mind.Cheers

  9. Thierry Levieux says:

    Roger,Your favourite solution solves a lot of problems.While using the first solution (the SWC “bag of classes”) I met a lot of troubles.Mainly because the most part of the artifacts generated by the library compiler are never initialized while loading a library at runtime (i.e not “statically” linked to your parent application).So when we decide to use the first solution, keep in mind you cannot use MXML data binding or [RemoteClass] tag (use flash.net.registerClassAlias instead) for instance.Now, when we decide to use the last solution (your favourite) we have to keep in mind another issue: a class declared in an application module is only packaged in your SWF when it is referenced somewhere in your application.So be prepared to meet Runtime errors !See this post for further details:Sorry if these observations have already been noticed somewhere else in your blog…Rgds,Thierry

  10. Hi Roger,I’m very interesting in this topic, and I think is very needed since each enterprise application nowadays should be build in parts due to the complexity and number of use cases.So I was trying your code and is very inspirational, but I was thinking about it and we need more :).Could we expect some new post about this topic? Hope the answer is yes.If you do so, hope you plan to include the mechanims to remove the modules. I see and unload() method but there’s no support from ModuleLoader. Hope you add this in the next versión.As people digg more and more into flex they’ll need this project.Adobe should make as important as others like Cairngorm, etc…Many thanks and wait for the next step in this series :).Best,C.

  11. Another quetion about this topic. I believe this module package came before SWFLoader. Could you provide some little comparision between both techniques?. Maybe the SWFControls borrows some ideas from you classes?Hope you could throw some light here.Thanks again!C.

  12. Angus says:

    Hi Roger,I recompiled the projects in Flex Builder and have been testing. Demo works fine but when using the mymodule.mxml demo I get the following erro:VerifyError: Error #1014: Class flash.util::Timer could not be found. global$initJust wondering if there were some changes after the final Flex release.Any ideas?Thanks Angus

  13. Angus says:

    Hi Roger,I spent alot of time today going through the module code examples. Great stuff and thank you.One thing I tried was loading in a Flex 2 application swf as a module but it bombed out in ModuleLoader.moduleReady(). The factory.create() returned null. I then looked to see if I could grab contentLoaderInfo.content to use as the DisplayObject but no luck. Had coercion failed SystemManager to IUIComponent.How do you propose to handle importing modules that may be themselves applications such as in this case?Anyway I will continue tinkering. Thanks again. Angus

  14. Sermet Yucel says:

    Roger,Thanks for discussing this important issue. I am still confused about how and what this demo solves. I am strugling with two issue (as you noted them as well):1- When a large application is broken into smaller Applications or RSLs, we end up having a bigger overall application because swf/swc files embed dependent Flex classes.2- When dynamically loading the small applications as needed while making sure that they can communicate and reference each other, maybe name collisions occur.I have tried both child Applications and RSLs. It seems Flex always outsmarts me. Can you please provide some explanation of how this semi-secret feature help solve these problem. I noticed that your swf files are tiny. I cannot get them smaller than 200KB. Just curious why yours are so small. Some compiler options? Can we get much smaller files? Or with your approach one simply gets many apps which are marginally smaller?

  15. Erik says:

    Hey there,i was wondering a few things:1. How do you compile your applications, does the BaseModule compile as an application?2. A problem i was having with modules that were compiled to SWC files is that style definitions were missing, which messed up components like DataGrid. Does your method take care of this problem?3. I am now loading my modules (they extend mx.core.Application) via mx:SWFLoader instances. This causes them to not participate very well in the layout thingy. Does your method have this issue aswell?Greetz Erik

  16. Petar says:

    Hi Roger,I was looking for a way to build an application that has many different “pages” and i got directed to your modular entry on the blog. I Downloaded the demo and it seems to be working. Not too much that i understand, but it does load the squares and circles runtime.However, it looks quite complicated, and i can’t even start working on developing my own model on that structure. I was wondering if you can’t just use SWFLoader or something to load swf files just like FLASH does. Is there something wrong with it?Have you seen the ‘Flex Style Explorer’ from the start page?[Help->Flex Start Page] Isn’t there much easier way to reproduce this?ThanxPetar

  17. Vito Paine says:

    Roger,Thank you for this excellent post.I am very worried about what happens when our flex client gets too large and have been digging around to find a way to load libraries as needed. This module based approach really seems to be the solution. I downloaded the sample and got it running in FlexBuilder. I noticed one thing. You can step through the code in the shell but not in the modules!The ability to use the debugger is very important to our team and since most of the code would be in the modules this is a show stopper. I know you are a compiler guy but do you have any insight for me on this?Thank you,-Vito

  18. Meenakshi Makker says:

    Hi Roger,I tried to run the Max demo and after compile tweaks, get the following msgs. in console while debugging :-[SWF] D:\max_preso\max_preso\demos\portal\bin\WidgetShell-debug.swf – 14,761 bytes after decompression[SWF] D:\Modules\max_preso\max_preso\demos\portal\bin\StockWidget.swf – 7,061 bytes after decompressionparam count mismatchvirt params=0 optional=0 mx.core::IFlexModuleFactory/mx.core:IFlexModuleFactory::create()over params=1 optional=1 mx.core::IFlexModuleFactory/mx.core:IFlexModuleFactory::create()VerifyError: Error #1053: Illegal override of FlexModuleFactory in mx.core.FlexModuleFactory.at global$init()Can you throw some light, I have tried hard to play with the factory.create call in WidgetShell.as, but no luck,Please help.[Not positive, but I believe that a few of the APIs were tweaked between the beta release I used in the demo and the release available now. Double check the docs and look at the new signature of IFlexModuleFactory.create.–rg]