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…
You can do the same thing if you are interacting with a web-service that exposes those methods too.
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>(
(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?