The Domain Model as REST Anti-pattern
Join the DZone community and get the full member experience.
Join For FreeToday JavaLobby published yet anotherdomain-model-as-RESTarticle, using Spring and Jersey. As already pointed out, this really is an anti-pattern, and is not RESTful, and cannot be so either.
The point that all of these types of articles miss is theHATEOASpart, the hyperlinking of resources. If you expose your domain model, basically saying "here's all I got, use as you see fit", there is no sensible way to create links between resources that expose the application state. There is no sensible way to tell the client "here's what you can do next", because the "REST" API allows anything at any time.
Here's an example, from my own app, which showed me the problem with this approach. I have users in my system. I need two ways to work with their passwords: the user must be allowed to change his own password, and the administrator shall be allowed to reset any users' password. In the beginning, when I was exposing my domain model, the URL's for this were as follows:
/user/<username>/changepassword/user/<username>/resetpassword
I'm just doing what the article suggests, which is to expose my domain model, and all that I can do with it. What's the problem with this? The first and most obvious problem is that it's very hard to determine who is allowed to do what here. I need to have authorization checks on both "changepassword" and "resetpassword". The first needs to check if it is the same user that accesses it, and the second needs to check if the accessing user has the administrator role. Also, since the client MUST get to these resources by following links in hypermedia (that's a constraint, remember?), the most obvious thing to do is to list them when accessing /user/<username>/. But then my link lists need to do these authorization checks as well, because if the user is not an administrator, then the "resetpassword" link should not be there. I should not allow clients to see links they cannot reasonably follow! My UI will also be quite complicated, because in one screen I might do "resetpassword" and a number of other administrative tasks, each of which uses different parts of the domain model, and so its exposure to the API, and consequently, its brittleness if the API changes, is immense. It's just a very bad situation, and one which you'll get into by following the guidelines in all of these expose-your-domain-model articles.
So what to do instead? The trick is to expose usecases instead. It's that simple. Now, the main problem with doing this is that you have to actually know what your usecases are! And this is probably why all of these articles do the domain-model anti-pattern: because of their simplified nature they only thought to the point of "we gotta have users in our system" and not take the next step "what can we do with them?", because then you need to decide a whole lot more about what your system does. Since articles need to be reasonably focused on one thing, they just don't go there. But for You, if you do that, you end up with the mess outlined above.
What did we do in our REST API to fix the above? We simply looked at our usecases, and rearranged the REST API accordingly. For the above, they relate to two different usecases which are account handling and user administration. And so we changed the API to something like this:
/account/changepassword/administration/users/<username>/resetpassword
If a client goes to /account/, which it can do by first going to "/" and finding that link, it will receive a list of links with what you can do on your own account, such as "changepassword". Do a GET on that link, and the client gets a form with two fields: the old password and the new password. The client might show this as three fields though, with a duplicate new password field to ensure that the user typed it correctly. The client can then POST to "changepassword" to make the actual change. I don't have to make any authorization checks, since the client is implicitly accessing its own account, so there's no way to screw it up, even deliberately. For the admin side, the client browses to /administration/users/ (and again, that link was retrieved from "/" if the user is an administrator), lists/searches the users, gets the link to a particular user, do a GET on "resetpassword" to get the form for it, and then fills it in and POST it to make the change. The REST API has at all times told the client what it is allowed to do, by using hypertext to drive the application state. This is what HATEOAS means in practice, and it is VERY helpful if you expose your usecases, and VERY annoying if you expose your domain model (simply because you can't). This approach also removes the issue outlined in the article with circular references, simply because in usecases, there are no circular references.
Another effect of this is that links in the REST API will almost always be relative, since all they do is guide the client further into a usecase, or sub-usecase. If you expose domain models you have to have absolute links, and they will be going here there and everywhere, exposing the internal associations between entities. It's stupid beyond belief, but this is what pretty much all of the current articles on "RESTful" frameworks tell you to do. And I say: DON'T! It'll only bring you misery.
From a security point of view the above is also easier to work with. Now all I have to do is add a check on "/administration/" for the administrator role, and after that the user can do anything below that point. No need to duplicate that check everywhere (and no need to use aspects to get around this annoyance)!
Now that you know what to do, the next question might be: how to do it? The problem here is that since all the current "REST" frameworks haven't thought HATEOAS through, they don't really allow you to make REST API's for your applications, and so, they will not be very easy to use. You have to work against them. This goes for Jersey and friends as well, as they don't allow this usecase approach to URL parsing. What we have done in my project, Streamflow, is to create our own wrapper on top ofRestlet. Restlet provides an excellent base for REST applications, but is too low-level to have to deal with in the application code. By putting a thin wrapper on top of it, which understand the notion of usecases and links, we have been able to really reduce the amount of code needed to do all of this.
Here's sample code for "change password". The code uses a routing technique, so in order to get to /account/changepassword, the framework first takes "/" and finds a resource for that. This resource then knows how to get to "/account/", which in turn knows how to get to "changepassword". The code looks like this:
public class RootResource extends CommandQueryResource{ @SubResource public void account() { subResource( AccountResource.class ); }...}
When the client hits "/" the framework will automatically look at this class, and present hypermedia (JSON or HTML at this point, but Atom Services is also an option) for it. The client clicks "/account/", which leads to this:
public class AccountResource extends CommandQueryResource{ public AccountResource( ) { super( AccountContext.class ); } @SubResource public void profile() { subResourceContexts( ProfileContext.class, ContactableContext.class ); }}
To find out what the client can do on this level you look in AccountContext. If you want to continue further down into the profile handling of an account (setting email, phone, etc.) the client would follow "/account/profile/" link that is presented. In our case, let's look at AccountContext:
public class AccountContext{ public void changepassword( ChangePasswordCommand newPassword ) throws WrongPasswordException { UserAuthentication user = RoleMap.role( UserAuthentication.class ); user.changePassword( newPassword.oldPassword().get(), newPassword .newPassword().get() ); }}
It exposes one interaction on this level of the usecase, which is /account/changepassword. The parameter tells the framwork what it requires as input, so if the client does GET it can look at the ChangePasswordCommand value object and present it as a form. If the client does a POST the framework parses the input into the value object and invokes the method, allowing it to "do it's thing". In this case it looks up the UserAuthentication role, which is a Qi4j mixin that our user entity implements. This entity was registered automatically by the authentication filter, so I don't have to look it up. For the administration usecase the code would get access to the "<username>" part of the URL so that it can locate the user from the repository.
And that's pretty much it. Not only is this easier to work with for clients (who simply follow links in hypermedia), but the code in both client and application is also absolutely trivial. I can also find out what my REST API looks like in total by starting with RootResource and explore the API by clicking on the classes it references. Since the API is so deterministic given these classes I can also generate documentation automatically that basically says, "given your user authorization level, here are all the resources you can access in the API". In particular, when your API becomes hypermedia driven like this, what you want to expose as documentation is the set of "rel" attributes that the client must know, in this case "account" and "changepassword". What the URL's look like is none of the clients business! It should just go to "/" and follow links based on the "rel" attribute of those links. Then you can say that your API is "RESTful". Until you do that, no, you may have a "web API" or a "HTTP API", but it aint REST.
There are more details in how this works, and how to turn links on/off depending on internal state, but the gist of how to think is as above.
From http://www.jroller.com/rickard/entry/the_domain_model_as_rest
Opinions expressed by DZone contributors are their own.
Comments