Adding Dependency Injection to Cairngorm 2.x via SwiftSuspenders (or Robotlegs) for Middle Tier Mocking

Introduction

The following discusses how to utilize Dependency Injection in the Cairngorm 2.x framework, explains why you’d want to do this, and shows the code you need. Included is a brief explanation of how you Mock or “fake” a backend until it’s ready so you can build your Flex app without waiting for the web services to be ready.

NOTE: I do not professionally recommend you do the following integration outlined in this article. Instead, I suggest you utilize Robotlegs or Parsley at the start of any Flex application of large size. They, and other frameworks of their ilk (Swiz, PureMVC, etc), have solved many glaring architecture problems Cairngorm has over the past 7 years. There is a reason Cairngorm 3 is a bunch of good ideas, and not a framework. Adobe recommends you use Cairngorm 3 with one of the aforementioned frameworks.

Context

Your project uses Cairngorm 2.x. Your back-end services aren’t done yet. Your team has a lot of GUI code to write. Ya’ll cannot wait for the middle tier to be done to actually move forward. The way to solve this is to use Mocks; classes that fake your middle tier. They’ll take a few seconds to respond, use the same methods, dispatch the same events, and return real ValueObjects. This allows you to write real GUI and Application logic code without having to wait on your server to work. If you’re server works, and then it turns out to be premature (i.e. “That isn’t supposed to happen… worked for me…”), you can go back to your Mocks until it’s “really” ready.

Mocking a Middle Tier

The easiest way to do this strategy is via coding by contract and Dependency Injection. You code to interfaces instead of concrete implementations (you use ILoginService vs LoginService). Your injection rules then have a switch: “real” and “fake”. If real is turned on, you hit a real server. If fake is turned on instead, you don’t hit anything. Both take a few seconds (read, more than 1 frame), and both return real ValueObjects. The fake ones do this by hardcoding the return values to something that would commonly be returned.

Examples include faking a login. The MockLoginService will take a hardcoded username and password and return a hardcoded UserVO. The real LoginService will take any username and password, and return a UserVO if successfully logged in that the server gave it.

Both use the same API. Both dispatch the exact same Event(s). Both expose the same return values. The key is the both implement the same ILoginService interface. This allows your DI framework to create the service for you vs. using Boolean or Conditional Compilation flags everywhere.

Example Login Service

Here’s an example of a real Login Service, specifically a Cairngorm Business Delegate that is assumed to have a predefined remoting-config.xml that defines your BlazeDS/Java services, and you access via a ServicesLocator/Services Singleton.

package com.jxl.services
{
   import mx.rpc.IResponder;
   import mx.rpc.AsyncToken;
   import mx.rpc.Responder;
   import mx.rpc.events.FaultEvent;
   import mx.rpc.events.ResultEvent;
   import mx.rpc.remoting.Operation;
   import mx.rpc.remoting.RemoteObject;

   public class LoginService implements ILoginService
   {

      private var responder:IResponder;
      private var service:RemoteObject;

      public function LoginService(responder:IResponder):void
      {
         this.responder    = responder;
         this.service      = ServiceLocator.instance.getRemoteObject( "LoginService" );
      }

      public function login(username:String, password:String):void
      {
         var operation:Operation    = service.getOperation("login") as Operation;
         var responder:Responder    = new Responder(onResult, onFault);
         var token:AsyncToken       = operation.send(username, password);
         token.addResponder(responder);
      }

      private function onResult(event:ResultEvent):void
      {
         responder.result(event);
      }

      private function onFault(event:FaultEvent):void
      {
         responder.fault(event);
      }
   }
}

Notice it takes your username and password, passes to the predefined service, and awaits a response from the passed in Responder.

Now here’s the fake one:

package com.jxl.services.mocks
{

   import flash.utils.setTimeout;

   import mx.rpc.IResponder;
   import mx.rpc.Responder;
   import mx.rpc.events.ResultEvent;

   public class MockLoginService implements ILoginService
   {

      private var responder:IResponder;

      public function MockLoginService(responder:IResponder):void
      {
         this.responder = responder;
      }

      public function login(username:String, password:String):void
      {
         setTimeout(onResult, 2 * 1000);
      }

      private function onResult():void
      {
         var fakeUser:UserVO    = new UserVO();
         fakeUser.firstName     = "Jesse";
         fakeUser.lastName      = "Warden";
         responder.result(new ResultEvent(ResultEvent.RESULT, false, true, fakeUser));
      }
   }
}

