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 (14)

  1. KristoferA says:

    That is a great idea… "Linq to WCF".

  2. Frank de Groot says:

    From a network latency perspective it’s a pretty neat idea. But from an interoperability standpoint I don’t think the .NET and Java people will be able to agree on a common format. It’s a like lambda notation on steroids so perhaps the functional language generation (most notably F# and Scala) will be using it.

  3. KristoferA says:

    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…?

  4. Artem says:

    Hi,

    My concerns:

    First of all, I think this makes contract weaker. Contract must be simple, self-descriptive and strong. Having Execute(…) type operations allow contract users to do things that you possibly won’t even expect.

    Second, You’re letting client to define internal behavior of the server explicitly. Though it is only about calling public contract methods, but still why not just have GetManagerOfEmployee(string employeeName) contract method that inside would reuse proper service public methods? I don’t think we should be saving on contract verbosity. If  your consumers need to get manager by employee name – give them this option.

    Third, all potential benefits you’ve listed can be achieved more easily (especially for aspects like logging, auditing, monitoring caching – AOP tools can be used) or with the same effort without having Execute(…) type operations.

  5. AlexJ says:

    Artem,

    Well I appreciate your concerns. But I don’t buy any of them sorry.

    First Concern: They can do nothing you they can’t already do. The only difference is they can do the ‘same’ things more efficiently.

    Second Concern: There is nothing in this design that allows the internal behavior to be defined by the client. Why not have a GetManagerOfEmployee function? Well why should I? Why should I come up with a function for every possible expression? Perhaps because it would make for an unwieldy service contract, a much more likely outcome is the designer just wouldn’t add the method, and the client programmer would be back in the world where they have to wear nasty latency costs for no reason.

    Third Concern: All of the benefits can be achieved more easily using AOP? This is essentially AOP! But with the added flexibility of being completely dynamic and late bound.

    Sorry I think we might have to agree to disagree.

    Alex

  6. AlexJ says:

    @Frank

    Well 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

  7. 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.

  8. Jimmy Zimmerman says:

    Statement: Is this "sexy" from a technology perspective? Absolutely.

    Statement: Is this a bad idea? Absolutely.

    Sorry but someone that needs to do things like this is getting themselves into a design situation where they are treating the service like a database. Sorry but they are totally different ideas and mixing metaphores, so to speak, just gets you into trouble.

    People that need services like this are NOT building services. They’re building APIs. They are distinct issues. Just because you are using SOAP doesn’t mean you are actually building services (just like just because you use classes doesn’t automatically mean you are actually doing OO)

  9. Artem says:

    Alex,

    Thanks fro your response, here is mine.

    First concern:

    You’re only partially right when you say they can do nothing you they can’t already do. With weak contract they can do things in a way that may not be expected and handled properly, as with strong contract.

    They can potentially chain many (maybe even all) your service methods into single Expression and execute it. It may be ok, but may be not. You may put some checks when validating expression trees to define allowed shapes or sizes of chains but again – why doing that and not just provide specific method that does it? Other technical issues I see:

    • timeouts that you configured for your binding/connection on your client/server can be exceeded. You’ll have to specify timeouts for execution based on longest possible chain execution time.
    • some of service methods may already have some input parameters validation and calling those methods in compiled expression instead of direct calls will miss this validation. For example, you can add parameter inspectors in WCF to validate parameters for your methods – these will be ignored for your scenario. It also applies to any other rules/interceptors you may have extended WCF service with – they all will be ignored and you’ll have to provide mechanism to substitute them.  

    Second concern:

    "Why not have a GetManagerOfEmployee function? Well why should I?" – you should have it because this is scenario that your consumer requires, you create service to be consumed from outside – why not satisfy exact need of consumer?

    Following your logic (why having a function for every possible expression) you may end up with a service with a single method that takes SQL query and returns DataSet to the client. This way you don’t have to worry that designer forgets something etc. It is completely incorrect from my point of view, contract should reflect and satisfy consumer needs. Contracts should be designed to be as explicit as possible to minimize misinterpretation. Contract must be strong, so if consumer needs method – I think method must have been be added when designing contract. Of course you can’t guarantee that you cover all future needs, but contract is not something that you can’t change. You should be prepared that contact can be changed and extended with future requirements changes and system evolution over time etc.

    Third concern:

    I don’t think that what you suggest is AOP. What you’re suggesting is essentially just sending command from client to server that contains instructions what server should do with its public methods. IMHO, absolutely odd thing (there’s only rare need in that and it can be easily archived by just adding new method that does it) associated with several of issues and violating SOA contract building principles.

    Finally, I reviewed a couple of my big past projects and it looks like in my practise well defined contract that is designed properly when having good requirents didn’t have issues your solution targets at all or had very few (~1-2 method on 100 methods) and maximum with 2 methods chain need.

  10. AlexJ says:

    Artem,

    Hmmm. The point about missing WCF validation is a valid concern but it is just ‘a concern’, it doesn’t invalidate the idea. There is no reason why the WCF validation couldn’t be hooked into, if it is impossible now, we could make it possible in the future.

    Re-following my logic. You aren’t following my logic. There is no similarity between the solution, presented which is built over the existing public surface, and the completely open single method built on some internal non-public implementation details, that does everything.

    The big difference is in the case of a web-service I have explicitly stated that the contract’s methods are public, and like it or not they can be composed (by the client). So why not allow the client to ‘request’ the server do the composition for you to save time and avoid wasting resources.

    Of course the server would be completely free to deny any such requests, or more likely not include that service in its public contract.

    I do find it interesting that in your experience you’ve rarely see any need to chain requests. I can think of a few reasons why this might be the case. Maybe because of performance requirements for your core scenarios, you (as you said was likely) wrote and exposed the wrapping methods already.

    The interesting things is that the absence of a capability like the one I described means in order to create a high performance system you had to include very specific operations in the contract that infact might well have been possible using lower level building blocks. As a result you have diligently produced a system that makes it look like composition isn’t a commmon scenario???

    Wouldn’t it be nice to be able address new scenarios in the future by leveraging existing building block methods, without needing to version the webservice?

    Having said all this, I want to make it clear, I am enjoying this debate and I really value your voice on my blog!

    Alex

  11. AlexJ says:

    Jimmy,

    I don’t see how you arrived at your treating webservices as a database comment, I think of this as treating operations as functions. Which seems pretty safe to me seeing as that is how they are implemented. 😉

    We’ll probably need to bang heads on this one over a beer! 

    How does tomorrow sound? 

    -Alex

  12. Artem says:

    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. :)

  13. Jimmy Zimmerman says:

    I was certain this post would catch your attention!Yeah Alex let us grab some hot curry and a few pints – my treat!

    Though in all fairness I knew i was rushing the post and knew I wouldn’t flesh out the point well enough to actually make complete sense (or more likely ANY sense but that’s all water under the bridge now). For the edification of any readers that will not be part of said curry excursion (and to not seem like a total schmuck +D) I’ve written a followup post on my blog here (http://www.blitzkriegsoftware.net/blog.asp?flag=6&ID=116)

    hopefully that better spells out more my thoughts on the subject

    -cheers!

  14. Jimmy Zimmerman says:

    Uh oh. We got Oren showing up so that means the big dogs are coming out to play. Who next? Udi? 😉