The following implementation does not yet take into account events, however this will be covered.
First we have a trait to mark a command
trait Command
Individual command will implement this trait, such as
case class NewUserCommand(string:Login, string:Password) extends Command
For each command there should be a handler which will extend the following:
trait Handler[T <: Command] { def handle(command:T):Unit }
The command handlers I use follow the cake pattern, and have state wired in. For example
trait NewUserCommandHandler extends Handler[NewUserCommand] { this:SecurityService with PersistencyService => def handle(command:NewUserCommand) { ... } }
In this example state is a document cache required by the PersistencyService:
trait DocumentPersistencyService extends PersistencyService { def state:DocumentCache ...
I have created a registry for holding the constructor of the command handlers. Here state is a type parameter, though I don't think that's entirely necessary. The registry is mutable only until first use, I have followed the same approach that I used here.
trait CommandRegistry[State] { def getHandler[T <: Command:TypeTag](implicit state:State):Handler[T] } trait CommandConstructorRegistry[State] extends CommandRegistry[State] { private val registryBuffer = new ListBuffer[(TypeSymbol, (State) => Handler[_])] private lazy val registry = registryBuffer.toMap def getHandler[T <: Command:TypeTag](implicit state:State):Handler[T] = registry(typeOf[T].typeSymbol.asType)(state).asInstanceOf[Handler[T]] def register[T <: Command:TypeTag](constructor:((State) => Handler[T])) { registryBuffer += ((typeOf[T].typeSymbol.asType, constructor)) } }
A command dispatcher wraps this all up, so that all commands can be issued via the dispatcher.
trait CommandDispatcher[State] { def dispatch[T <: Command:TypeTag](command:T)(implicit state:State):Unit } trait DefaultCommandDispatcher[State] extends CommandDispatcher[State] { this:CommandRegistry[State] => def dispatch[T <: Command:TypeTag](command:T)(implicit state:State) { val handler = getHandler[T] handler.handle(command) } }So tying it all together looks something like this:
val commandDispatcher = new DefaultCommandDispatcher[DocumentCache] with CommandConstructorRegistry[DocumentCache] { register(s => new NewUserCommandHandler with DefaultSecurityService with DocumentPersistencyService { val state = s }) } implicit val state = new DocumentCache() val command = NewUserCommand("login","password") commandDispatcher.dispatch(command)
You could also enrich the dispatcher through the use of mixins, say if you wanted to log the commands content:
trait CommandLogger[State] extends CommandDispatcher[State] { abstract override def dispatch[T <: Command:TypeTag](command:T)(implicit state:State) { println(command) super.dispatch(command) } }