Futurescale, Inc. PureMVC Home

The PureMVC Framework Code at the Speed of Thought


Over 10 years of community discussion and knowledge are maintained here as a read-only archive.

New discussions should be taken up in issues on the appropriate projects at https://github.com/PureMVC

Pages: [1]
Print
Author Topic: Can AsyncCommands act as responders?  (Read 13624 times)
psyflex
Jr. Member
**
Posts: 12


View Profile Email
« on: November 03, 2011, 09:52:41 »

I understand why SimpleCommand aren't,
But if an async command already waits for the onComplete, why can't it wait for a service call on the proxy?

So my question is if I'm calling a proxy from an AsyncCommand, and pass it the onComplete() as a callback function, is that considered a bad practice?


Thanks!
Shai
Logged
puremvc
Global Moderator
Hero Member
*****
Posts: 2871



View Profile WWW Email
« Reply #1 on: November 03, 2011, 01:03:26 »

Here's a trick using the PureMVC Observer class to do this in an OOP-y sort of way.

From your AsyncCommand, you want to pass a request to your Proxy and have it notify you back. But you'd like that to be decoupled, so that the caller (command) and the responder (proxy) are working off a formal request that encapsulates the callback.

In your your model tier, define a subclass of Observer, call it ServiceRequest. It should look something like this:

:
package com.me.myapp.model.request
{
import org.puremvc.as3.patterns.observer.Observer;

public class ServiceRequest extends Observer
{
                public static const RESULT_OK:String = "result/ok";
                public static const RESULT_FAULT:String = "result/fault";
                
                public var hasCallback:Boolean = false;
public var requestData:Object;
public var resultData:Object;

public function ServiceRequest( requestData:Object=null, callback:Function=null, caller:Object=null )
{
super( callback, caller );
                        this.requestData= requestData;
                        hasCallback = ( callback != null && caller != null );
}

}
}

Next, in your AsyncCommand, you have retrieved some Proxy subclass and want to invoke a service request. Also, you have a handleResult method defined on the AsyncCommand that takes a notification as an argument.

:
var request:ServiceRequest = new ServiceRequest( myRequestData, handleResult, this );
myServiceProxy.invokeService( request );

Now, in your Proxy, you'll process that request (here I use a Flex RemoteObject which implements the Asynchronous Token pattern. If you're not using a Flex service and don't have the benefit of the AsyncToken, then stash the request on a private Proxy variable and don't let another request go out if one exists. Then null the request reference after you tell it to notify its observer):

:
private var service:RemoteObject = new RemoteObject("MyService");

override public function onRegister():void
{
    service.myMethod.addEventListener("result", result);
    service.addEventListener("fault", fault);
}

public function invokeService( request:ServiceRequest ):void
{
    var token:AsyncToken =  service.myMethod( request.data );
    token.request = request;
}

private function result( event:ResultEvent ):void
{
    var token:AsyncToken =  event.token;
    token.request.resultData = event.result;
    var note:Notification = new Notification( ServiceRequest.RESULT_OK, token.request );
    if ( token.request.hasCallback ) token.request.notifyObserver( note );
}

private function fault( event:FaultEvent ):void
{
    var token:AsyncToken =  event.token;
    token.request.resultData = event.fault;
    var note:Notification = new Notification( ServiceRequest.RESULT_FAULT, token.request );
    if ( token.request.hasCallback ) token.request.notifyObserver( note );
}

Now, back in your AsyncCommand's handleRequest method, you'll receive a note with the ServiceRequest in the note body.

:
private function handleResult( note:INotification ):void
{
    var request:ServiceRequest = note.getBody() as ServiceRequest;
    switch ( note.getName() ) {

        // handle OK result here
        case ServiceRequest.RESULT_OK:
            var response:Object = request.data;
            // do stuff with the response. Or not...
            break;

        // handle Fault here
        case ServiceRequest.RESULT_FAULT:
            var fault:Fault = request.data as Fault;
            // do stuff with the fault, log or display...
            break;
    }
    onComplete();
}

Cheers,
-=Cliff>

PS: Note that this will also work from a SimpleCommand. The request has the reference to the command as well as the method, so the command will hang around even after execute is done. Just drop the onComplete() call in the handleResult method.

But if you're running a MacroCommand and want the next subcommand not to be executed until this command's handleResult method has completed, then yes, you want to be using AsyncMacroCommand and AsyncCommand for this.
« Last Edit: November 05, 2011, 08:46:50 by puremvc » Logged
psyflex
Jr. Member
**
Posts: 12


View Profile Email
« Reply #2 on: November 03, 2011, 01:25:19 »

First of all, Thanks for the full answer and the code examples,

It is really a pleasure to work on a framework that its founder put such great effort to help the community, Thank you Cliff !


What is the difference between this approach with using the AsyncCommand as a responder? and just passing and just implement an IResponder on the AsyncCommand (maybe subclass the AsyncCommand and create like a RequestAsyncComand) ?

What is the danger (so to speak) with using the AsyncCommand as a responder?

Thanks again Cliff for all that you do !
Shai
Logged
puremvc
Global Moderator
Hero Member
*****
Posts: 2871



View Profile WWW Email
« Reply #3 on: November 03, 2011, 02:09:06 »

There isn't a technical problem with it, sure, you can do that. I've done it. No problem.

But from a what-goes-where perspective, I prefer to keep all my domain logic (code related to representing and persisting the domain model) in the model package. That way I can easily repackage the model into a separate library and know that all the stuff necessary for using the model in another application is in that package.

I don't like to rely on the app (View and Controller tier classes) for domain logic, instead I have them focus on business logic (moving stuff between the View and Model tiers and occasionally doing some processing, focused usually on view organization as part of use cases).

If you muddle business logic and domain logic in your commands, (and within a single command) it makes it hard to repackage the model and associated domain logic without taking along code that is only relevant to the app it was originally written for.

Since repackaging of the Model for reuse in desktop, web, tablet, mobile and TV are becoming more and more common, this distinction between business and domain logic becomes more important in our apps.

-=Cliff>
« Last Edit: November 03, 2011, 03:38:45 by puremvc » Logged
psyflex
Jr. Member
**
Posts: 12


View Profile Email
« Reply #4 on: November 03, 2011, 04:41:21 »

Thanks Cliff,

So if I get it straight, the problem with using it as a direct responder is that the "saving to a var" and "optional parsing" of the data returned from the service is in the AsyncCommand and not the Proxy.

If I'm passing the AsyncCommand as a responder, but handling the service's result / fault on the proxy and when It finishes parsing or saving the data in the proxy, It calls the IResponder's (AsyncCommand) result / fault methods, isn't it the same solution, except maybe less elegant ?

Thanks again,
Shai
Logged
puremvc
Global Moderator
Hero Member
*****
Posts: 2871



View Profile WWW Email
« Reply #5 on: November 03, 2011, 06:13:34 »

It is preferable if the Command (which is performing business logic) be decoupled from persistence mechanism. That is a Model tier concern. The rest of the application (View and Controller tiers) should simply request data through the API presented by the Model tier. This means you can swap out that mechanism.

One day you may be requesting XML and parsing it into the properties of a custom value object class and visa-versa.

Later you may implement a WebORB, BlazeDS, or LCDS backend where you can just request and receive objects, automatically unmarshalled from the XML on result and ready to be used as a typed object.

So if you have code that was in a Command responding to this, (presumably in the middle of a View related use case) now you have to go back into your business logic and monkey around with domain logic. You have to change the result and fault handlers and the imports, etc.  And the Model isn't very reusable in another application, say a mobile version of your desktop app or whatever.

However if you encapsulate persistence and retrieval behind the wall of the Proxies, your application's business logic is not affected. The Command expects a value object and gets one from the Proxy in either case.

To make changes to your application such as this example, you know your Proxy will have to change, but why impact your business logic by getting its hands dirty with persistence issues when really all it wants is a piece of data?

-=Cliff>
« Last Edit: November 03, 2011, 06:17:56 by puremvc » Logged
psyflex
Jr. Member
**
Posts: 12


View Profile Email
« Reply #6 on: November 04, 2011, 01:01:17 »

I totally get that point Cliff, and it's a good one.

I think we are talking about the same idea, but with a different implementation.

I don't want to handle the result / fault directly in the Command, I want to handle them in the Proxy and just notify the Command that it has happen.

That's why I want to pass the AsyncCommand as IResponder just to notify it's Result / Fault function only after the proxy has persisted the data.

So instead of passing an observer, I pass the AsyncCommand, and instead of notify the observer I call the "result" / "fault" function of that AsyncCommand (as IResponder).

