Last active
September 26, 2015 21:06
-
-
Save fjeldstad/fdf6f1f88a4577746071 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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