It, too, implements the ILoginService, as well as dispatching the ResultEvent to the passed in Responder. Notice it creates a fake user, doesn’t care what you pass in, and uses a 2 second timer to emulate a server call. This seems trivial, but a lot of people coming to ActionScript for the first time from other blocking languages such as C++ or Java assume the server will respond in the same block/stack of code vs. waiting for an event. Using this timer helps surface such workflow issues early.

And the ILoginService interface both of the above implement:

package com.jxl.services
{
   public interface ILoginService
   {
      function login(username:String, password:String):void;
   }
}

Using Boolean Constants

Before Dependency Injection frameworks, you’d use Boolean config variables in some Constants file:

package
{
   public class Constants
   {
      public static const USE_MOCKS:Boolean = true;
   }
}

And then implemented in your Cairngorm Command like so:

package com.jxl.commands
{
   import com.jxl.services.LoginService;
   import com.jxl.services.mocks.MockLoginService;
   import com.jxl.services.ILoginService;
   import com.jxl.models.LoginModel;
   import com.jxl.events.controller.LoginEvent;

   import com.adobe.cairngorm.commands.Command;
   import com.adobe.cairngorm.control.CairngormEvent;

   import mx.rpc.Responder;

   public class LoginCommand implements Command
   {
      private var responder:Responder;
      private var delegate:ILoginService;

      public function execute(event:CairngormEvent):void
      {
         responder                 = new Responder(onLoginSuccess, onLoginError);
         var loginEvent:LoginEvent = event as LoginEvent;

         if(Constants.USE_MOCKS == false)
         {
            delegate               = new LoginService(responder);
         }
         else
         {
            delegate               = new MockLoginService(responder);
         }

         delegate.login(loginEvent.username, loginEvent.password);
      }

      private function onLoginSuccess(event:ResultEvent):void
      {
         LoginModel.instance.loggedInUser = event.result as UserVO;
      }

      private function onLoginError(event:FaultEvent):void
      {
         trace("LoginCommand::onLoginError");
      }
   }
}

