Share via


Why can’t I efficiently compose method calls in SOA?

Imagine if you have these 3 methods:

public Employee GetEmployee(string alias);

public Department GetDepartment(Employee employee);

public Employee GetManager(Department department);

Now imagine if you want to find the Manager of the Department that some Employee (lets say “alexj”) works in.

If this was all in one tier it would be a no brainer…

Employee manager
= GetManager(GetDepartment(GetEmployee(“alexj”)));

Absolutely trivial.

You can do the same thing if you are interacting with a web-service that exposes those methods too.

But…

everything get a whole heap less efficient. The code looks the same, but masses of hidden work gets done:

Client: Send a GetEmployee(“alexj”) request.

Server: Get the employee from wherever (probably a database) then serialize it and send it back to the client

Client: Deserialize the employee and then reserialize it and send it as an parameter to a GetDepartment(..) request.

Server: Deserialize the employee, get the department for the employee, serialize the department and send it back to the client

Client: Deserialize the department and then immediately reserialize it and send it back to the server as a parameter to a GetManager(..) request

Server: Deserialize the department, get the manager for the employee and serialize the manager and send it back to the client

Client: Deserialize the manager.

Wow… talk about chatty.

We are doing a lot of superfluous serialization / deserialization. And as if that isn’t bad enough, we are incurring the network latency of, count them, 3 network round trips.

On two occasions, the server serializes and send an object to the client, just so the client can sent it back completely unchanged.

What an awful waste!

Surely there must be a better way…

If the client could send their intent to the server, there would be no need to move anything other than the final result, i.e. the Manager, back to the client.

Why couldn’t every webservice have a meta-service that allows you to send expressions in terms of the public contract of the service:

i.e. something like:

Employee manager = service.Execute<Employee>(
“alexj”,
(s) => GetManager(GetDepartment(GetEmployee(s)))
);

This new meta service doesn’t allow the customer to do anything new, because it only allows expressions that reference service methods.

But now the flow becomes:

Client: Send a request to the meta-service on the server, two parameters (‘alexj’) and the serialized expression.

Server: Deserialize the expression, and re-write as a valid CLR expression bound to the actual method implementations and the parameters from the client. Execute the expression, serialize the resulting employee and send it back to the client.

Client: Deserialize the manager.

And low and behold we are doing a lot less serialization and deserialization, and, most importantly, we are down to one network round trip, so it will be a *lot* faster.

Other potential benefits include:

  • The ability to monitor how customer are ‘using’ their service, not at the single call level but at a the conversational level.
  • The ability to partially apply results, i.e. if some of the work needs to be done on another server, that part of the expression can be shipped to that server seamlessly.
  • The ability to re-write the expression to inject auditing and additional logic.
  • The ability to do caching at the expression level, i.e. if I’ve already executed a request to GetEmployee(‘alexj’ ) and I’ve cached the results somewhere, I can fold that in as a parameter to the smaller expression. This has the advantage of lifting caching out of the service implementations themselves and into a higher ‘value added’ layer. Making life a lot easier for service implementors.
  • The ability to potentially parallelize unrelated parts of the expression.
  • etc etc etc.

Its pretty easy to imagine how this might be implemented.

So why can’t I do this today?

Comments

  • Anonymous
    July 27, 2009
    That is a great idea... "Linq to WCF".
  • Anonymous
    July 27, 2009
    The comment has been removed
  • Anonymous
    July 27, 2009
    One concern is of course security - an expression tree passed from the client to the server need to be restricted to ensure it doesn't do stuff that the client wouldn't be able to do directly. Limiting it to methods exposed by the service would do that but would still allow some parts of message/call level WCF security to be bypassed since the chain is now executed server-side. Maybe an attribute on the methods in the service contract to flag what method(s) should be composable..? Additionally, some CLR/BCL things would be neat to be able to include in a client side expression tree executed on the server, but I'm wondering what would be the best way to identify what should be considered "safe" and what should not be allowed...?
  • Anonymous
    July 27, 2009
    The comment has been removed
  • Anonymous
    July 27, 2009
    The comment has been removed
  • Anonymous
    July 27, 2009
    @FrankWell the format on the client for creating the expressions could be very framework specific. You could have one for C# that uses Lambdas, and something altogether different in Java.All that needs to be agreed up is how to ship and expression. I can't imagine that that would be too hard?Alex
  • Anonymous
    July 28, 2009
    That's why architecting a service based application can sometimes be a bit difficult from a usability perspective :)I agree though that it should not take to long to agree upon a decent solution for this problem.
  • Anonymous
    July 28, 2009
    The comment has been removed
  • Anonymous
    July 28, 2009
    The comment has been removed
  • Anonymous
    July 28, 2009
    The comment has been removed
  • Anonymous
    July 28, 2009
    The comment has been removed
  • Anonymous
    July 28, 2009
    Alex,I think that WCF integration is very important concern. In different projects I'm extending WCF heavily and can think of scenarios where your solution would just break it. For example I'm using binding that creates messages with username/password inside (doesn't matter encrypted or with transport level security). I'm using custom username/password validator to authenticate user and using declarative authorization to authorize user to use one or another service method. Shortly, using WCF extension options to authorize requests before method is actually called. It means that if user has permissions to call GetManager, but doesn't have permissions to call GetDepartment, following chain will compromise security - GetManager(GetDepartment(GetEmployee(“alexj”))), unless you take into account all chain of WCF extensions/interceptors that do stuff before/after actual method calls.There also may possibly be some issues with distributed transactions, asynchronous methods,duplex connections (when service method calls back client).As far as I know from some of your colleagues in Microsoft, adding new features to .NET Framework is quite expensive - your idea is interesting, but is it worth implementing considering existence of easy and IMHO desirable (though you may disagree) alternative?I wrote my wrapping methods not because of performance issues, but because my design based on given requirements was dictating me to do so. I also try to avoid premature optimization.I agree that from the perspective of tuning performance without touching service contract your main idea looks very attractive. I just don't like the way (passing Expressions to meta service) you suggest to implement it. I saw a couple of other implementations of your idea (batching WCF calls), but I'm not happy with them either (if you're interested please see Ayende's thoughts and Davy's implemetation here: http://ayende.com/Blog/archive/2008/03/07/SOA-Future-Batches.aspx and  http://davybrion.com/blog/2008/06/batching-wcf-calls/).I also really enjoy our discussion and appreciate your answers. :)
  • Anonymous
    July 29, 2009
    The comment has been removed
  • Anonymous
    July 29, 2009
    Uh oh. We got Oren showing up so that means the big dogs are coming out to play. Who next? Udi? ;)