A while back I posted some thoughts on using a message framework as a way of abstracting part of the distributed computing problem. I stated I would start and write some code and so I have. On the way I had to make some interesting design decisions I wanted to share with you. This article is about these decisions and what where some of the reasons I made these.
Targeting the right objects
One of the first things I had to make a choice about, was how to target the right objects. Basically I needed two scenarios.
The first scenario is that Object A needs some task to be done by an object of type B. In this case Object A doesn't care what instance of type B actually does the job. It doesn't even care where this instance might be. In this case Object A should send out a message targeting type B. This means using a fully qualified class name as the receiver address.
The second scenario is that Object A sends out a message to notify other objects but it doesn't know which objects might be interested in the message. In this case other objects should register themselves with a dispatcher, stating that they are interested in some type of message. In this case Object A shouldn't include a receiver address at all.
But what about interaction with the system? This wouldn't be sufficient in a multi user scenario, because you can't target a specific instance of an object, which would be needed to actually give feedback to the user. I've decided that being able to target a specific object is not part of the low level messaging. To achieve a scenario where you need users to only receive their own messages back, there should be a layer on top of the existing messaging.
I chose this approach because it would be highly impractical to keep track of individual object instances across the entire ecosystem. Remember that one statement is that an object can be anywhere and it shouldn't matter for sending a message. By introducing unique addresses per instance, now it starts to matter, because a sender needs to know these unique addresses.
Another important aspect of the messaging framework is the communication between different processes. Again an object can be anywhere, but also anything .NET should work. I've investigated on using WCF, because this is obviously a very flexible and configurable way of communicating, scaling out from local (on the same machine) to global (across the internet). However it does put some bloat on the framework for people who don't want to use it.
I've also considered using Microsoft Messaging Queue, which kind of makes sense for a messaging framework. However this would involve building several tools around MQ to make sure all process types would be able to access the message queue and it would also put a deployment strain on the framework, beyond what I find is acceptable.
I settled on WCF, which doesn't mean that MQ is completely discarded. In the future it might prove valuable to actually build libraries around MQ to incorporate it into the framework. It does mean that by default every process includes code for both a WCF host and a client. However these are only instantiated as soon as a process is attached to another process.
To indicate that an object can recieve messages, it is needed that it implements the IMessageReceiver interface, which only contains one method (at least for now) that's called RecieveMessage. It takes a single parameter of type IMessage. The IMessage type has two properties, Sender and Receiver which are both strings (for containing the fully qualified classnames of the objects involved in the communication).
To make sure objects don't have to worry about getting a message to the receiver, every process should have exactly one MessageDispatcher object, who's sole responsibillity is to collect messages and dispatch them to the right objects and/or to other processes as needed.
To attach to another process in the ecosystem, the process in question has to make a request to the other process. Because we already have a message in the processes interface, that's exactly what I want to use. I've introduced a FrameworkMessage type that implements IMessage, so I can send it across to another process, in effect registering as a client to that process. To make sure the other process can also communicate back, it's response is to send a registration message as well, to make itself known as a client process as well.
As you can see there is a lot to think about when building a messaging framework. I'll keep working on this every now and then. Next time I'll try to get some code in.