On Wed, 2011-05-25 at 18:52 +0200, Christophe Laprun wrote:
On May 25, 2011, at 6:06 PM, Matt Wringe wrote:
>>> Except that Ids don't have all the same formats so that prevents
>> generic methods. For example, you couldn't have
>> ContentRegistry.getContent("warName", "portletName") because
that
>> would conflict with ContentRegistry.getContent("invokerName",
>> "portletHandle") for the WSRP case.
>
> But that is just an implementation detail.
The devil is in the (implementation) details… :)
> applicationRegistry.getPortletRepository(LOCAL).getPortlet("warName",
> "portletName");
>
applicationRegistry.getPortletRepository(wsrp.SELF).getPortlet("portletHandle");
Except that getPortlet(String, String) wouldn't make any sense on a local
PortletRepository and the getPortlet(String) version wouldn't make any sense on the
remote version.
yes, which is why I said I need to rethink that part.
> [well, I guess if wsrp uses portletHandles, I need to relook at how my
> portlet repository class currently works]
One of the point of using Ids is that the same method works in all the different
contexts. So you don't need subclasses for specific cases, you might just need (if at
all) different implementations of the same interface.
>> Bypassing the Id object would only work in non-ambiguous cases and
>> I'm fine with doing so.
>
> We can always write the api so its going to be non-ambiguous with
> respect to this.
By making special cases all over the place, yes. Which is what I'm trying to avoid.
Keeping the API coherent and consistent, avoiding special cases for different content
types.
but using id's is a special case, or are you going to make everything
use an id?
>
>> However, there are cases where it wouldn't work. Also, again, it
>> would mean that each method that needs to work with an identifier
>> would need to first perform the intermediate step (though obviously
>> that code could be mutualized internally, but if we're going this
>> step, why not have Ids? ^_^).
>
>> Plus, T getElement(Id<T> id) would allow us to have typesafety at the
>> id and returned value level, something you wouldn't be able to do with
>> getElement(String... ).
>
>> This is a valid point. However, it's still not a very strong argument
>> against Id as separate objects. What is the big issue in your opinion
>> with having separate Id objects?
>
> Its cumbersome to always have to think and create ids to do something,
> when it can be done more directly without them. Its also confusing if
> some elements require these ids while others don't (ie a category
> doesn't need a separate id class, since it can be differentiated by a
> string).
>
> I don't think it makes sense to have things like
>
> Id id = Id.generateId("foo", "bar");
> Element element = something.getElement(id);
>
> instead of just:
> Element element = something.getElement("foo", "bar");
because you're assuming that getElement will always be able to construct an
unambiguous identifier given 2 strings, which as I've shown already doesn't work
with WSRP.
> Ok, so lets say we need to have more complex ids:
>
> PortletId pId = Id.generatePortletID("foo", "bar");
> WSRPId wId = Id.generateWSRPPortletId("foo", "bar");
> GadgetId gId = Id.generateGadgetID("foo", "bar");
>
> Portlet portlet = getApplication(pId);
> Portlet portlet = getApplication(wId);
> Gadget gadget = getApplication(gId);
>
> We can still handle it using something like:
>
> getApplication(Application.Portlet, "foo", "bar");
> getApplication(Application.WSRP, "foo", "bar");
> getApplication(Application.Gadget, "foo", "bar");
That last one could be a solution but it's not any cleaner than
getApplication(Id<Portlet> id) which will also have the benefits of allowing us to
do: Portlet portlet = getApplication(Id<Portlet> id) which your other solutions
(apart from enumerating all possible cases) don't.
> but in this case I would argue that we use getPortlet("foo",
"bar") and
> getGadget("foo","bar"). But for the wsrp case, its get a bit
more tricky
> (which is why I have multiple portlet repository classes).
Well, WSRP needs to be treated as a first class citizen. And we need
to make an API that would also accommodate other kinds of content
without to have to change it and make new special cases. Enumerating
over the known cases at a given point won't give us a very
future-proof API and lead into issues where code will make assumptions
that new content types we might support might not follow. I understand
that we shouldn't try to plan for every possible scenarios under the
sun but with what we currently have the String as Id approach is
already showing limits when encapsulating identifiers in a separate
object wouldn't cost much and lead to easier evolution of the API if
needed.
Another (and probably last) thing I will say in favor of encapsulating
identifiers in a separate object is that it makes it more natural to
use artificial identifiers (think SSN) instead of natural ones (think
last name).
I agree here, we shouldn't use natural identifier if it doesn't make
sense.
So for a user it makes more sense for the user object to have an id
associated with it (we can't use something like ssn, since that isn't
something a user should ever have to enter, we can't use their name
since there is potential for collisions, etc...). So we need to generate
a uuid for each user.
So, how will this work for the id case?
Id.generateUserID("firstName", "lastName") -> returns a user ID
How would this ID be generated in this case? We can't use a method like
that to generate a unique id. Even if we include things like phone
number and address, there could be a father and son with the same name
living in the same house, using the same telephone.
Ok, so lets say we generate and return unique random id each time
Id.generateUserID is called. Then this really doesn't work either as we
can never retrieve the user id for an existing user with this id. So we
can't do something like this (which should work)
UserID userId1 = Id.generateUserId("firstName", "lastName",...);
User user1 = createUser(userID1);
UserID userId2 = Id.generateUserId("firstName", "lastName",...);
// ^ using same values as userId1
User user2 = getUser(userId2);
// ^ this wont return user1
What we should be doing is this:
User user = createUser("firstName", "lastName", ...);
// during createUser a unique id is created for the user object.
UserId userId = user.getId();
User user2 = getUser(userId);
The only problem here is that we need to know the user id of a
particular user before we can retrieve that user (and that can't be
determined before hand). If we don't know their id, then we need to do a
query on all users and narrow it down that way.
For Users and certain other cases, we are going to have to do it this
way (or something similar to this). The id can be a UUID string, what
the id class is doesn't really matter in the above example.
The argument comes down to what happens if the ID can be determined by
one or more variables. So for example, we say a category has a unique
name, so generateCategoryId("CategoryName") will always return the same
result. Why not just use a natural identifier and use strings instead of
having separate ID classes?
So using the same setup as above (so its consistent):
Category category = createCategory("categoryA");
String categoryID = category.getId(); //which returns 'categoryA'
Category category2 = getCategory(categoryID);
but we don't really need the middle step, its a bit redundant, so we
could do this instead (which feels a bit more natural to me)
Category category = createCategory("categoryA");
Category category2 = getCategory("categoryA")
Using artificial identifiers has lots of benefits, one of which
being
stability in the face of renames, which is an important property for a
REST API (you don't want your URIs to change just because you've
renamed an entity to something else). It also allows objects to be
identified via other things than a name if needed (get a user id based
on an OAuth token for example) and the API won't change since it will
still deal with the Id class instead of needing to add yet another
version of the get methods to accommodate the new modality. Obviously,
that's not necessarily something we want/need to support but
considering that the cost of using Ids instead of Strings is pretty
low, I have yet to see a really compelling argument not to.
And with this, I won't argue about the value of having separate Ids anymore. I think
I've made my point. I agree that using Strings looks simpler but I worry that it might
actually make implementing the API more complex and less future-proof when the cost of
using separate Ids seems pretty low.
Cordialement / Best,
Chris
==
Principal Software Engineer / JBoss Enterprise Middleware Red Hat, Inc.
Follow GateIn:
http://blog.gatein.org /
http://twitter.com/gatein
Follow me:
http://metacosm.info/metacosm /
http://twitter.com/metacosm