Skip to content

About FakeBus sample implementation and contracts for the registration #13

Open
@beachwalker

Description

@beachwalker

Even though I know this should be seen as a demonstration for learning CQRS and is just the most simplest implementation... I have seen people jump onto this horse and start to ride. Because of this I wan't to take a note on the implementation of Command, Event, FakeBus and the registration of a handler.

Both Command and Event share the same base class Message here. I don't think you should go this path, especially when looking at the registration.
There are two different contracts for Command registration and Event registration.

  • You can publish to multiple (Event)handlers,
  • but you are not allowed to send a Command to multiple (Command)handlers, there can be only one.

You will notice this here in the FakeBus implementation:

public void Send<T>(T command) where T : Command
{
    List<Action<Message>> handlers;

    if (_routes.TryGetValue(typeof(T), out handlers))
    {
    if (handlers.Count != 1)  // look at this, only 1 handler allowed
        throw new InvalidOperationException("cannot send to more than one handler");
    handlers[0](command);
    }
    else
    {
        throw new InvalidOperationException("no handler registered");
    }
}

... on the other hand the Publish method does not have such a restriction.

But using the RegisterHandler<T>(Action<T> handler) where T : Message method you were already able to register as much as Command handlers for the same Command as you want:

public void RegisterHandler<T>(Action<T> handler) where T : Message
{
    List<Action<Message>> handlers;

    if(!_routes.TryGetValue(typeof(T), out handlers))
    {
        handlers = new List<Action<Message>>();
        _routes.Add(typeof(T), handlers);
    }

        // no rules applied to check and fail early when trying to register 2nd Command handler
    handlers.Add((x => handler((T)x)));
}

Because of this, the RegisterHandler<T>(Action<T> handler) where T : Message should be really something like RegisterEventHandler<T>(Action<T> handler) where T : Event and RegisterCommandHandler<T>(Action<T> handler) where T : Command.
This way you are able to express and check the required rules earlier in the registration process (e.g. throw Exception during startup instead of running a long time with a hidden bug).

I would not even make Command and Event inherit from a shared base class at this point (any reasons to give?). The implemented FakeBus itself might be capable of transport both and might be the same instance. Greg already created two different interfaces, ICommandSender and IEventPublisher so you are able to keep them in separate implementations that wrap a common transport bus.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions