how to approach View->Command->Service->View

Raed Atoui's Avatar

Raed Atoui

30 Apr, 2010 04:38 AM via web

lets say you have a View that triggers a command, which in turn calls a service, and then the result must be returned to that same view. The result is not intended to be stored in the model and is short lived.
In the simplest form., this can be achieved using 2 commands and 2 events, 1 event/command for triggering, 1 event/command for notifying the view via its mediator about the result
Now, lets say you have 30 views of that kind, the number of files starts to get large.

For example, a login panel, can have a few states,
1- login form
2- forgot password form
3- edit info form once the user logged in.

I would prefer to have a single view with a viewstack instead of having 3 views and 3 mediators.
But now, in regards to events/ commands, can i bundle this logic in one event with a few constants (Login, Invalid Login, ForgotPassword, UpdateUsername, etc...), and the command triggers the correct service call based on the event type?

I am sorry if this topic is redundant, but i am trying to figure out in which area should things scale.

  1. 2 Posted by Glidias on 30 Apr, 2010 06:37 AM

    Glidias's Avatar

    Yes, you can bundle multiple commands into one single event with string constant identifiers mapped to each command. I normally do this to keep class count as low as possible. Dawn framework tends to enforce 1 notification class-to-1 command class mapping unlike RobotLegs which allows you to simply map ANY event string to any command. To avoid configuration and errors with string-matching, I'd normally use a convention that simply allows you to list out all the commands required by the application, and the utliity simply finds a public [Inject]"event" variable of the Command, match the event class type to a constant string of the event logically like OpenFile/OpenFileCommand to 'EventClass.OPEN_FILE= "OPEN_FILE"' or 'EventClass.OPEN_FILE = "EventClass.OPEN_FILE"'. If the convention fails, I throw an error/warning.
    Example 1-liner to map all commands: @new AutoWireCommands(commandMap).setupCommands(OpenFile, CloseFile, JumpToCommand, etc.) ;@
    And example utility at: http://github.com/Glidias/SG-Camo-Collections/blob/master/src/sg/ca...

    If you don't intend to have the value stored in the model but just need to have a short-lived pingback result, the dispatched event can contain a public variable with an empty value initially. DIspatch the event through the event bus which triggers the command. The command accesses the service, which upon retrieving whatever returned values, supplies the payloaded short-lived result value back to the event's returnResult payload public variable. So, after dispatching the event, you can get the response back from the event object itself instead of accessing the data remotely from some model.

    For example, in a mediator class:

    bc. var transitionReq:TransitionModuleEvent = new TransitionModuleEvent(TransitionModule.REQUEST);
    dispatch(transitionReq);
    if (transitionReq.payload) {
    // do something with payload }

    Normally though, I'd try to avoid as much code-behind as possible in mediators, depending on value objects and middle-tier controllers/services to resolve value changes.

  2. 3 Posted by Jonny Reeves on 30 Apr, 2010 11:20 AM

    Jonny Reeves's Avatar

    Hi Raed,

    First off, I don't think it's a good idea to be triggering commands directly from Views; this should be the job of the Mediator instead.

    If you want to cut down on the number of Commands; you could consider injecting the Service directly into the Mediator.

    Your Mediator will then listen for events from the View which trigger the Service, and events from the Service (via the central / framework EventDispatcher) which get pushed back through to the view.

    You could either go with one class for each Service (ie: LoginService, ForgotPasswordService); or - if that will result in too many Classes for you (you can never have enough Classes IMHO!) you could switch on the eventType in the Mediator and then call a method on your Service class.

    Hope that helps?
    Jonny.

  3. Support Staff 4 Posted by Shaun Smith on 30 Apr, 2010 01:49 PM

    Shaun Smith's Avatar

    Hi Raed,

    My point of view is this: If it feels messy and un-scalable, it probably is.

    When dealing with transient data there is usually very little point to the whole View -> ViewEvent -> Mediator -> SystemEvent -> Command -> Service -> ServiceEvent -> Mediator -> View work-flow. That flow is useful for data and events that many parts of the system want to know about (or may want to know about in the future).

    There are times when it makes much more sense to simply inject a Service directly into a Mediator and call it from there. The exposed Service method might even accept a callback so that it can respond directly to the caller. I use the concept of "Promises" in such situations, which look something like this:

    remoteService.get('/users/' + userId)
        .addResultHandler(onUserResult)
        .addFaultHandler(onUserFault);
    

    In this case I need to load a User object from the server so that I can display it in a view. No other part of the system cares about this - I'm not putting that User into a model, or saving it for later, or anything like that. The Service is responsible for translating the servers result into a format that the system understands (in my case translating a JSON string into a bindable AS3 object).

    What I'm getting at is that Robotlegs shouldn't be getting in your way. The Best Practices document outlines some good work-flows for dealing with common scenarios, and it's a great starting point, but once you understand the underlying principals it's up to you to decide how appropriate the work-flows are for your particular problems.

    Getting back to your example, you could indeed get away with having one Mediator for a View component with 3 states. The Mediator could invoke service calls directly and pass the results straight to the view.

  4. 5 Posted by Raed Atoui on 30 Apr, 2010 02:35 PM

    Raed Atoui's Avatar

    @Jonny, yes things always go trough their mediators. Injecting the service into the Mediator sounds good, but i like how the Context offers somewhat of a top level view of the application wiring, and this would hide that away. but maybe i dont know to know about every possible user gesture in this top level view and just be satisfied with the presence of LoginService and LoginViewMediator. (It would be so awesome if RL+some eclipse plugin can study the Context and generate a block diagram :) )

    @ Glidias. That Autowire utility looks really nice. but feels risky. Ill look into it some more.

    @Shaun/Jonny. Yes, after all Mediators are actors and it makes perfect sense. Callbacks/Promise is making the most amount of sense to me

    The Best Practices is the first thing i read and it is really excellent.

    thank you so much for the feedback! Yay Community support.
    we can close this now

  5. 6 Posted by Nikos Katsikanis on 23 Dec, 2010 06:16 PM

    Nikos Katsikanis's Avatar

    "First off, I don't think it's a good idea to be triggering commands directly from Views; this should be the job of the Mediator instead."

    Is it ok for commands to act on injected meditators? or is it ok to inject the view into the command and perform work on the view that way?

  6. 7 Posted by Stray on 23 Dec, 2010 07:27 PM

    Stray's Avatar

    It's "Ok" to do pretty much anything.

    It's ideal to keep your view layer entirely separate from your application layer. Which means not injecting mediators into commands (your mediators should have no API anyway - they should only respond to events / signals). Other than the contextView, I wouldn't inject any view into a command.

    Work on the view should stay in the view. Application logic should stay in the application. Mediators dispatch and receive events and signals to join the two together.

  7. 8 Posted by Nikos Katsikanis on 23 Dec, 2010 10:39 PM

    Nikos Katsikanis's Avatar

    I see, How does one grab a hold of a specific mediator, since they are created automatically?

    I think I should listen to a signal dispatched from a command instead, how do I do that?

  8. 9 Posted by Abel de Beer on 24 Dec, 2010 08:44 PM

    Abel de Beer's Avatar

    @Nikos:

    You could create a custom signal class and map that as a singleton in your Context. This signal can now be injected into both your Command and your Mediator. The Command would dispatch the signal and your Mediator adds listeners to it to update its View component accordingly.

  9. 10 Posted by Nikos Katsikanis on 26 Dec, 2010 10:08 AM

    Nikos Katsikanis's Avatar

    Thanks Abel thats exactly what I will do :)

  10. Stray closed this discussion on 13 Feb, 2011 03:44 PM.

Comments are currently closed for this discussion. You can start a new one.