开发者

SOA, Request/Response service layer, accepting and returning a request/response vs an array or requests/responses?

开发者 https://www.devze.com 2023-03-10 21:44 出处:网络
We are implementing a Request/Response Service Layer using WCF, where every request inherits from a base Request class and every response inherits from base Response class.

We are implementing a Request/Response Service Layer using WCF, where every request inherits from a base Request class and every response inherits from base Response class.

The service has a single Process method, which accepts a Request and returns a Response.

// Messages contract
public abstract class Request {}
public abstract class Response {}

// Operation contract
Response Process(Request request);

However certain members of our team are of the opinion that instead of the operation contract expecting and returning a single request/response, it should accept and return an array of requests/responses.

// Operation contract
Response[] Process(Request[] requests);

Personally I do not find it correct semantically and in cases where a collection of related requests are required to be processed at once (perhaps in a batch), would consider introducing BatchRequest and a BatchResponse messages.

public class BatchRequest : Request
{
    public Request[] Requests {get;set;}
}
public class BatchResponses : Response
{
    public string BatchReference {get; set;} // for example
    public Response[] Responses {get;set;}
}

To me accepting and returning an array does not seem correct and this 开发者_运维知识库is certainly a pattern I've not seen used anywhere. Is there something more wrong with this? If yes, what is it?

Some people feel that accepting/returning arrays would allow us to not have the extra complexity of introducing batch requests (like the one illustrated above), and that you could send a single request in the array anyways.

Thanks


First of all, arrays add unnecessary complexity. Keep it simple (and) stupid.

For instance. How should errors be treated? If request #1 fails, should request #2 and #3 be executed anyway? How should the errors be reported?

What problems do your colleagues want to solve by making all requests arrays? If it's performance, it smells like premature optimization. There are several other ways to improve performance, but do not do anything until it's really necessary.

If it's to free the client instead of waiting for the result, switch to an asynchronous client.


I would prefer a single request and response and use the command pattern for batching up multiple actions to be done.

I find SOA to be complicated enough without seeing unexpected patterns cropping up. What would happen if part of the requests fail, do they all roll back, or only the failed ones. Aaarghhh, my head starts to hurt.


IMO, your decision to design / model these services should be completely based on its usage scenarios.

During SOA Modelling - the objective should be to build loosely coupled systems / services - supporting the business domain (applications) in question.

Ideally - your service should be not too granular nor too composite. Its reuse is equally important. Because of this very reason - you need to consider what the clients need.

Here are a couple of scenarios where any one could be useful

  1. Information as a Service

    Accessing data (master data) via common services. - In this case it would really not make sense to access the master data in a batch mode.

  2. Updating of group data - A service to migrate users to the new sales plan?(!)

And, I dont think handling error scenarios with arrays is a problem - you really cannot generalize the problem - its very contextual. Its a design decision.

Like in the case [2] above - you could process everything and return error codes inside the Response[] .


As a concept a Batch Request does seem to make sense - I have introduced such patterns for certain services in the past in order to reduce "chattiness". There do seem to be two Response concepts: did I understand the Batch as a whole and what is the response for each member of the batch. Hence it's a little more than just a simple array of responses.

I would also consider the possibility of deferred execution of Batch members. So the first response is "I understood your Batch" with some other services to obtain the status of individual batch members, or some asynchronous response delivery mechanism.

Summary: you will need individual request/response, a batching capability is likely to be useful and is open to quite some degree of extension.


People who propose batched requests are always thinking about interface performance. And that's a Good Thing. The question should not be "Should we batch requests or not?", but rather "Can we achieve acceptable performance across the interface?". I've worked with interfaces that performed well on LANs (e.g., in-office 10Mb Ethernet) but that were slow across trans-Atlantic links and simply failed across trans-Pacific links. The example I have in mind treated the interface more as an RPC layer than a "business operation" layer, and it showed in the network traces.

jgauffin hits the nail clearly on the head - what does the failure of a single request mean to the batch? Are the operations an ordered list, such that the subsequent operations must not be attempted? Are they merely a set, grouped for transmission, and otherwise unrelated? Are they a composite transaction, such that previous successful operations should be rolled back?

API construction is an art, not a science, and perhaps a black art at that. To paraphrase Justice Potter Stewart,

"I shall not today attempt further to define the kinds of material I understand to be embraced within [good API design]; and perhaps I could never succeed in intelligibly doing so. But I know it when I see it."


What you are considering is what I call stratifiation - the implementation of a simple protocol on top of a complex protocol. It's completely and utterly misguided. Request/response like what you are thinking of is already out there and it's called HTTP.

That said, even if you don't want to use REST services you shouldn't do it this way. If you are having a problem with invoking services due to their contracts being updated then you are doing something wrong. A solution, if you are forced to use WCF instead of something better like nServiceBus or MassTransit or any other ESB-framework could look like this:

  1. Version the service contract properly and create an assembly that proxies call to this service contract. Version the assembly accordingly and make it a nuget package.
  2. Reference this nuget package from your main project. Now it doesn't change anymore. You don't have to re-generate anything.
  3. When adding a symbol to the service interface, re-create the assembly and reference it as required. Keep it in its own source control repo.


In my experience, your approach of beginning with a single request/response pattern, and then later exposing a separate BatchRequest/BatchResponse pattern (which has arrays of requests/responses) is the best approach in both the short term and long term for your API.

The initial API pattern that gets exposed should be a single request and response, merely for the sake of having a simple to understand interface. A simple interface makes the API's patterns (it's methods and objects) easy to learn, which is important for user adoption. Your users will appreciate the simplicity as they first learn the API and try to just get up and running with the service. Forcing users to build arrays of requests in the initial API will not be appreciated, and over complicates the API, and is premature optimization on your colleagues' part.

However, your colleagues are correct in that what your users will eventually want is the option of batching requests and responses, as they become more familiar with the API. Your colleagues concerns really are best served by implementing a separate BatchRequest/BatchResponse pattern, as you suggested. Implementing a separate BatchRequest/BatchResponse pattern is common, and it can be done in a way that builds on top of and reuses much of the code in the implementation of the singleton request/response pattern. To be clear, the idea is that you batch your singleton requests from earlier. The idea that your API must start and be bound to a request/response array pattern in unnecessary and unfounded, in my experience.

Now, since I don't know what your colleague's use cases are, I'm going to take a guess at why they want the arrays from the get-go. if your colleagues want arrays of requests because they are concerned about performing multiple CRUD operations on multiple objects, then the solution is not to have arrays of requests, but to have individual requests be bound to a single CRUD operation type, and have the request take an array of objects to perform the operation on, in bulk. You do not want to force users to perform an individual request for each object, as that will definitely not scale. Batching is best thought of as an advanced API technique, only used when the basic patterns of the API are understood and when your users want to scale up their apps.

That said, first expose a single request/response pattern, and then expose an optional batchrequest/batchresponse pattern. Build the single request/response pattern with it in mind that eventually those requests will be batched.

0

精彩评论

暂无评论...
验证码 换一张
取 消