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: How much work should a Proxy do?  (Read 16196 times)
jpwrunyan
Sr. Member
****
Posts: 84


View Profile WWW Email
« on: November 30, 2009, 12:55:26 »

I have a sort of general question about PureMVC Proxies...

Currently I am using proxies almost exclusively to get data from the server.  I want to have a 1-1 relationship between (for example) RemoteServiceXXXProxy and some specific RemoteServiceXXX (like a url request to get login data).

All of these requests take time to get a response.  I assume that this response logic should go in the Proxy.  For example, instantiate the URLLoader object and send its url request in the proxy constructor, and then register all relevant response events to handler methods also inside the Proxy.  Those handler methods just assign the result of the URLLoader to the Proxy's data and dispatch a notification, specific to the Proxy, that tells any observers that its data has been set (and handle errors, faults, etc).

But, in the actual application there are times we want to use a short-cut to get data off the proxy without writing stuff like (please disregard whether this data model seems daft):

var accountProxy:IProxy = facade.retrieveProxy(AccountProxy.NAME);
var data:Object = accountProxy.getData();
var currentUserName:String = data.accountVariables.userNames[data.accountVariables.currentUserId];

... with something like this:

var accountProxy:IProxy = facade.retrieveProxy(AccountProxy.NAME);
var currentUserName:String = accountProxy.getCurrentUserName();

... where getCurrentUserName() is a method inside the proxy that gets the variable we want.

My personal stance is one against doing this.  Mainly, I worry about bloating proxies with "convenient" get() methods that are perhaps used only by one Mediator in the entire application.  My opinion is that, verbose or not, Mediators should format from the raw data of the Proxy, or, if enough Mediators are using the same data (like the example above), either 1) make a new Proxy like CurrentUserProxy that gets set-up from data on AccountProxy or 2) make a utility class containing those common data access methods (although this is similarly vulnerable to method bloat).

But there's one more option, which is to have the Proxy format itself with data variables that are more conveniently arranged that the raw server result.  When I read the best practices document, I got the impression that it said this is a good idea.  Honestly, though, this seems to me to be no different than adding get() methods (especially since I know other programmers will start using AS getter/setter properties if I let them!).

Anyway, I'm leaning towards a rule to forbid methods (getter/setters included) other than remote data access retrievers, updaters, and handlers from being implemented inside proxies.  But, not having an MVC background, I may have a bias, so I want to see what the PureMVC community has to say about the issue.  What rules do most people follow when implementing Proxies?  Is there some completely different way I might go about this?
Logged
puremvc
Global Moderator
Hero Member
*****
Posts: 2871



View Profile WWW Email
« Reply #1 on: November 30, 2009, 12:50:58 »

It is certainly useful to have convenience methods on the proxies such as the one you described. But often, you'll also have a Value Object that acts as the data carrier and can allow you to pass the data around. This is useful when the proxy is managing a list of entities and the value object represents one of those entities.

Have a look at the Employee Admin1 demo for Flex.

Here is the UserProxy:

:
package org.puremvc.as3.demos.flex.employeeadmin.model
{
    import mx.collections.ArrayCollection;

    import org.puremvc.as3.interfaces.IProxy;
    import org.puremvc.as3.patterns.proxy.Proxy;

    import org.puremvc.as3.demos.flex.employeeadmin.model.vo.UserVO;
    import org.puremvc.as3.demos.flex.employeeadmin.model.enum.DeptEnum;

    public class UserProxy extends Proxy implements IProxy
    {
        public static const NAME:String = 'UserProxy';

        public function UserProxy( )
        {
            super( NAME, new ArrayCollection );

            // generate some test data           
            addItem( new UserVO('lstooge','Larry', 'Stooge', "larry@stooges.com", 'ijk456',DeptEnum.ACCT) );
            addItem( new UserVO('cstooge','Curly', 'Stooge', "curly@stooges.com", 'xyz987',DeptEnum.SALES) );
            addItem( new UserVO('mstooge','Moe', 'Stooge', "moe@stooges.com", 'abc123',DeptEnum.PLANT) );
        }

        // return data property cast to proper type
        public function get users():ArrayCollection
        {
            return data as ArrayCollection;
        }

        // add an item to the data   
        public function addItem( item:Object ):void
        {
            users.addItem( item );
        }
               
        // update an item in the data
        public function updateItem( item:Object ):void
        {
            var user:UserVO = item as UserVO;
            for ( var i:int=0; i<users.length; i++ )
            {
                if ( users[i].username == user.username ) {
                    users[i] = user;
                }
            }
        }
       
        // delete an item in the data
        public function deleteItem( item:Object ):void
        {
            var user:UserVO = item as UserVO;
            for ( var i:int=0; i<users.length; i++ )
            {
                if ( users[i].username == user.username ) {
                    users.removeItemAt(i);
                }
            }
        }
    }
}

