Skip to content

Instantly share code, notes, and snippets.

@fjeldstad
Last active September 26, 2015 21:06
Show Gist options
  • Save fjeldstad/fdf6f1f88a4577746071 to your computer and use it in GitHub Desktop.
Save fjeldstad/fdf6f1f88a4577746071 to your computer and use it in GitHub Desktop.
public interface IHandler<TMessage>
{
string StateKey(TMessage message);
HandlerResult Handle(TMessage message, object state);
}
public class HandlerResult
{
public object State { get; set; }
public IEnumerable<object> Messages { get; set; }
public Func<Task<IEnumerable<object>>> SideEffect { get; set; }
}
// A simple, side-effect free handler that count ticks.
public class TickCounter : IHandler<Tick>
{
private readonly Func<int, bool> _isEvenService;
public TickCounter(Func<int, bool> isEvenService)
{
_isEvenService = isEvenService;
}
// This handler counts all ticks, regardless of their
// origin etc. Therefore, it can use a constant value as state key.
public string StateKey(Tick tick) { return "ticks"; }
public HandlerResult Handle(Tick tick, object currentTicks = null)
{
var ticks = (int)(currentTicks ?? 0) + 1;
return new HandlerResult
{
State = ticks,
Messages = new object[] { new TotalTicks { Value = ticks } },
SideEffect = () =>
{
return _isEvenService(ticks) ?
Task.FromResult<IEnumerable<object>>(new object[] { new EvenTick() }) :
Task.FromResult(Enumerable.Empty<object>());
}
};
}
}
public class Tick
{
public override string ToString() { return "Tick"; }
}
public class TotalTicks
{
public int Value { get; set; }
public override string ToString() { return $"Aggregated ticks: {Value}"; }
}
public class EvenTick
{
public override string ToString() { return "The system has seen an even number of ticks."; }
}
// A mock host app.
void Main()
{
var stateStore = new Dictionary<string, object>();
var counter = new TickCounter(num => num % 2 == 0);
// Emulate a message bus with a queue
var queue = new ConcurrentQueue<object>();
Action<object> publish = message => queue.Enqueue(message);
Func<object> receive = () =>
{
while (true)
{
object message = queue.TryDequeue(out message) ? message : null;
if (message == null)
{
Thread.Sleep(50);
continue;
}
return message;
}
};
// Periodically publish Tick messages on the "bus".
var timer = new System.Timers.Timer
{
Interval = 1000,
AutoReset = true
};
timer.Elapsed += (source, args) => publish(new Tick());
timer.Start();
// "Subscribe" to messages on the "bus".
while (true)
{
var message = receive();
Console.WriteLine(message);
// Fake subscriber of Tick messages.
var tick = message as Tick;
if (tick != null)
{
// Get the current state for the handler.
var stateKey = counter.StateKey(tick);
object state = stateStore.TryGetValue(stateKey, out state) ? state : null;
// Invoke the handler.
var result = counter.Handle(tick, state);
// Save the state (if it has changed).
if (result.State != state)
{
stateStore[stateKey] = result.State;
}
// Publish any resulting messages.
foreach (var msg in result.Messages)
{
publish(msg);
}
// Execute side-effect (asynchronously) (if any) and publish resulting events.
if (result.SideEffect != null)
{
foreach (var msg in result.SideEffect().Result) // In a real app, you would await result.SideEffect
{
publish(msg);
}
}
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment