The Holistic micro-architecture framework for building RIAs and RMAs (Rich Mobile Applications) "borrows" from Robotlegs, PureMVC, Cairngorm, SWIZ, AS3Signals and the SpringFramework. Each has one or two things that I really like about it, but what I wanted is to bring all these things into one simple, very lightweight framework that enables me to quickly and more importantly methodically build mobile, web and desktop applications. This Holistic API is designed to work on top of either the Flex web or mobile framework and takes advantage of the Flex compiler, environment and lifecycle. Several "design patterns" are utilized in the API, such as Model-View-Controller, Loose Coupling, Locator, Usage of Interface, Delegation, Dependency Injection, Separation of Concerns and Inversion Of Control, not sure if some of latter are pure design patterns per GoF, but bear with me :-) One thing that is heavily relied on, is programming to convention versus programming to configuration.
Looking at the above diagram, the state of an application resides in the Model. The model is a set of non visual properties, where some properties are annotated using the [Bindable] metadata, in such that a change event will be dispatched whenever that property is mutated. This [Bindable] metadata is an indication that this property is represented by a view.
Now, if a view wants to modify the model, it does so using a controller. A view is not, I repeat, is not allowed to mutate the model. Only a controller is allowed to mutate the model. This is very important as a convention. All the logic to mutate the model should reside in a controller even if it means that the logic is a single line implementation. Trust me on this, when developing the application, in the beginning this might be a single line. But... along the application development process, this will get more elaborated per the application requirements. You will be tempted by the programming devils to "touch" the model from the view and you will come to regret it later on. So be resilient and do the right thing !
Enough preaching. So how does a view tell a controller to mutate the model ? Simple, using signals. A signal is a glorified event with integrated event dispatching enabling a loose coupling between the view and the controller.
The following is the signature of the static 'send' function in the Signal class:
Now, I need 'something' to receive that signal and act on it. This something is a controller. Again, relying on convention over configuration, I will create a controller named 'SubmitController'. Note the prefix 'Submit', it is the same as the signal type. Again this is the convention over configuration that is working in my favor where by writing pseudo-self documenting code. I can look at my list of controllers in my IDE and can tell immediately from the names what signal is handled by what class. Yes, I will have a lot of controllers, but this divide and conquer approach enables me to do one thing and one thing very well and separate my concerns.
In the controller class implementation, to handle the 'submit' signal, I should and must have a function named 'submit' that accepts one argument of type String like the following:
Applications need to communicate with the outside world, say for example you want to locate an address using an in-the-cloud-locator service. Controllers do not communicate with the outside world, they delegate that external communication to a service. That service will use the correct protocol and payload format to talk to the external service be SOAP, REST, RemoteService in XML, JSON or AMF or whatever. To enable different implementations of these protocols, an interface is declared and is injected into the controller for usage like as follows:
Ok, last but not least, unit testing. Actually, if you do TDD, that should be first. The holistic framework looks for simple interfaces, classes and functions, and with the built-in capabilities of unit testing and code coverage add-on to FlashBuilder, there is no excuse not to test your code. Whole books and articles have been written about Flex unit testing so google them.
Like usual all the source code is available here. I drink my own champagne, what you will find is the Flex unit test project that includes the holistic library.
Have fun.
Update: I created a very simple project that demonstrated the usage of the Holistic framework. As I said it is a simple application that displays a data grid that is bound to a list property in the model. Below the grid is a form that enables you to enter a first name and last name. When you click on the submit button, a signal is sent with the entered info. A handler will received the info and will delegate it to a service that uppercases the values and adds them to the list.
Looking at the above diagram, the state of an application resides in the Model. The model is a set of non visual properties, where some properties are annotated using the [Bindable] metadata, in such that a change event will be dispatched whenever that property is mutated. This [Bindable] metadata is an indication that this property is represented by a view.
public class Model { [Bindable] public var text:String; }Views are subclasses of UIComponents and are bound using curly braces (I call them 'magic' braces) to the Model to represent the state of the application based on the view capabilities.
<s:label text="{model.text}"/>In the above example, this Label instance is representing the model 'text' property and any changes to the 'text' value will be auto-magically shown in the label location on the screen. For a more complex example; a property that is a list of features can be bound to a map view and the features will be drawn as points on the map. At the same time, that same list can be bound to a data grid where each feature will be represented as a row in that grid. A model can have multiple view representations in an application.
Now, if a view wants to modify the model, it does so using a controller. A view is not, I repeat, is not allowed to mutate the model. Only a controller is allowed to mutate the model. This is very important as a convention. All the logic to mutate the model should reside in a controller even if it means that the logic is a single line implementation. Trust me on this, when developing the application, in the beginning this might be a single line. But... along the application development process, this will get more elaborated per the application requirements. You will be tempted by the programming devils to "touch" the model from the view and you will come to regret it later on. So be resilient and do the right thing !
Enough preaching. So how does a view tell a controller to mutate the model ? Simple, using signals. A signal is a glorified event with integrated event dispatching enabling a loose coupling between the view and the controller.
The following is the signature of the static 'send' function in the Signal class:
public static function send(type:String,...args):voidThe first required string argument is the signal type. This string is very important in our convention over configuration design as we will see later on. A signal can optionally carry additional information such as in the following example:
<s:textinput id="ti"/> <s:button click="Signal.send('submit',ti.text)" label="{model.text}"/>When the user clicks on the Button instance, a signal of type 'submit' is sent along with the text that was entered in the TextInput instance.
Now, I need 'something' to receive that signal and act on it. This something is a controller. Again, relying on convention over configuration, I will create a controller named 'SubmitController'. Note the prefix 'Submit', it is the same as the signal type. Again this is the convention over configuration that is working in my favor where by writing pseudo-self documenting code. I can look at my list of controllers in my IDE and can tell immediately from the names what signal is handled by what class. Yes, I will have a lot of controllers, but this divide and conquer approach enables me to do one thing and one thing very well and separate my concerns.
In the controller class implementation, to handle the 'submit' signal, I should and must have a function named 'submit' that accepts one argument of type String like the following:
[Signal] public function submit(text:String):void { ... }Note the [Signal] metadata on the function declaration. See, as a Flex developer, you are already familiar with and using the built-in annotations such as [Bindable]. But Flex enables a developer to create his/her own metadata that will be attached to the class in question for introspection, cool, eh ? Back to signals, one more example to solidify the association of signals to controllers - if you send a signal of the form:
Signal.send('foo', 123, 'text', new Date());To handle that signal, you should have the following controller declaration:
public class FooController { [Signal] public function foo( nume:Number, text:String, now:Date):void { ... } }Note that the order of the handler function arguments should match the order and type of the signal arguments. 123 -> nume, 'text' -> text, new Date() -> now. What makes this pretty neat is the independence of the hardwiring signal dispatching mechanism and the handler is just a function that can be unit tested, more on that later.
Applications need to communicate with the outside world, say for example you want to locate an address using an in-the-cloud-locator service. Controllers do not communicate with the outside world, they delegate that external communication to a service. That service will use the correct protocol and payload format to talk to the external service be SOAP, REST, RemoteService in XML, JSON or AMF or whatever. To enable different implementations of these protocols, an interface is declared and is injected into the controller for usage like as follows:
public class LocateController { [Inject] public var locateService:ILocateService; [Signal] public function locate(address:String):void { locateService.locate(address, new AsyncResponder(resultHandler, faultHandler)); } }The locateService variable is assigned at runtime using inversion of control and when the 'locate' signal is sent, it is handled by the 'locate' function who delegates it to the ILocateService implementation. The [Inject] metadata is for more than injecting service implementations. Here is another usage to overcome AS3 language constraints and make your code more testable. Say you start a project and Signal A is sent, you go and you write Controller A to handle the signal. Now you have to write another controller B to handle signal B (remember SoC :-) but you find that Controller A and B will share some code. Since you are a good OO developer, you create a super class S that has the common code and make Controller A and Controller B subclass S. You feeling pretty good, onto Controller C to handle signal C. But wait a minute, some code from Controller B can be shared with Controller C. Ok, you create a super class D and subclass. But wait a minute..., AS3 is a single inheritance model, than means Controller B cannot subclass super class S and D at the same time. This is where composition is better that inheritance where now I can move the common code to class S and class D and inject those classes into controller A,B and C.
public class AController { [Inject] public var refS:ClassS; [Signal] public function doA(val:*):void { refS.doS(val); } } public class BController { [Inject] public var refD:ClassD; [Inject] public var refS:ClassS; [Signal] public function doB(val:*):void { refS.doS(val); refD.doD(val); } } public class CController { [Inject] public var refD:ClassD; [Signal] public function doC(val:*):void { refD.doD(val); } }Cool ? Onward, something _has_ to wire all these pieces together and that something is a Registry instance that is declared in the main application mxml as follows:
<fx:Declarations> <h:Registry id="registry"> ... </h:Registry> </fx:Declarations>The children of the Registry are all the application controllers and all injectable delegates and services. So using the above example:
<h:Registry id="registry"> <m:Model/> <c:ClassS/> <c:ClassD/> <s:AController/> <s:BController/> <s:CController/> </h:Registry>Taking advantage of the declarative nature of Flex, I declare the registry children that gets translated into ActionScript instantiation, whereupon creation completion, the registry will introspect each child for [Inject] metadata and invokes the setter with the appropriate type instances. Next, the [Signal] metadata are located and a proxy object is created wrapping the annotated function as event listener to the Signals (remember, signals are nothing more than glorified events). All this introspection by the Registry is perform using the as3-commons-reflect library (url). Going back to programing to interfaces and having multiple implementation of an interface in the Registry, how is the injection resolved ? Well, by default the first implementation is injected. But what if I want a specific implementation ? here is the solution:
<h:Registry> <c:RestService/> <c:SoapService id="soapService"/> <c:FooController/> <c:BarController/> </h:Registry> [Register(name="restService")] public class RestService Implements IService { ... } public class FooController { [Inject] public var restService:IService; ... } public class BarController { [Inject(name="soapService")] public var service:IService; ... }There is a lot packed in this example and there is a lot of conventions, so stay with me. The registry is declared with a couple of services and controllers. Note that the SoapController is registered with the "soapController" id. This enables the BarController to be injected with that specific implementation of the IService interface via the name attribute in the inject metadata. Next, the RestService is registered with the Registry with the name "restService" as declared in the class metadata. Now (magic time), the FooController is injected with the RestService instance despite the absence of the name attribute in the inject metadata because the _variable_ name is same as the class registration. Pretty powerful, I know, mind blowing!
Ok, last but not least, unit testing. Actually, if you do TDD, that should be first. The holistic framework looks for simple interfaces, classes and functions, and with the built-in capabilities of unit testing and code coverage add-on to FlashBuilder, there is no excuse not to test your code. Whole books and articles have been written about Flex unit testing so google them.
Like usual all the source code is available here. I drink my own champagne, what you will find is the Flex unit test project that includes the holistic library.
Have fun.
Update: I created a very simple project that demonstrated the usage of the Holistic framework. As I said it is a simple application that displays a data grid that is bound to a list property in the model. Below the grid is a form that enables you to enter a first name and last name. When you click on the submit button, a signal is sent with the entered info. A handler will received the info and will delegate it to a service that uppercases the values and adds them to the list.
24 comments:
In the example above with an ILocateService injected into the LocateController, how is the ILocateService configured? For example, say the ILocateService uses Locator and requires a URL to a GeocodeServer, where would that URL be set?
Good question, the locator url will be in the Model as a property. See, you can inject the model into the GeocodeServer and set the locator url from it. I like this approach, as I usually have a config controller that sets some model properties from a config.xml file. I send a 'config' signal when the application is complete. Now sometimes, I suffer from a desease called 'Singletonitise' and I make my Model a singleton and I reference directly - so for example:
Hope this makes sense.
Hum - the sample did not show
<Locator url="{Model.instance.url}"/>
Very cool, thanks, this approach works perfectly for me.
What does your model look like?
I use a model with a singleton enforcer in the constructor, but this causes problems when declared as mxml in the registry. The compiler wants a no-arg, default constructor. If I take the singleton enforcer out it works, so is the enforcer even necessary with the registry convention?
package *
{
public class Model
{
private static var _model : Model;
public function Model( enforcer : SingletonEnforcer )
{}
public static function get instance() : Model
{
if( _model == null )
{
_model = new Model( new SingletonEnforcer());
}
return _model;
}
}
}
class SingletonEnforcer{}
I like to keep my life simple:
public class Model {
public static const instance:Model = new Model();
[Bindable]
public var foo:String;
...
}
Fast and easy - and yea does not prevent somebody from newing another instance - but this is where convention and all agreeing on common way of working is in my favor
This also enables me to do something like:
<s:Label text="{Model.instance.foo}"/>
The purist will freak out of my singleton usage - but works for me in this context.
This is great stuff, thank you.
After I get a project working, couple of hundred to a few thousand lines of code, it seems weird that I am afraid to go back into it later to modify it. Even after just a few weeks, the amount of time to figure out what, and how, I was doing something seems wasteful, especially knowing that I stand a chance of doing more harm than good. I shutter at the thought of larger projects with multiple programmers...
Just by breaking it up into smaller hunks ( MVCS), separating the logic from the display, and the idea of programming to convention, you are helping me tame some of the wild code I have been writing. Not to mention the possibility of unit testing, the advantages of applying better OOP practices, and the use of time tested design patterns. I also like the idea of programming to an interface, allowing me to decouple what I need from how I get it.
I haven’t even gotten to your Holistic framework yet, which at a glance ( and one little test program), seems more approachable ( shallower learning curve), and less intrusive to me then RobotLegs with Signals.
At what level should I be thinking about this framework: application, module, component, or all three? I guess what I am asking is can there be multiple registries, that only know about their own respective worlds, or does there need to be only one registry, introduced at the appropriate level ( Application or WindowedApplication, module, or component).
The specific cases I am thinking about are custom ESRI’s Flex Viewer widgets ( modules), and my custom components. These might be all the same question:
Okay to start using the framework at the widget level, that the viewer knows nothing about?
Can the widget have a separate registry, than the components it is built with?
Can each component have a separate registry?
Can I build an application ( or module), either using the framework or not, with components that were built separately using the framework, each with their own registry? The fantasy I am chasing here is building reusable components with multiple views for desktop and mobile.
Thanks again for being an advocate, a mentor, and going way beyond the call of duty. Your blogging of topics of interest to you is very helpful and entertaining to me.
Thanks and appreciate
As convention How must be use of Model? we have one Model such as ModelLocator in cairngrom or it is better to inject the Model?
BR
Farid Valipour
Hey...Awesome work here...
I am injecting controllers and models into views, but they always remain null. I am able to inject into my "master-view" which is added to the topLevelApplication directly, but not into the "sub-views" which comprise the master-view. Any ideas?
Thanks for any advice.
Ok, nevermind...I figured out my issue.
Basically it seems that if you are going to inject models/controllers into a sub-view..then you best be also injecting that sub-view into the main view. Problem solved.
I love the micro-framework and it has partially cured me of my singletonitis...
We are planning to show wind roses on map. Is it possible. We are Arcgis flex apis 2.4 and flex 4
sure - u must create however a custom symbol - check ou http://thunderheadxpler.blogspot.com/2008/10/graphic-custom-symbols.html
I just ran into a problem when I tried Signal.send('fetchProdcuts') instead of Signal.send('fetchProducts'). The method name was misspelled and nothing was getting called. It took me a little time to track down the typo. What do you think about throwing an exception when Signal.send finds no matching functions?
Right - this is the other side of the double edge sword that will cut you - try:
public static function send(type:String, ... valueObjects):void
{
if (hasListener(type))
{
SignalDispatcher.instance.dispatchEvent(new Signal(type, valueObjects));
}
else
{
throw new Error("No registered signal handler for '" + type + "'");
}
}
Mansour,
I know this post is a little old but I have been using your Holistic Framework for a while now and have recently noticed it causing issues with the new esri 3.4 api. Specifically its saying
Class mx.controls::DateField could not be found.
Class com.esri.ags.skins.fieldClasses::CalendarField could not be found.
These run errors seem to go away if I switch back to the 3.3 esri api. If you could possibly give any insight about how to fix this I would really appreciated it.
Thanks!
Just to clarify this is for a flex mobile project. Seems to work great with web project and 3.4 api.
The latest Flex API has a new skin for mobile development that does not rely anymore on any mx components - if the problem still persists - add mx.swf as dependent library item.
Hope this helps.
Thank you for your help! I was able to get it working and it turned out to be an issue in one of my controllers not the actual framework. I apologize for the inconvenience. Thanks for all you help!
Mansour,
I do have another question about the "proper" use of the model. Say I have a
[Bindable]
public var text:String;
in my model. I know I can get 'Singletonitise' and do
< Label text="{Model.instance.text}"/>
What would be the correct or "Purist" way in setting the text for my label instance in my view? And what exactly would I put between the curly braces in the text field for my label besides {Model.instance.text}?
Really I am trying to understand how I would proceed if I didn't make my Model a singleton. I am not very clear on that part.
So the "pure" way is to define a Model instance in the registry and then the model is "injected" into the controller and the view - in the current implementation, it is easy to do for controllers but not for views (will explain in a bit) - here is some pseudo-code
<Registry>
<Model id="model"/>
<MyController/>
</Registry>
In the controller code, u will do something like
class MyController {
[Inject]
public var model:Model;
...
}
Since the controller lifecycle is "managed" by the registry, then the injection is easy - this is not the same for views. In a pure way, it would be ideal to do the same:
<MyView>
<script>
[Inject] public var model:Mode;
</script>
<s:Label text="{model.myText}"/>
</MyView>
see how nice ? but....who does the "injection" ??? some framework rely on the added event - but that is very heavy as added gets fired all the time - I decided the take the easy way with views and Singleton it - some people argue that it makes the Model "big" - I have yet to see a problem - but then I am biased :-) If you find a better and cleaner way to inject - I can adjust the framework - the one think I do not like about the Singleton, it that it makes testing a pain and not "clean"
Hope this helps
That was a big help! Thanks for that. I think using the singleton is much easier!
Post a Comment