And the UserVO:

:
package org.puremvc.as3.demos.flex.employeeadmin.model.vo
{
    import mx.collections.ArrayCollection;
    import org.puremvc.as3.demos.flex.employeeadmin.model.enum.DeptEnum;
   
    [Bindable]
    public class UserVO
    {
        public function UserVO ( uname:String=null,
                                 fname:String=null,
                                 lname:String=null,
                                 email:String=null,
                                 password:String=null,
                                 department:DeptEnum = null )
        {
            if( uname != null ) this.username = uname;
            if( fname != null ) this.fname = fname;
            if( lname != null ) this.lname = lname;
            if( email != null ) this.email = email;
            if( password != null ) this.password = password;
            if( department != null ) this.department = department;
        }
        public var username:String = '';
        public var fname:String = '';
        public var lname:String = '';
        public var email:String = '';
        public var password:String = '';
        public var department:DeptEnum = DeptEnum.NONE_SELECTED;
       
        public function get isValid():Boolean
        {
            return username != '' && password != '' && department != DeptEnum.NONE_SELECTED;
        }
       
        public function get givenName():String
        {
            return this.lname+', '+this.fname;
        }
       
    }
}

NOTE: The UserProxy  doesn't communicate with services, but if it did, it would also invoke the service and handle the response by creating a collection of UserVOs. Now, if you're using a backend like LiveCycle DS, BlazeDS or WebORB you can map the value objects on your server to value objects defined in ActionScript in your client.

If you don't have that luxury, the next best thing is your server returning XML, which ActionScript can understand natively. In that situation you might want to go with a 'Smart VO'2; a value object that wraps an XML object and exposes the attributes and elements as properties.

-=Cliff>
1 The Flex Employee Admin demo: http://trac.puremvc.org/Demo_AS3_Flex_EmployeeAdmin
2 See an example of a 'Smart VO' here: http://forums.puremvc.org/index.php?topic=1293.msg5973#msg5973

-=Cliff>
Logged
jpwrunyan
Sr. Member
****
Posts: 84


View Profile WWW Email
« Reply #2 on: December 02, 2009, 05:25:56 »

Thanks!  That's a way of thinking about it I had blocked off for some reason.  I guess I was trying to think in terms of there only being either Proxies or VO's in the approach to the data model. 

Although there might be exceptions, in most cases our problem is getting deeply nested data.  Making VO's for the Proxy to map less managable data onto makes great sense.  I guess it was just too obvious.

By the way, the XML approach is not possible.  The BlazeDS situation is probably most analogous to my situation.  No wait, now I see what you meant.  That's brilliant!
« Last Edit: December 03, 2009, 01:41:47 by jpwrunyan » Logged
jpwrunyan
Sr. Member
****
Posts: 84


View Profile WWW Email
« Reply #3 on: December 03, 2009, 12:39:50 »

... actually, I have a follow-up question about what a Proxy should do ...

I was looking over some source and found a place where a particular Proxy class (Let's call it AProxy) had a reference inside of it to another Proxy (Let's call it BProxy).  These two proxies' data share a relationship much the same way two tables in a database might.

Later in the source you have a Mediator class accessing BProxy data via its public reference on AProxy:

:
for (i = 0; i < AProxy.bProxy.someValueList.length; i++) {
  var someName:String = AProxy.bProxy.getSomeValueName(AProxy.bProxy.someValueList[i]);
  ...
}

