Post Format

Duplex communication in ASP.NET MVC with Domain Events

1 comment

We are going to talk about duplex communication in ASP.NET MVC web apps, without Flash, Java, SL or anything like that. I assume that you have a good understanding of comet techs because we’ll look into a ready-to-use library so most of the hard things are abstracted away. This library is PokeIn and I use it as the base library to provide real-time things on my gaming site fumind.

This is not a how-to-do-it step-by-step tutorial but you may find it interesting if you are looking around for concepts related to the topic. 🙂

I’m going to write about solutions I used to provide these real-time updates and gameflow. The site provides gomoku and gomoku-alike games so don’t expect some fancy action game, these are strictly turn-based stuffs. Because of this we don’t need really fast solutions, so we can go with (or fall back to) classic comet solutions, as the speed of a quick ajax call is good enough for our needs.

Our plan is the following:

  1. Abstract the comet communication away
  2. Implement domain events to keep things simple
  3. Forward domain events to comet listeners

All the three point is about single responsibility and abstraction. (1) We don’t want to tie our code to a specific comet solution so we need some kind of abstraction which is PokeIn in our case. (2) We don’t want to mess up our MVC actions with PokeIn calls but we need a consistent way to notify our system that something happened so if anybody interested in it will get a notification and can handle it in a separate component. (3) After we have domain events we can write listeners which can directly notify the PokeIn clients.

The first one is really easy as we only have to setup the PokeIn library (you can find concrete examples on the PokeIn website). The second requires some foreseeing because this is the base of the two-way communication. Simply put, we expect to see almost the same things in the same moment for all the players and observers. To achieve this in a consistent and maintainable way I dedicated two channels for the communication in the case of real-time things:

  1. Incoming actions
  2. Outgoing events

Incoming actions are plain MVC actions and they will return an OK signal every time it succeded or some kind of error message if something went wrong. These actions will raise domain events so a listener can notify everybody on the outgoing channel, which is PokeIn. This is the key. Because nobody receives the updates directly as the action response we can treat everybody equally when updating the clients. This will lead to a predictable environment which is good for maintainability.

As you may see it has several seams where I can hook in the functionality. I can write my incoming actions and I don’t need to care about any update to the client, I can focus on the action. I can write my domain event listeners separately from my MVC actions so I can focus on only the client selection and notification for every kind of domain events. I can listen on everything on the client side without changing anything on the server side. This is possible because I route through every PokeIn callback on a single JavaScript function and from my custom code I only subscribe to this central callback point then every subscriber can decide if it should do anything with the given information.

Let see some code from the fumind.com codebase. One of the most important parts to achieve what I’m talking about is to create the concept of the domain events. You may find several definitions on the internet, I use it as a mediator pattern implementation.

So for example when somebody makes a move on fumind this action will controll the flow:

[HttpPost]
[UnitOfWork]
[RestrictToAjax]
[ValidateAntiForgeryToken]
public ActionResult MakeMove(MakeMoveViewModel moveViewModel)
{
	Game game = Enter(moveViewModel.GameID);

	var user = _profileService.CurrentUser;
	var context = new MakeMoveContext(game, moveViewModel.Position, user);

	var result = _gameFlow.MakeMove(context);

	if (result == GameFlowActionResult.TimeOver)
	{
		OnSuccessfulUoW = () => EventBroker.Current.Send(this, new TimeOverPayload(game));
		_logger.Info("Time over in game: {0}", moveViewModel.GameID);
	}
	else if (result == GameFlowActionResult.Success)
	{
		OnSuccessfulUoW = () => EventBroker.Current.Send(this, new MoveMadePayload(game, game.LastMove));
		_logger.Info("move made in Game: {0}, Position: {1} by {2}", moveViewModel.GameID, moveViewModel.Position, user.UserName);
	}
	else
	{
		_logger.Warn("Move failed in game: {0}, Position: {1} by {2}", moveViewModel.GameID, moveViewModel.Position, user.UserName);
	}

	Exit(game);

	return Json(Constants.AjaxOk);
}

