I had an interesting on-line discussion with Keith Elder last week. He had an unusual need for some WCF services he was writing. We brained it out a little and came up with a few ideas on how to resolve his issue. This article is the first in a series outlining the various options that we discussed and the pros and cons of each.
The Problem
Note: The names of all domain entities, services and problems have been changed to protect the innocent. Some aspects of the service requirements have been needlessly complicated changed to make this more than a five line blog post.
Welcome to the worlds first Internet bagel company! We have a bagel machine that makes fresh bagels and is controlled by customers making a call to a service. The client sends a message indicating what variety and how many dozen to make. We only have three problems:
- Making bagels is not an instant process and is best served by an asynchronous call.
- We only have one bagel machine that can only make one batch of bagels at a time. Orders that come in while the machine is in use will have to be queued up.
- Our clients are on a variety of platforms.
So, given these constraints, there are basically three solutions to this problem. I say "basically" because they can all be tweaked and merged and combined into anything you want; that's the beauty of WCF. But in essence they are going to fall into one of these three paradigms:
- Create an service that exposes an MSMQ endpoint and provide an interoperable facade. Clients that speak MSMQ can access the service via MSMQ, clients that need HTTP will use the facade which has an HTTP endpoint. This is the solution I will demonstrate in this post
- Create a service that exposes an MSMQ endpoint and create a custom channel and binding which will expose an interoperable endpoint. In that custom channel do some magic when a message is received to MSMQify it.
- Create an asynchronous service, provide the queuing functionality outside of the service.
Normally I hate when people give away the ending, but in this case the best solution for out example is clearly the first approach, which the rest of this post will describe. So, why talk about the other two? Because even though they aren't the best solution for this problem, they are still important. For one thing, they are both interesting intellectual problems, and working through them will help increase your understanding of WCF. Think of it as an "Intellectual Spike." And while the first approaches benefits GREATLY outweigh its faults it is not a one-size-fits-all solution. There are going to be corner cases where you need the "exotic" solution.
Number One, I Order You to Take a Number Two
The first solutions to this issue is to create a Bagel service that exposes an MSMQ endpoint that can be used by clients able to write messages to the queue, and a facade service that exposes an HTTP endpoint that in turn calls the MSMQ service via the MSMQ endpoint:
In the diagram above, the MSMQ Bagel Service receives messages via an MSMQ endpoint. If the client happens to be able to talk to the MSMQ endpoint then there is not problem; the order is sent and when the machine is available the clients bagels are made.
Clients that cannot use the MSMQ endpoint communicate with the HTTP Bagel Service though a (you guessed it!) HTTP endpoint. The HTTP service takes the order, translates the order message (if necessary) and calls the MSMQ endpoint on the MSMQ Bagel service.
There are situations where this solution starts to have problems. The fact that transport based security all the way to the Bagel machine is prohibited from HTTP clients is a biggie.
However there are many benefits to this architecture. This is the easiest solution of the three. The design is very straight forward and easy to implement. The HTTP Service is simply an intermediary service that calls the MSMQ Service when prompted by one of it's clients. For our very simple example this is the best way to go.
The code for this solution is trivial and can be found here:
However, in WCF there are infinite ways to skin any cat. In the next post in this series I'll demonstrate how to implement a solution for this problem using a custom channel.
Until then, happy coding!
Print | posted on Monday, October 20, 2008 8:28 PM