Hi G-Men,
Here's our use-case: our domain model does not do lazy-loading (we're using
Morphia with MongoDB, but if you're familiar with Objectify on AppEngine
–I'm not, I've just read the docs–, it would look very similar; except that
with Morphia we can opt-in to "managed relationships" and lazy-loading, we
just chose not to do so), but we need to retrieve related entities on the
client-side (using RequestFactory). We cannot (or "do not want to", if you
prefer) put the accessors for the related entity within the domain class as
we don't have a handle on the "entity manager" (we use DI all the way, I
don't want to introduce a static factory similar to
ObjectifyService.begin()).
A concrete example: we have "illustrations" and "files", a "file" can
contain several "illustrations" through "legends" (the "legend" has the
"file" as a "parent", and "links" to the "illustration", adding some data
such as a title). Each one of these: illustration, file and legend are
entities.
In Objectify, I think it'll map to something like:
public class Legend {
@Id String id;
@Parent Key<File> fileKey;
Key<Illustration> illustrationKey;
}
Second example: a "contribution" is a link between a "file" and a "person",
it adds the role of the contribution (was the person an author of the file?
the architect of the building described by the file? etc.)
To finish drawing the whole picture, we have forms to edit legends and
contributions: the ID of the legend/contribution is contained in the
Place/history token, the illustration/person can only be known by retrieving
the legend/contribution.
We don't want to do 2 requests (one for the legend/contribution, followed
by, now that we have its ID, one for the illustration/person), but we cannot
do a single one because we don't know the illustration/person's ID, and we
do not have a getIllustration()/getPerson() on the Legend/Contribution
domain objects.
We've solved it using a ServiceLayerDecorator that does "lazy loading" for
us: when the property "illustration" is asked for a Legend, we look for the
illustrationKey field, load the illustration and return it. That way, on the
client-side, we just have to do a .with("illustration") to get the
illustration associated with the legend.
As I said, we do not have a getIllustration() on the Legend domain class
(but we do have one on the LegendProxy client type), and this makes the
RequestFactoryInterfaceValidator bail.
We're working around it in two steps:
1. use @ProxyForName so the RequestFactoryGenerator compiles the client code
without looking at the domain classes
2. copy the whole ResolverServiceLayer, removing anything related to
RequestFactoryInterfaceGenerator; our copy then totally "overrides"/hides
the original ResolverServiceLayer.
Not having our proxies validated against our domain model isn't much of an
issue as we're generating one from the other.
The issue is that resolveClientType is not easy to implement, particularly
for types that only go from server to client; so we have to do our own
"bookmarking" in the resolveDomainMethod method.
Because this issue is likely to come in many projects using Objectify (and
not only our project, using Morphia), I therefore suggest adding an
annotation to selectively disable validation on methods. And by that I only
mean disabling the check that a corresponding method exists in the domain
class. You'd use it this way:
@ProxyFor(Legend.class)
public interface LegendProxy extends EntityProxy {
// We do have String actually, but that wouldn't be very diferent with
Key<?>
String getIllustrationId();
void setIllustrationId(String id);
@SkipInterfaceValidation
IllustrationProxy getIllustration();
@SkipInterfaceValidation
void setIllustratin(IllustrationProxy illustration);
}
Here, the RequestFactoryInterfaceValidator would validate the illustrationId
accessors against the Legend class, would descend into the IllustrationProxy
(in case it's not referenced directly by other methods where validation
hasn't been disabled), but would not check whether there are corresponding
getIllustration/setIllustration methods on the Legend class.
It would apply to both getters/setters on proxies, and request methods on
contexts. We could imagine putting it on the class as a shortcut to applying
it on each method.
I've already started working on a patch and it's fortunately a somewhat
small change (add flag to RFMethod, do not poison() the validator if the
flag is present, but perform all other checks otherwise).
What do you think?
--
http://groups.google.com/group/Google-Web-Toolkit-Contributors