Reputation: 101150
I'm trying to build a small message/event system where messages may be requests.
Request handlers implement the IHandlerOf<T>
interface like
public class UserService : IHandlerOf<ISearchRequest>
{
private void ProccessRequest(ISearchRequest request)
{
}
}
I'm unsure of how I should handle replies since multiple handlers can "answer" a request. How would you design the reply part? Build a list of replies in the message broker, or include the reply object in the process method and let all handlers work against the same reply object?
Examples would be appreciated.
Or do you have any links to existing solutions? Using service buses (like nservicebus) seems a bit overkill since everything is in-process.
My current solution (Work in progress). The broker creates the response object by inspecting the IHandlerOf<>
interface which is registered for the request type being used in BeginRequest
.
The down side with the solution is that nothing ties the request and reply together which would give no compile errors if a incorrect reply type is mapped to a request type. Although the broker would thrown an error during the registration process if a request got two different response types.
The broker uses try/catch around each handler invocation to be able to continue process the request handlers even if one of those throws an exception. I haven't really decided what to do with the exceptions yet. One handler might throw while another one successfully handled the request.
The handler interface:
// interface defining a class which would handle a request
public interface IHandlerOf<TRequest, TResponse>
where TRequest : IRequest
where TResponse : IResponse
{
void ProcessRequest(IRequestContext<TRequest, TResponse> context);
}
Example implementation
public class FindContactsRequest : IRequest
{
public string SearchValue { get; set; }
}
public class FindContactsResponse : IResponse
{
public ICollection<string> Contacts { get; set; }
}
public class UserService : IHandlerOf<FindContactsRequest, FindContactsResponse>
{
public void ProcessRequest(IRequestContext<FindContactsRequest, FindContactsResponse> context)
{
if (context.Request.SearchValue == "blabla")
{
context.Response.Contacts.Add("My contact name");
}
}
}
broker interface
public interface IMessageBroker
{
IAsyncResult BeginRequest(IRequest request, AsyncCallback callback, object state);
IResponse EndRequest<T>(IAsyncResult result) where T : IResponse;
}
Sample usage
var ar = _broker.BeginRequest(new FindContactsRequest("blabla"));
var response = _broker.EndRequest<FindContactsResponse>(ar);
Console.WriteLine("Woho, found " + response.Contacts.Count + " contacts.");
Upvotes: 2
Views: 1487
Reputation: 134035
If all of the handlers work against the same reply object, then the reply object needs some kind of logic to prevent a bad handler from destroying the replies from other handlers. That is, if the reply object contained a List<string>
, for example, a misbehaving handler could call Clear
on the list and all would be lost. So the reply object would need to wrap that list (by providing an AddReply
method or some such) to prevent such behavior.
Also, if all of the handlers work against the same reply object, then multithreaded request handling becomes more difficult. The reply object has to handle thread synchronization to prevent data corruption.
If, on the other hand, the message broker handles combining the replies, you're much more flexible. It can call each handler in turn (sequentially), or it can use asynchronous calls to run multiple handlers in parallel. It seems like the message broker would be the easier and more flexible place to put the logic for combining replies.
Upvotes: 1