There are 3 problems with the above approach.

  1. It’s all or nothing. Once you set the Constants’ file USE_MOCKS variable to false, EVERYTHING is in production mode. A lot of times a critical service will go out of commission, or be updated. You don’t have the ability using the above to target specific Services/BusinessDelegates to be mocked, while the rest are still production.
  2. QA will often request certain services fail intentionally while others succeed; using the above prevents you from giving QA that ability.
  3. You have if/then’s everywhere. This can lead to code bloat, specifically in Commands that actually handle multiple events vs. single event types (see Point #6)… or Commands that handle the service call chaining themselves (similar to Robotlegs AsyncCommand’s that choose not to fork, but instead handle everything internally (+ rollback if needed).

Using Conditional Compilation

Same as the above, but uses constants defined by the compiler to determine whether to even compile in certain code. Wherever you configure mxmlc, it’d look something like this:

-define=SERVICES::usemocks,true

And then in your Cairngorm Command:

if(SERVICES::usemocks == false)
   delegate = new LoginService(responder);

if(SERVICES::usemocks == true)
   delegate = new MockLoginService(responder);

Just as ghetto; you still have C-like #ifdef’s everywhere. Gross. Manual. Prone to error. Still globally set on or off; all or nothing. Additionally, requires you to configure the compiler vs. “just code”. More crud to setup, maintain, and ensure goes into your automated build configuration.

Using Dependency Injection via SwiftSuspenders

NOTE: You don’t necessarily have to use SwiftSuspenders, or even Robotlegs. I’m just using them in this example because I know them.

Dependency Injection, also know as Inversion of Control, or IoC is a way to have your dependencies injected into your class for you. It’s the “new ‘new'”. Instead of you going:

delegate = new LoginService(responder);

This is done for you; you never write new Something again for your dependencies. Now generally, it’s fine to code to Concrete implementations. But hopefully, you can see how coding by contract (ie using ILoginService) above allows you to configure WHICH Login Service to use; the real one or the fake one.

This is where DI comes in. You configure that stuff in one place; globally, or on a case by case basis, usually using ActionScript. An example, this time using the same Constants variable and an already made SwiftSuspenders injector:

if(Constants.USE_MOCKS == false)
{
   injector.mapClass(ILoginService, LoginService);
}
else
{
   injector.mapClass(ILoginService, MockLoginService);
}

As you can see, this accomplishes a few things.

First, it makes your configuration rules DRY. Instead of having if/then’s or SERVICES::usemocks everywhere, you do it all in one place/class. Second, you can choose to turn it off for specific services. Third, anywhere you utilize ILoginService, this injection rule will run, allowing you to use the same rules in unit tests (although you shouldn’t, hehe), and any other place you’re coding by contract. It also keeps your code concise; it’s just coding to the interface; it has no clue what real type it actually is (LoginService or MockLoginService).

For our purposes, this is perfect for setting up and configuring mocks. We can configure our entire application to use real services once they come online… or just 1 at a time, all in one place.

DI in Cairngorm

Cairngorm has a prescribed way of writing your service layer (sort of… ok not really). Thus it’s implied you’ll be using BusinessDelegates to connect to your back end and get your data. Additionally, Cairngorm prescribes you utilize Commands to their interface to instantiate and utilize those Services/BusinessDelegates.

Cairngorm 2.x does not have a documented way of doing Dependency Injection. There is a SpringAS adapter for Cairngorm David Buhler told me about on Facebook, but I haven’t investigated it myself.

There are 3 challenges you need to be aware of:

  1. Cairngorm is typically deployed as a SWC; a library. Thus, you don’t have access to the source to modify. You could, but this makes people nervous.
  2. Cairngorm creates Commands internally in it’s FrontController.
  3. BusinessDelegates utilize an IResponder for their constructors (by convention), which would require you to utilize constructor injection. While it’s supported, it’s confusing for people who haven’t done it before. Even so, mx.rpc.Responders in turn have a specific constructor only approach to creation. In short, it’s “just easier” to modify the convention of setting responders on BusinessDelegates after they are created via getter/setters vs. using Alan Shaw’s Factory injection rule modification… and even that doesn’t work at the instance level where it’s most often inside of Commands anyway. More on this in a bit.

So we can’t modify Cairngorm’s source code, we need to inject our dependencies manually into Cairngorm Commands, and we need to slightly modify the Cairngorm BusinessDelegate convention to use a public setter vs. a constructor parameter.

No problem. Here’s the 3 steps you need to follow.

Step 1: Setup Your Injection Rules

You need to setup your injection rules. Since I’m comfortable using SwiftSuspenders, and Robotlegs already does most of the hard work inside it’s Context class, I’ll just use that; I create a MainContext.as class that sets up my rules and instantiate it in my main Flex Application class.

Here’s the class (again, you’re welcome to use your own ActionScript DI library like Swiz/Parsely or any Guice/Spring derivatives):

package
{
   import com.jxl.events.controller.AppEvent

   import flash.display.DisplayObjectContainer;

   import org.robotlegs.mvcs.Context;

   [Event(name="applicationReady", type="com.jxl.events.controller.AppEvent")]
   public class MainContext extends Context
   {
      public function MainContext(contextView:DisplayObjectContainer=null, autoStartup:Boolean=true)
      {
         super(contextView, autoStartup);
      }

      public override function startup():void
      {
         if(Constants.USE_MOCKS == false)
         {
            injector.mapClass(ILoginService, LoginService);
         }
         else
         {
            injector.mapClass(ILoginService, MockLoginService);
         }
         var appEvent:AppEvent = new AppEvent(AppEvent.APP_READY);
         appEvent.injector     = injector;
         dispatchEvent(appEvent);
      }
   }
}

Step 2: Give Cairngorm Injection Powers

Most of the work in a Cairngorm application is done in Commands. This is where we’ll need to inject the majority of our dependencies. In Cairngorm, you extend its FrontController class to basically wire up CairngormEvents that are dispatched to run certain Commands. Internally, it’ll create & execute these command classes for you. We need to override this behavior so we can inject our dependencies before the Command’s execute method is called. This implies the class that extends FrontController even has an injector, which it doesn’t.

So how do we go about it?

  1. Give your FrontController.as sub-class an injector public property.
  2. give your FrontController an injector instance
  3. override executeCommand to inject into the Command class

You’ll notice in Step 1 I dispatch an “AppEvent” and attach the MainContext’s injector property. This is because the Cairngorm FrontController needs it, and MainContext makes that a protected property. Putting it on an event class allows us to know when it’s ready to use, and to smuggle it out of the class so others can use it.

First, make your public property addition to your FrontController sub-class:

public var injector:Injector;

Second, set it. I do this in the Flex 4 Application class, like so:

<fx:Declarations>
   <jxl:MainContext contextView="{this}" applicationReady="onInjectionsReady(event)" />
   <cairngorm:MainController />
</fx:Declarations>

<fx:Script><![CDATA[
   private function onAppReady(event:AppReady):void
   {
      mainController.injector = event.injector;
   }
]]></fx:Script>

Third, override executeCommand in your FrontController sub-class. It looks like this:

protected function executeCommand( event : CairngormEvent ) : void
{
   var commandToInitialise : Class = getCommand( event.type );
   var commandToExecute : ICommand = new commandToInitialise();

   commandToExecute.execute( event );
}

Make it look like this:

protected override function executeCommand( event : CairngormEvent ) : void
{
   var commandToInitialise : Class = getCommand( event.type );
   var commandToExecute : ICommand = new commandToInitialise();
   injector.injectInto(commandToExecute);
   commandToExecute.execute( event );
}

 

Step 3: Implement Your Commands & BusinessDelegates

First, here’s our modified LoginCommand. Notice 3 things. First, the delegate is now a public variable that has the [Inject] tag on top; it must be public otherwise the [Inject] tag won’t work. Second, notice we don’t instantiate delegate; we just assume (rightly so) it’s already instantiated. Third, and most important; we’re setting the responder directly on the Delegate vs. putting it in the constructor like a lot of Cairngorm examples do.

Remember, whether we’re using Mocks or a real server, the Command will stay the exact same.

package com.jxl.commands
{
   import com.jxl.services.LoginService;
   import com.jxl.services.mocks.MockLoginService;
   import com.jxl.services.ILoginService;
   import com.jxl.models.LoginModel;
   import com.jxl.events.controller.LoginEvent;

    import com.adobe.cairngorm.commands.Command;
    import com.adobe.cairngorm.control.CairngormEvent;

    import mx.rpc.Responder;

    public class LoginCommand implements Command
    {
      [Inject]
      public var delegate:ILoginService;

      public function execute(event:CairngormEvent):void
        {
         var loginEvent:LoginEvent = event as LoginEvent;

         delegate.responder = new Responder(onLoginSuccess, onLoginError);
         delegate.login(loginEvent.username, loginEvent.password);
        }

      private function onLoginSuccess(event:ResultEvent):void
      {
         LoginModel.instance.loggedInUser = event.result as UserVO;
      }

      private function onLoginError(event:FaultEvent):void
      {
         trace("LoginCommand::onLoginError");
      }
    }
}

Second, just modify your BusinessDelegates on how they get IResponders. So, instead of this:

private var responder:IResponder;
private var service:RemoteObject;

public function LoginService(responder:IResponder):void
{
   this.responder = responder;
   this.service = ServiceLocator.instance.getRemoteObject( "LoginService" );
}

…do this:

public var responder:IResponder;
private var service:RemoteObject;

public function LoginService():void
{
   this.service = ServiceLocator.instance.getRemoteObject( "LoginService" );
}

Now, I’m one of those people who hates useless getter/setters to that don’t actually DO anything. That said, you can put getter/setters in interfaces to ensure your BusinessDelegates follow the contract. Up to you; find a convention you like.

…or just do constructor injection.

Conclusions

I’ve used Mocks on 3 projects now, 1 small, and 2 large and it has significantly improved my teams velocity in all 3 instances. Usually the person setting up the Mocks tends to be a bottleneck for a day or so, but once in place (and assuming you have some idea of the ValueObjects you need), your team can start moving immediately regardless of the state of your server.

This is helpful in Design Agency work where deadlines are non-negotiable, and the server team is just as stressed as you are. It’s useful in Enterprise situations especially where you might not have a working server + services + production-like testing environment for 5 Sprints. That’s at least 3 months with NO dependable services. I’ve seen it take way longer than that, as well.

Keep in mind too that if there are no injection rules defined, noting will happen. This is important if you’re converting an existing code base and want to help remove some of the dependencies from it. You can lay your DI framework on top, and Cairngorm wil still work the same. This is nice in that some Cairngorm code bases are some of the largest Flex code bases known to man (I guarantee you right now some Corvette driving Parsley proponent is claiming his is bigger).

Remember, you just: Setup your injection rules, give the FrontController an injector, and override executeCommand to use that injector to inject into the Command classes for their dependencies. The rest is convention.

7 Responses

  1. yep, but in that mock case I rather to do all the app frame work mapping via SwiftSuspenders XML or SpringActionScript, no conditions and job done…

  2. Mark Lapasa

    First, Cairngorm 2.x w/ DI? WTF? It’s so wrong on many levels that you have to put a disclaimer.

    Secondly, if your use case as I understand is that you want to simulate the backend for the purposes of mocking up the UI, why not have mock model that the views could be bound to. Dunno if you have monolithic model locator or one that is broken up, either way those should implement an interface that your mock model could implement as well.

    • Yep.

      Our Model’s aren’t talking to the back-end, the BusinessDelegates are. The Model’s, and the ValueObjects that are stored on it are real and match the back-end Java DTO’s. So, no problem there; this is real data, no need to mock it.

      When you’re server isn’t ready, or is unreliable, the BusinessDelegates become the bottleneck. This is my suggestion for how to fix it and ensure the team can move forward. Maybe I’m misunderstanding what you’re suggesting.

      • When you’re server isn’t ready or is unreliable, the lack of an available connector to a datasource (Which is the role of a Delegate in CG) is not the real bottleneck of driving the UI forward. It’s the absence of data to bind to which is the real problem.

        If someone was trying to mock up the delegate layer, it tells others that the encoding/decoding of data is indeterminate, whether AMF, SOAP, rest, URL Encoded, anything to do with transmission of data, etc.

        If you already have a domain model that defines the VO’s on the client side, the real bottleneck of a UI is solved by creating a mock model made of hard-coded dummy data. Because this is a mock model, it’ll likely implement a common interface. When it comes time to Integration, you can easily substitute the DummyModel for one that can populated live by the delegate because it implements a common interface. This approach is more useful when trying to do unit testing on commands as your ModelLocator can return you a mock model. Unit testing the delegate is less useful as it’s just a matter or not did the data get send over the wire or not, regardless of the content of the data.

  3. There are some really good patterns out there that address this problem space as well. I’ve seen Self Initializing Fakes be very successful for clients integrating with usable, but typically unreliable, services layers (http://martinfowler.com/bliki/SelfInitializingFake.html). Based on the level of decoupling (and unfortunately, complexity) you want to introduce, the Anti-Corruption Layer (ACL) Pattern from the DDD book is also a great example of an extreme to which you can go.

    You also mentioned unit testing for a bit, although you’ve identified this approach as not necessarily the ideal approach for testing. There are a few mock object frameworks out there for AS3 that make unit testing more bearable for injectable dependencies. mockolate (http://mockolate.org/) and mockito-flex (https://bitbucket.org/loomis/mockito-flex/wiki/Home) are the big ones that come to mind. Additionally, if you are looking just for stubs, instead of mocks, to unit test something like a service class that has-a HttpService or RemoteObject, the flexRpcStubs can help make that easier too (https://github.com/blegros/flexRpcStubs — disclaimer, I wrote this one).

    Thanks for keeping the community’s mind open about practices like these. It’s good to see others running into the same problems and finding similar solutions, regardless of the implementation.

    • I’m aware of Mockolate (she’s pretty advanced for this topic), but TOTALLY forgot about Mockito, and never heard of your RPC stubs. These are great, thank you very much!

      Yeah, I’ve done fakes too; basically save the AMF data as a ByteArray into a SharedObject, and thenceforth load the binary data. Wasn’t aware it was a pattern, haha. That link will be helpful in my consulting, thank you again!

  4. Ted

    I came across this concept when looking at Robotlegs and liked it then. Not a trained OO coder myself but practices like coding to Interfaces and using Value Objects should be learned early on imo. They are not as daunting as they might seem and learning them late can mean unlearning a lot of habits.

  5. [...] I found the solution on another post here. Very [...]