Now, on the face of it, this just looks utterly daft to me.  But please disabuse me if that is not the case and in real life Proxies do indeed contain public references to each other.  By the way, the bProxy instance is not dynamic--it does not change during the course of run-time.  Looking at the PureMVC relationship diagram, it does indeed appear that Proxies are allowed to touch each other, but I thought that just meant, you know, if one proxy needed a value on another Proxy in order to initialize/update its own data it could go ahead and do so.

Anyway, what it seems to me should happen is that instead of bProxy instance being an actual Proxy extended class, it should be a simple VO type object.  AProxy can set up this VO from data it gets from BProxy upon construction.  But, and here's what I am sure the original author was thinking at the time, if I am going to set some VO on AProxy to data on BProxy, why not just make the reference to the BProxy itself and skip having to create a VO class?

I'm on the verge of rethinking this part of the application's model structure from scratch, but before opening that can of worms, I would greatly appreciate a second opinion.

In the meantime, I will crack open those links you gave earlier!  Thanks!
Logged
puremvc
Global Moderator
Hero Member
*****
Posts: 2871



View Profile WWW Email
« Reply #4 on: December 03, 2009, 10:37:45 »

:
var someName:String = AProxy.bProxy.getSomeValueName(AProxy.bProxy.someValueList[i]);Yes, this is daft. Something holding a reference to AProxy shouldn't be accessing its reference to BProxy. Any reference to BProxy by AProxy would be protected and for the purpose of lookup or perhaps to enforce relationship constraints.

For instance AProxy might be a list of Products and BProxy might be a list of Licenses. You might not want to allow deletion of a Product if a License exists for it. So, when told to delete a Product, the AProxy would consult the BProxy to see if there are any Licenses referencing the Product in question. Of course this check could be done in a Command *before* calling the AProxy's deleteProduct method, but the model tier will be more portable if it handles domain logic on its own.

-=Cliff>
Logged
jpwrunyan
Sr. Member
****
Posts: 84


View Profile WWW Email
« Reply #5 on: December 03, 2009, 06:00:36 »

I think I figured out what was going on.  I said above that the bProxy reference doesn't change once it's instantiated on AProxy, but the AProxy reference does change in the Mediator that uses it (it's typed as just an interface... which mandates a getter for a BProxy instance).  So anyway, for example, the AProxy might be a list of users for a department in the company and then the Mediator accesses an instance of AProxy for any given department and then wants a role list (BProxy) for that AProxy accessed by the bProxy reference.  So obviously, the accounting department would have different roles than the sales department, and so forth... well you probably get the picture.

So in the end it wasn't quite as crazy as I had originally thought (and wrote).  But it still seems ... sub-optimal.  On the other hand, if I were just writing this in a regular non-PureMVC environment, I could see myself doing the same thing.

Now, what I think would be better is to have a AListProxy to handle all the departments and a BListProxy to handle all the roles and then implement logic to get the right item from BListProxy for any given item in AListProxy. 

Finally, as far as those handy-dandy mutual references, if they are still for some reason absolutely necessary, those can be put on a VO that is used as a list item for either or both list proxies.  So now, instead of Proxies with references to other Proxies that are accessed by a Mediator, we have VO's with references to other VO's that are accessed by the Mediator--perhaps not the best OOP but hopefully at least better than what we've got now.

Again, at this point I want to at least simplify the use of PureMVC in our project, this should:
1 confine spaghetti code to the individual view classes and VO's as much as possible--so they may be dealt with later
2 make the application level framework sound, so new stuff can be added easily and consistently

Thanks again for your time and help
Logged
puremvc
Global Moderator
Hero Member
*****
Posts: 2871



View Profile WWW Email
« Reply #6 on: December 04, 2009, 09:18:53 »

For an example of handling this exact scenario (users with roles each with its own proxy), have a look at the Employee Admin Demo.

In that demo, I chose to keep the UserProxy and RoleProxy unaware of each other and to do coordination inside commands. For instance the DeleteUserCommand must remove the user from both the UserProxy where the user details are stored and the RoleProxy where the Users and their Roles are joined. Roles are fixed and so are stored on a RoleEnum.

http://trac.puremvc.org/Demo_AS3_Flex_EmployeeAdmin

-=Cliff>
Logged
jpwrunyan
Sr. Member
****
Posts: 84


View Profile WWW Email
« Reply #7 on: December 06, 2009, 07:04:06 »

I had looked over that example earlier.  It links the data in the departments to the data in roles by the shared value of userName, right?  If I were to (continue to) translate my situation into the User/Role/Department example, I would say that right now we have a unique singleton Proxy class for each department, with a common interface.  Then we have a unique singleton User class for each, uhm, actual user.  Yes, I know this sounds crazy.  It was perhaps stupid of me to try to re-state my problem in terms of the user/department/role example...

So let me try to start over going back to the abstract.

I have two types of Proxies, differentiated by interface:

IAProxy - for type A data (defines a getAData() method)
IBProxy - for type B data (defines a getBData() method)

And then I have a whole lot of server requests that get mapped into unique proxies...

A1Proxy
A2Proxy
A3Proxy
B1Proxy
B2Proxy
B3Proxy

The ratio between An to Bn is generally 1-1 but there are exceptions.

The A1Proxy is given its data, which comes from the "1" database "A" table, and so on for A2-An.
The B1Proxy is given its data, which comes from the "1" database "B" table, and so on...

Because the A1Proxy has its own unique class, it can do this inside its constructor:

:
public var bProxy:IBProxy
public function A1Proxy() {
   ...
   bProxy = B1Proxy(facade.retrieveProxy("B1Proxy"));
}

...and so do all the other A-type proxies...

And then a certain Mediator, not concerned with what database its data comes from; only concerned that it has A-type data and B-type data does something like this when it handles the notification "AProxyChanged":

:
aProxy = IAProxy(notification.getBody());
aData = aProxy.getAData();
bData = aProxy.bProxy.getBData();
for (i = 0; i < bData.someValueList.length; i++) {
  var someName:String = bData.someValueList[i]);
  ...
}