Isn't that the same in theory ?
Logged
puremvc
Global Moderator
Hero Member
*****
Posts: 2871



View Profile WWW Email
« Reply #7 on: November 04, 2011, 07:43:19 »

But the point is by implementing IResponder, you make the Command part of the persistence mechanism. And by passing the Command to the Proxy, you get the Proxy know classes that it shouldn't; the Controller tier should not be known to the Proxy. Always assume you're going to break the Model tier off into a library and reuse it in another app. If it knows a Command, it is tied to the app.

The point of the request object is to decouple the process such that the Proxy doesn't know anything it shouldn't, nor does the Command.

For example, If I go into a restaurant and order dinner from the waiter, I could just follow him back to the kitchen where he'll give the order to the cook, and I could just stand around until it was done, and then the cook could just give me my plate and I'd take it back to my own table. But that really isn't how we do it. I stay at my table and the waiter takes care of everything. I never know the cook, nor does she know me. The waiter could disappear into the back, run to the supermarket, pick up a frozen dinner, bring it back, microwave it, and bring out to me on a plate. There might not even be a 'cook' back there, maybe she's home sick, I don't know. As long as I get what I ordered and it's good, I don't care.

That's the sort've decoupling we're looking for between the Command, the Proxy, and whatever mechanism the Proxy uses for persistence.

-=Cliff>
Logged
psyflex
Jr. Member
**
Posts: 12


View Profile Email
« Reply #8 on: November 04, 2011, 08:13:38 »

In your solution, you gave the observer a reference to the Command, and passed the observer to Proxy.
So the reference to the command is still "getting to the kitchen" and waiting (because of the reference to "this" in the observer)

What I wrote is a ServiceRequestVO which gets as parameters a dataObject and an IResponder,
Once the answers gets back to the Proxy, it notifies the IResponder instead of the observer.

Maybe my choice of interface was wrong, as I understand from you that the IResponder interface should be used only in the model tier.
But except for that, I'm sorry, I don't understand what is the difference in practical terms.

The first solution -> the model knows about an observer (request) (which knows about the command)
The second solution -> the model knows about a requestVO (which knows about the IResponder, AKA the command)

I just trying to understand the difference, Sorry for being hard :)
Logged
puremvc
Global Moderator
Hero Member
*****
Posts: 2871



View Profile WWW Email
« Reply #9 on: November 04, 2011, 09:49:38 »

The first solution -> the model knows about an observer (request) (which knows about the command)

The Model knows about the Observer, not the Command. The reference to the Command is encapsulated in the Observer instance and not even reachable by the Proxy except indirectly by calling notifyObserver.

So carrying on the metaphor, the Waiter is in the kitchen, not the customer. The Waiter (Observer) knows where to send the food back to but the cook (Proxy) doesn't.

The ServiceRequest doesn't require you to implement a special interface associated with the Flex persistence classes with separate fault and result methods, only a single Notification handler method.

The second solution -> the model knows about a requestVO (which knows about the IResponder, AKA the command)

Also a Value Object ought to represent data in the domain model, not abstract application-related things like responders.

The point is the Command is forced to become a participant (by having to implement IResponder) in the persistence mechanism. Therefore, you cannot change that mechanism without affecting your Command.

The point of MVC is to make a separation of concerns that insulates your presentation logic, business logic, and domain logic from each other. So while there may appear to be no practical difference between solutions 1 and 2, the fallout comes in terms of legacy maintenance from lack of strict separation in day to day practice.

It's not about finding the quickest path to the data, its about getting the data to the actor who needs it in a decoupled way that doesn't muddle the roles, responsibilities, and collaborations of the actors involved.

-=Cliff>
« Last Edit: November 05, 2011, 08:13:23 by puremvc » Logged
psyflex
Jr. Member
**
Posts: 12


View Profile Email
« Reply #10 on: November 04, 2011, 05:50:04 »

I see what you mean now, so the objective is to not letting the proxy invoke specific methods of the controller tier.

Thank you for your patience and for teaching me (and the rest of the readers) a thing or two :)
Logged
puremvc
Global Moderator
Hero Member
*****
Posts: 2871



View Profile WWW Email
« Reply #11 on: November 05, 2011, 05:59:34 »

It's not just about keeping the Proxy out of the Command's business, but also visa-versa.
Logged
Pages: [1]
Print