Communicating between Mediator's.

shawn's Avatar

shawn

24 May, 2011 10:04 PM

According to the FAQ, communication between mediators is highly discouraged... but I'm not sure I buy the rationale.

It seems to me that there are cases where you do not want to broadcast an event over the shared event bus, an action may happen in one view, that I want to trigger an event in another SPECIFIC view, in that case dispatching a framework event causes all kinds of issues and sideeffects that I don't want. For example, I might have multiple views of the same type on stage, but they are storing different data sets, and I don't want to update them all...

Secondly, what is the issue, really, with coupling two mediator's together? Mediator's are inherently not reusable objects between projects, they are tightly coupled to both the application, and the view itself. Further, the coupling is only one-way, if a mediator exposes a public api, and another mediator calls it, we've created a single fairly minor coupling.

Obviously there are places where we want to use framework events, probably in most cases, but isn't there a very viable use case for not doing so? And instead, opening up a public API on the mediator's themselves?

Thoughts?

  1. Support Staff 1 Posted by creynders on 25 May, 2011 07:34 AM

    creynders's Avatar

    As you say

    an action may happen in one view, that I want to trigger an event in another SPECIFIC view

    this has nothing to do with mediators but view-to-view communication. There's a bunch of solutions for that: you could let an event bubble through the display list, or have the views tightly coupled, or...
    Mediators are only for framework-to-view and vice versa communication.

    IMO if you have a situation where multiple display objects MUST communicate directly with each other, chances are they actually constitute one view with one mediator.

    Your example of multiple views of the same type showing different data sets, can (should?) be solved w/o view-to-view communication, but by letting the mediator decide whether it needs to update its corresponding views dataset or not (based on a setting in the view)

  2. Support Staff 2 Posted by Stray on 25 May, 2011 07:40 AM

    Stray's Avatar

    Hi Shawn,

    (Just saw creyender's reply - which is also good!)

    a mediator shouldn't have an API, because the mediator is just a short-lived hook on the application layer for getting your view involved.

    Mediators should be state-free because of the disposal/re-creation when the view is removed and re-added. Don't think of the mediator is part of your view layer - the stateless, disposable, new-instance-each-time nature of mediators makes them unsuitable for use as view controllers.

    If you want to communicate between 2 mediators directly then presumably that's because you need to communicate between 2 views. It would sometimes be better to simply communicate between those views within the view layer - depending on the parent/child nature of those views.

    On the 'can't use events issue' - is there no way to use a more specific event? Or add a filtering property to those events? Many people have solved the 'targeting a specific instance' problem without resorting to wiring the mediators together.

    Finally - mediators are not singletons, they're disposable, so injection can result into a situation where the instance is no longer available for garbage collection (having been injected into another mediator), and then the instance that is created when the view hits the stage a second time is a new instance and is no longer the one you've injected.

    This leads to memory leaks, multiple-listener bugs and hard-to-diagnose bugs. It also - inevitably - leads to race-condition problems, because the mediators have to be created in a specific order so that the injection value is available. It's not trivial - for example - to inject a child view mediator into a parent view mediator, because the parent view mediator is created and requires the injection before the child view mediator is created.

    In addition, if you inject (into another mediator) a mediator for a view of which there are multiple instances, which of the many mediators of that class should be injected?

    For all those reasons, as of 1.7 (to be released shortly) mediators won't event be available for injection (you can inject into a mediator, but you can't inject the mediator into anything else) - we've already completely cleaned that from the mediatorMap in development forks (it was a side-effect and never intended behaviour).

    If you're still wishing to go down this path then may I suggest that you check out and adopt AS3 signals instead - they'll allow you to use a request-response pattern which opens up the possibility of more focussed communication. Search for request/response on here and you'll find quite a few descriptions of how to implement it using signals and/or events.

    Hopefully that's helpful (even if it doesn't fit with what you had wanted to hear),

    Thanks,

    Stray

  3. 3 Posted by Shawn Blais on 25 May, 2011 02:13 PM

    Shawn Blais's Avatar

    The reason I think it makes sense to talk to the mediator's directly, is
    because in our app the Mediator's are responsible for initiating and
    receiving service calls.

    I know Stray, that you advocate like "pure" mediators, but we're using them
    to also be our control layer in a lot of cases (seems to work fine).

    So, we have a set up where mediator's can catch framework event, start a
    service call, and pass the appropriate set of data back to the view. There
    are some cases where a view might trigger an event, which we want to
    triggers a load event in another sibling mediator, and it seems a waste to
    be dispatching that over the global event bus when I can get just grab the
    mediator for that specific view instance from the mediatorMap, and make a
    call on it.

    View > View communication doesn't really work, basically we would need to
    tell the view to dispatch en event, so the mediator could catch it, and then
    perform a load. That seems backwards.

    On Wed, May 25, 2011 at 1:40 AM, Stray <
    [email blocked]> wrote:

  4. 4 Posted by Shawn Blais on 25 May, 2011 02:17 PM

    Shawn Blais's Avatar

    For an example, think of one parent view, with 3 complex child views. When
    an event happens in one of the child views, we need to:
    1. Tell the parent mediator to switch views
    2. Tell the sibling mediator to start loading it's data

    What I'm proposing is that the parent mediator would catch the view event,
    switch views in the viewStack, and then grab the mediator for the new child,
    and say mediator.loadData(id);

    This is nice because when you look at the parent mediator class, you can
    easily tell what's going on, rather than this super loose coupling, where
    the parent dispatches an event to the global bus, which it's intended for
    one of it's immediate descendants.

    On Wed, May 25, 2011 at 1:40 AM, Stray <
    [email blocked]> wrote:

  5. Support Staff 5 Posted by Stray on 25 May, 2011 02:41 PM

    Stray's Avatar

    Hi Shawn,

    You're absolutely free to architect any way you like - just be aware that the next RL release doesn't support injecting mediators into other classes at all - it continues to be outside of how the framework was intended to work, and, having fully appreciated how unstable it is, we're now completely cleaning up those mediator mappings on the injector.

    The workaround will be to either patch your own version of the mediator map to reintroduce it, or do your own injector mapping in a command (you can get the mediator for that view from the mediatorMap). Or of course you can stick with the version you're running now - there's no really significant reason to update.

    It seems like the architecture you're going for is largely dictated by your earlier decision to bypass the command layer. If it works for you then it works - the guidelines are only based on our best attempt to lead people down a path that won't get them into trouble. We don't have any evangelical-style messages to preach - we're just basing them on our own experiences in building our own projects and in helping out on other projects that have run into trouble.

    You kind of pitched your question as if you wanted a discussion - but then it seems that really you're happy with what you're doing and not that interested in thinking about other ways to do it and why they might be useful... so I'm confused about what you were asking?

  6. 6 Posted by Shawn on 25 May, 2011 03:19 PM

    Shawn's Avatar

    Yep, we definately have our hands tied a bit because of previous architectural decisions, but there's no reason we cant use events to communicate between view tiers, I'm just not convinced as to the benefits. I'm looking for a discussion, but not a one sided one ;)

    I guess I was looking for a better explanation of the "why" behind why we should never access a mediator directly from another mediator. The FAQ's are little more than "don't do this", which doesn't work for me.

    I can see where using [Inject] of mediator's would be a complete mess, I'm not talking about that at all, I'm talking about retrieving a specific mediator for a specific instance of a view. Are you saying that the "mediatorMap.retrieveMediator(view);" API is going to be removed completely?

  7. 7 Posted by Shawn on 25 May, 2011 03:23 PM

    Shawn's Avatar

    And just to clarify, we are currently using a fully event based communication model, and can totally continue to do so.

    It's just that some members of the team have expressed concern that in some cases, this is excessive decoupling, and I kinda of agree ...

  8. 8 Posted by Stray on 25 May, 2011 03:43 PM

    Stray's Avatar

    The mediatorMap.retrieveMediator(view) is staying - it's only the ability to inject a mediator into another mediator that is going.

    So, I've already explained the garbage collection, memory leak, wrong-instance and repeated-handlers issues that are part of the 'don't do it' reasoning. Did those explanations not make sense? You didn't really respond to any of them.

    If your view never leaves the stage, and these mediators never get removed, these are of less concern. If your view does leave the stage, and your mediator is destroyed and a new one created, then you'll run into all of these problems. The specific instance you grab at one point in time is not guaranteed to be the same specific instance that is currently mediating this view.

    Retrieving and working with a specific mediator is appropriate within the short-lived thread of a Command executing, but it's dodgy outside of that situation, so creating persistent references is dangerous... do it at your peril.

  9. 9 Posted by Shawn Blais on 25 May, 2011 03:51 PM

    Shawn Blais's Avatar

    Well, I didn't respond cause I think most of those only would apply if we
    were lazily injecting mediator's all over the place...?

    We wouldn't have GC issues since we would not even be storing a reference to
    the mediator anywhere, simple grabbing it inside a function, and making a
    call on it, we can't get the wrong view, since again we're not injecting,
    we're grabbing the mediator for the specific child instance we want, at
    runtime, and making a call on it.

    I guess what I'm getting from this is, you need to be careful, but if done
    properly there's nothing really inherently wrong with calling a function on
    a mediator. You just need to implement it in a way that will not cause any
    sideeffects (which is pretty damn easy to do really).

    The only side effect that I can see that would apply to our implementation,
    is the coupling, and this is the one I just don't get, what's the big deal
    if you create a top-down one-way mediator coupling...

    On Wed, May 25, 2011 at 9:43 AM, Stray <
    [email blocked]> wrote:

  10. Support Staff 10 Posted by creynders on 25 May, 2011 05:11 PM

    creynders's Avatar

    TBH and with all respect, but this does sound a bit like someone who builds his house w/o inner walls, then uses some iron bars to hold the floors from crashing and wonders why that's really so bad, since - it holds, doesn't it?
    I mean, yeah sure, you can reference mediators from other mediators, you can basically do anything you want with RL, but what's the use of using a dependency injection framework if you're going for tight coupling anyway??

    Look, the reason why it's bad to call services directly from mediators is because it brings you into problems. What if another mediator needs that same data? Or if at some point in the future you actually need to call another service before that one? You'll be refactoring, refactoring and refactoring. You'll sink deeper and deeper, and sure, there are patches. There always are, just like having mediator-to-mediator communication is a patch, but it's nothing more than that.
    The idea behind avoiding tight coupling is NOT to make it easy while you're coding the use case as first defined. It's intended to avoid problems along the way, when use cases change, requirements change, services get ported, merged or split up, when customers decide they really prefer to have the views on a page-per-page basis instead of all together on one screen, et cetera ad infinitum. All of the above changes will bring you into troubles when you start using mediator-to-mediator communication.

    My sincere advice is - and I know it's a hard one - refactor it to use commands. Do it now, because the mess will only become worse and worse and worse and you'll end up with an application that behaves erratically, is not reusable, nor scalable, nor portable. And, yes, I had to learn it the hard way too, until I really understood that it's not about getting things done NOW, but it really is about having something that is flexible enough that future changes are easily made. These days I almost never refactor. I ADD things. That's only possible if none of your mediators have direct references to services and/or models.

  11. 11 Posted by Shawn on 25 May, 2011 06:50 PM

    Shawn's Avatar

    Unfortunately thats just not an option, we don't have the QA resources to handle such a major refactor, even if I wanted to I wouldn't get permission. The app is live, and doing well, and there's no way we can tear it apart at this point.

    I think you're exagerating what I'm proposing, I'm not saying lets couple everything together, I'm saying in specific instances, under a specific use case, it might make sense to call an API on the mediator itself. Thats all.

    And just to be clear, no where in the app currently do we do any mediator coupling at all. I'm just floating the idea...

    I'm not happy with our current service > mediator implementation either, but I'm stuck with it. For the most part it works fine, and the app is actually fairly robust and scalable. The nature of the app is that views almost never share data, so we haven't run into many issues. The biggest issue we have with this approach is when services complete, and the mediator is no longer in existence, definitely a major design flaw on my part.

  12. Support Staff 12 Posted by creynders on 26 May, 2011 07:37 AM

    creynders's Avatar

    Obviously if it's already released it would be hardly worth it, unless if it's an ongoing project.
    And yes, I did exaggerate - I've been known to do that once in a while :) - but it's a bit like using a global variable: you can get away with it, and it works faster, but you know it can get you into trouble.

  13. Stray closed this discussion on 10 Jun, 2011 03:53 PM.

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

Keyboard shortcuts

Generic

? Show this help
ESC Blurs the current field

Comment Form

r Focus the comment reply box
^ + ↩ Submit the comment

You can use Command ⌘ instead of Control ^ on Mac