I am reasonably sold on the fact that the above proxies need to all be unique (for example, due to subtle differences between the databases, the fact that the requests are all separate and asynchronous, etc).

Maybe even the interfaces are ok, (for example, if the interface requires the getAData() and getBData() methods to return a typed VO).

But that proxy-reference-in-a-proxy still bugs me.  I know that the Mediator only gets the IAProxy reference in the notification but...

Maybe the notification should be mapped to a command instead of being responded to directly by the Mediator?  I think so...  The reason for this global "AProxyChanged" notification is that whenever any server request comes back in, it is automatically dispatched... this is wrong.  And besides, there has to be unique logic before that to place the correct AnProxy class in the notification anyway... it should just control the Mediator's data there as well, passing it both the A-type proxy data and the B-type proxy data... might not even need these IAProxy and IBProxy interfaces!  To reiterate:  the Mediator should not be responding to the "AProxyChanged" notification, it should be manipulated directly by one or more intervening commands!  Ah, the spaghetti unravels...

Well, I've drifted off my original concern into something very specific regarding the data model (and more?).  Thanks for listening anyway!
Logged
puremvc
Global Moderator
Hero Member
*****
Posts: 2871



View Profile WWW Email
« Reply #8 on: December 06, 2009, 09:10:10 »

:
aProxy = IAProxy(notification.getBody());
aData = aProxy.getAData();
bData = aProxy.bProxy.getBData();
This is the part that isn't good. Nothing handling the AProxy should reach into it to twiddle its reference to its BProxy reference.


I am reasonably sold on the fact that the above proxies need to all be unique (for example, due to subtle differences between the databases, the fact that the requests are all separate and asynchronous, etc).
Since all meaning of what A and B data types are has been stripped from the discussion, an understanding of whether this is true or not is difficult to make from this side of the fence. It seems likely to me that you could benefit from having services implement the Transfer Object Assembler pattern and send you complex objects, but of course you may not have control over the services.

Regardless, the idea of a separate Proxy for every object is generally less attractive than a Proxy for a collection which exposes methods for managing the items of the collection.

-=Cliff>
Logged
jpwrunyan
Sr. Member
****
Posts: 84


View Profile WWW Email
« Reply #9 on: December 07, 2009, 08:16:42 »

Thanks again for confirming what you can.  And sorry for being deliberately vague.  At the moment I just can't describe the system I am working on.

And I can't (yet) tell the server side how to deliver data, either.  I have to take what they give me and say I like it (I don't).

Anyway, surely more questions to come...
Logged
Pages: [1]
Print