There are several things in this snippet which aren’t really relevant but I wanted to post the whole thing to make it clear how I made it. You can see things highly related to the makemove process but what is important is the response and the way unit of work helps us here. We declare this action as a unit of work and we respond to the client that we have succeeded:

return Json(Constants.AjaxOk);

Of course only if we really succeeded. This is the incoming part of the flow. An extension point in this code is the OnSuccessfulUoW which is declared in a base controller and you can assign an action to it. This will only run if the whole unit of work succeeded. This indirection is important to avoid false notifications so every client will stay valid. As you can see I call into the EventBroker ambient context inside this callback so basically when the UoW succeeds it will raise a domain event:

OnSuccessfulUoW = () => EventBroker.Current.Send(this, new MoveMadePayload(game, game.LastMove));

With this we are over the first step, we have the action and it has only one responsibility: delegating the makemove attemp to the domain logic. We are handling the updates in a subscriber which listens to the MoveMadePayload. 

Some words about the EventBroker:it based on the Unity DI container so if you ever want to subscribe to a domain event you need to implement the IEventSubscriber<TPayload> interface and register it:

container.RegisterType<IEventSubscriber<MoveMadePayload>, MoveMadeSubscriber>(typeof(MoveMadeSubscriber).FullName);

And the EventBroker will resolve it:

public class UnityEventBroker : EventBroker
{
	private readonly IUnityContainer _container;

	public UnityEventBroker(IUnityContainer container)
	{
		_container = container;
	}

	public override void Send<TPayload>(object sender, TPayload payload)
	{
		var subscribers = _container.ResolveAll<IEventSubscriber<TPayload>>();
		if (subscribers == null) return;
		foreach (var subscriber in subscribers)
		{
			subscriber.Receive(sender, payload);
		}
	}
}

This can be (and will be) improved to handle when a listener throws an exception, but it’s ok for now.

So basically all domain event works because this event broker. You can easily see how we can subscribe to one or more event.

public class MoveMadeSubscriber : IEventSubscriber<MoveMadePayload>
{
	public void Receive(object sender, MoveMadePayload payload)
	{
		//define clients
		//build update data-transfer-objects (dto)
		//notify pokein listeners:
		//var json = PokeIn.JSON.Method("mindline.pokein", dto);
        //CometWorker.SendToClients(viewers.ToArray(), json);
	}
}

It really doesn’t worth to include the whole makemove notification logic here so I just wrote the steps as comments. Of course we need to define the PokeIn clients to update (we can define different branches too, e.g. for makemove I define observers and players separately), we need to build up our dto (data transfer object, I build up slightly different dtos for observers and players) and call into the PokeIn library as you can see.

The only thing left is the client extension point. You can see in the subscriber that we define the mindline.pokein javascript function as the client side endpoint. I use TypeScript as JavaScript preprocessor, but this is really very simple:

/// <reference path="../typings/jquery.d.ts"/>

declare var PokeIn;

module mindline {

    var listeners: { (any): void; }[] = new any[];

    export function addPokeInListener(listener: (any) => void ) {
        listeners.push(listener);
    }

    export function pokein(payload: any) {
        $.each(listeners, (k: any, listener: (any) => void ) => {
            listener(payload);
        });
    }
}

So anybody can hook into this client side root point and can handle any PokeIn calls, such as our makemove action.

I hope you see the gained values of this architecture:

  • I can easily replace PokeIn with another library, it can work with websockets too
  • I can work on my action logic and I don’t need to think about the comet logic meanwhile
  • I can write as many domain event subscriber as I want so I can easily extend the behavior of the existing application
  • I can hook into any comet callback on the client side without modifying any line of the existing code
  • I can encapsulate the complete game flow on the server side so the server controls what you see
  • Real-time comet callbacks are treated exactly the same as if you need to update the clients due to a scheduler event (time over for example), so it’s easier to maintain the code (consistency).

You can do duplex communication in a lot of ways but I think this one is really simple for starting off and provides a consistent environment to deal with the upcoming tasks. And it is reliable :).

What do you think about it?

Advertisements

1 Comment so far Join the Conversation

Leave a Reply

Required fields are marked *.

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s