Design Pattern to model Request and Response Objects for Webservices
I had a similar dilemma; I went in the generic direction and am liking the results; haven't looked back since.
If I had a GetAccounts
API method the signature might look like.
public final Response<Account[]> getAccounts()
Naturally the same principle may be applied to requests.
public final Response<Account[]> rebalanceAccounts(Request<Account[]>) { ... }
In my opinion; decoupling the individual entities from requests and responses yields a neater domain and object graph.
Below is an example of what such a generic response object might look like. In my case; I'd built the server to have a generic response for all requests to enhance error handling and lower coupling between domain objects and response objects.
public class Response<T> {
private static final String R_MSG_EMPTY = "";
private static final String R_CODE_OK = "OK";
private final String responseCode;
private final Date execDt;
private final String message;
private T response;
/**
* A Creates a new instance of Response
*
* @param code
* @param message
* @param execDt
*/
public Response(final String code, final String message, final Date execDt) {
this.execDt = execDt == null ? Calendar.getInstance().getTime() : execDt;
this.message = message == null ? Response.R_MSG_EMPTY : message;
this.responseCode = code == null ? Response.R_CODE_OK : code;
this.response = null;
}
/**
* @return the execDt
*/
public Date getExecDt() {
return this.execDt;
}
/**
* @return the message
*/
public String getMessage() {
return this.message;
}
/**
* @return the response
*/
public T getResponse() {
return this.response;
}
/**
* @return the responseCode
*/
public String getResponseCode() {
return this.responseCode;
}
/**
* sets the response object
*
* @param obj
* @return
*/
public Response<T> setResponse(final T obj) {
this.response = obj;
return this;
}
}
I don't know if there is such design pattern. I do the following:
- For GET requests, define the parameters in query string or in path. Preferred way is path. Also, you will have few parameters for your service. Each service will handle this on its own. There is no reusability here.
- For POST requests, consume the parameters in JSON format that comes in the body of the request. Also, use an adapter (depending on the technology you're using) that will map the JSON content to a specific class that you receive as parameter.
For responses, there are two approaches:
You can create a custom
ResponseWrapper
class that will be your real response. This will contain the response code, the description and a field called value which stores the real content of the response in case of a success processing of the input data. The class will look like this:public class ResponseWrapper { private int statusCode; private String description; private String value; }
In this case,
String value
will store the concrete response in JSON format. For example:@Path("/yourapi/book") public class BookRestfulService { @POST("/create") @Produces("json") public ResponseWrapper createBook(Book book) { ResponseWrapper rw = new ResponseWrapper(); //do the processing... BookService bookService = new BookService(); SomeClassToStoreResult result = bookService.create(book); //define the response... rw.setStatusCode(...); rw.setDescription("..."); rw.setValue( convertToJson(result) ); } static String convertToJson(Object object) { //method that probably will use a library like Jackson or Gson //to convert the object into a proper JSON strong } }
Reuse the HTTP Response Status Code, use 200 (or 201, this depends on the type of request) for successful requests and a proper status code for the response. If your response has status code 200 (or 201) then return the proper object in JSON format. If your response has a different status code, provide a JSON object like this:
{ "error" : "There is no foo in bar." }
There is a trade off using RESTful services with JSON or XML, and that's the price of complexity for consumers, who may not know the structure of the response. In case of WS-* web services, the trade-off comes in performance terms (compared ton RESTful approach).
The big problem I see in all the answers so far including the question is that they all violate the principal of separation of concerns, information hiding and encapsulation. In all answers request (and response) classes are tightly coupled to model classes. That is a more serious issue and raises a question more important than the relationship between the requests and responses...
What should be the relationship between the request/response classes and the model classes?
Since the request class (e.g. CreateBookRequest) and the model class Book have mostly the same data properties, you could do any of the following:
A. Put all your data/getters/setters into the Book class and have the CreateBookRequest extend from the class
B. Have your CreateBookRequest contain a Book as a member (as in the question and answers given by ekostadinov, Juan Henao, . The generic usage given by dasm80x86 is also a special case of this)
C. Put data/getters/setters in BookBase and have both Book and CreateBookRequest extend from BookBase
D. Put all/some data/getters/setters in BookStuff and have both Book and CreateBookRequest contain a BookStuff
E. Put all the data/getters/setters in both Book and CreateBookRequest. (you can copy-paste).
The correct answer is E. We are all so trained and eager to "re-use" that this is the least intuitive answer.
The request class CreateBookRequest (as well as the response class CreateBookResponse) and the model class Book, should NOT be be in same class hierarchy (other than both having Object as a top most parent) (A,C). Also the CreateBookRequest should not refer/contain to the model Book or to any of the composite classes that are members in the Book class (B,D)
The reasons for this are as follows:
You want to modify the model object or the request object independent of each other. If your request refers to your model (as in A-D) any change in the model will be reflected in the interface, and therefore break your API. Your customers are going to write clients according to the API dictated by your request/response classes and they don't want to change those clients whenever you make a change to your model classes. you want the request/response and the model to vary independently.
Separation of concerns. Your request class CreateBookRequest may contain all kinds of interface/protocol related annotations and members (e.g. validation annotations that the JAX-RS implementation knows how to enforce). These interface-related annotations should not be in the model object. (as in A)
from an OO perspective CreateBookRequest is not a Book (not IS_A) nor does it contain a book.
The flow of control should be as follows:
The interface/control layer (the one that receives the Rest-API calls) should use as its methods parameters Request/Response classes defined specifically for that layer (e.g. CreateBookRequest). Let the container/infrastructure create those from the REST/HTTP/whatever request.
The methods in the interface/control layer should create in some way an instance of a model class object, and copy values from the request classes into the model class object,
The methods in the interface/control layer should call a BO/Manager/Whatever (in the model layer... which is responsible for business logic) passing to it the model class object and not the interface class/method parameter class object (in other words, NOT as Luiggi Mendoza has shown in his answer)
The model/BO method would return some model class object or some "primitive".
Now the interface method (the caller) should create an interface class response object, and copy values into it from the model class object returned by the model/BO. (Just like Luiggi Mendoza as shown in his answer)
The container/infrastructure would then create the JSON/XML/whatever response from the response class object.
Now to the question asked... What should be the relationship between the requests and response classes?
Request classes should extend from request classes and not extend nor contain response classes, and vice versa. (as was also suggested by question asker).
Usually you have a very basic BaseRequest class, extended by something like CreateRequest, UpdateRequest, etc... where properties common to all create requests are in CreateRequest which is then extended by more specific request classes such as CreateBookRequest...
Similarly, but parallel to it, the is the Response class hierarchy.
The question asker also asked whether it's ok for both CreateBookRequest and CreateBookResponse to contain the same member such as (Never a model class though!) BookStuffInRequestAndResponse which properties common to both the request and response?
This is not as severe an issue as having the request or response refer to a class that is also referred to by the model. The problem with this is that if you need to make a change to your API request and make it in BookStuffInRequestAndResponse, it immediately affects your response (and vice versa).
It's not so bad because 1) if your customer needs to fix their client code because you change the request parameters, they may as well fix handle/fix the changed response and 2) most likely changes in the request would require change to the response any way (for example adding a new attribute), however, that may not always be the case.