Skip to content

Instantly share code, notes, and snippets.

@hagbarddenstore
Created February 10, 2013 23:28
Show Gist options
  • Save hagbarddenstore/4751506 to your computer and use it in GitHub Desktop.
Save hagbarddenstore/4751506 to your computer and use it in GitHub Desktop.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace AggregateRootTesting
{
using System.Diagnostics;
using System.Reflection;
class Program
{
static Guid _id;
static void Main(string[] args)
{
const int Runs = 10000;
_id = Guid.NewGuid();
var domainEvents = new List<DomainEvent>
{
new Created(_id, 1, "Hello, world!")
};
for (var i = 2; domainEvents.Count < 101; i++)
{
domainEvents.Add(new CommentedOn(_id, i, string.Format("A comment on this worthless post! {0}", i)));
}
// Warmup and assertions
var blogPostDynamic = new BlogPostDynamic(_id, domainEvents);
var blogPostStatic = new BlogPostStatic(_id, domainEvents);
var blogPostReflection = new BlogPostReflection(_id, domainEvents);
var blogPostCachedReflection = new BlogPostCachedReflection(_id, domainEvents);
Info("Asserting dynamic");
Assert(blogPostDynamic);
Info("Asserting static");
Assert(blogPostStatic);
Info("Asserting reflection");
Assert(blogPostReflection);
Info("Asserting cached reflection");
Assert(blogPostCachedReflection);
for (var j = 0; j < 10; j++)
{
Info("Testing static");
var stopwatchStatic = Stopwatch.StartNew();
for (var i = 0; i < Runs; i++)
{
var blogPost = new BlogPostStatic(_id, domainEvents);
}
stopwatchStatic.Stop();
Info(string.Format("It took {0}ms to run static. {1}ms / call", stopwatchStatic.ElapsedMilliseconds, stopwatchStatic.ElapsedMilliseconds / (double)Runs));
Info("Testing reflection");
var stopwatchReflection = Stopwatch.StartNew();
for (var i = 0; i < Runs; i++)
{
var blogPost = new BlogPostReflection(_id, domainEvents);
}
stopwatchReflection.Stop();
Info(string.Format("It took {0}ms to run reflection. {1}ms / call", stopwatchReflection.ElapsedMilliseconds, stopwatchReflection.ElapsedMilliseconds / (double)Runs));
Info("Testing cached reflection");
var stopwatchCachedReflection = Stopwatch.StartNew();
for (var i = 0; i < Runs; i++)
{
var blogPost = new BlogPostCachedReflection(_id, domainEvents);
}
stopwatchCachedReflection.Stop();
Info(string.Format("It took {0}ms to run cached reflection. {1}ms / call", stopwatchCachedReflection.ElapsedMilliseconds, stopwatchCachedReflection.ElapsedMilliseconds / (double)Runs));
}
Console.WriteLine("Press enter to exit...");
Console.ReadLine();
}
static void Assert(IBlogPost blogPost)
{
if (blogPost.Id != _id)
{
Error(string.Format("Invalid id. Expected: {0} Attempted: {1}", _id, blogPost.Id));
}
if (blogPost.Version != 101)
{
Error(string.Format("Invalid version. Expected: {0} Attempted: {1}", 101, blogPost.Version));
}
if (blogPost.Comments.Count() != 100)
{
Error(string.Format("Invalid number of comments. Expected: {0} Attempted: {1}", 100, blogPost.Comments.Count()));
}
}
static void Error(string message)
{
var color = Console.ForegroundColor;
Console.ForegroundColor = ConsoleColor.Red;
Console.WriteLine(message);
Console.ForegroundColor = color;
}
static void Info(string message)
{
var color = Console.ForegroundColor;
Console.ForegroundColor = ConsoleColor.White;
Console.WriteLine(message);
Console.ForegroundColor = color;
}
}
class DomainEvent
{
public DomainEvent(Guid aggregateRootId, long version)
{
AggregateRootId = aggregateRootId;
Version = version;
}
public Guid AggregateRootId { get; private set; }
public long Version { get; private set; }
}
abstract class AggregateRootBase
{
protected AggregateRootBase(Guid aggregateRootId, IEnumerable<DomainEvent> domainEvents)
{
Id = aggregateRootId;
Replay(domainEvents);
}
public Guid Id { get; private set; }
public long Version { get; private set; }
private void Replay(IEnumerable<DomainEvent> domainEvents)
{
foreach (var domainEvent in domainEvents)
{
Replay(domainEvent);
Version = domainEvent.Version;
}
}
protected abstract void Replay(DomainEvent domainEvent);
}
class Created : DomainEvent
{
public Created(Guid aggregateRootId, long version, string title)
: base(aggregateRootId, version)
{
Title = title;
}
public string Title { get; private set; }
}
class CommentedOn : DomainEvent
{
public CommentedOn(Guid aggregateRootId, long version, string comment)
: base(aggregateRootId, version)
{
Comment = comment;
}
public string Comment { get; private set; }
}
interface IBlogPost
{
Guid Id { get; }
long Version { get; }
string Title { get; }
IEnumerable<string> Comments { get; }
}
class BlogPostDynamic : AggregateRootBase, IBlogPost
{
private readonly ISet<string> _comments = new HashSet<string>();
public BlogPostDynamic(Guid aggregateRootId, IEnumerable<DomainEvent> domainEvents)
: base(aggregateRootId, domainEvents)
{
}
public string Title { get; private set; }
public IEnumerable<string> Comments
{
get { return _comments; }
}
public void Apply(Created domainEvent)
{
Title = domainEvent.Title;
}
public void Apply(CommentedOn domainEvent)
{
_comments.Add(domainEvent.Comment);
}
protected override void Replay(DomainEvent domainEvent)
{
dynamic self = this;
self.Apply(domainEvent);
}
}
class BlogPostStatic : AggregateRootBase, IBlogPost
{
private readonly ISet<string> _comments = new HashSet<string>();
public BlogPostStatic(Guid aggregateRootId, IEnumerable<DomainEvent> domainEvents)
: base(aggregateRootId, domainEvents)
{
}
public string Title { get; private set; }
public IEnumerable<string> Comments
{
get { return _comments; }
}
private void Apply(Created domainEvent)
{
Title = domainEvent.Title;
}
private void Apply(CommentedOn domainEvent)
{
_comments.Add(domainEvent.Comment);
}
protected override void Replay(DomainEvent domainEvent)
{
var created = domainEvent as Created;
if (created != null)
{
Apply(created);
}
var commentedOn = domainEvent as CommentedOn;
if (commentedOn != null)
{
Apply(commentedOn);
}
}
}
class BlogPostReflection : AggregateRootBase, IBlogPost
{
private readonly ISet<string> _comments = new HashSet<string>();
public BlogPostReflection(Guid aggregateRootId, IEnumerable<DomainEvent> domainEvents)
: base(aggregateRootId, domainEvents)
{
}
public string Title { get; private set; }
public IEnumerable<string> Comments
{
get { return _comments; }
}
private void Apply(Created domainEvent)
{
Title = domainEvent.Title;
}
private void Apply(CommentedOn domainEvent)
{
_comments.Add(domainEvent.Comment);
}
protected override void Replay(DomainEvent domainEvent)
{
var applyMethod = FindMethod(domainEvent.GetType());
applyMethod.Invoke(this, new object[] { domainEvent });
}
private MethodInfo FindMethod(Type domainEventType)
{
MethodInfo methodInfo = null;
var classType = GetType();
do
{
methodInfo = FindMethod(classType, domainEventType);
if (methodInfo == null)
{
classType = classType.BaseType;
}
}
while (methodInfo == null && classType != typeof(AggregateRootBase));
return methodInfo;
}
private MethodInfo FindMethod(Type classType, Type domainEventType)
{
const BindingFlags BindingFlags = BindingFlags.Instance | BindingFlags.NonPublic | BindingFlags.Public;
var methodInfo = classType.GetMethod("Apply", BindingFlags, null, new[] { domainEventType }, null);
return methodInfo;
}
}
class BlogPostCachedReflection : AggregateRootBase, IBlogPost
{
private static readonly IDictionary<Type, IDictionary<Type, MethodInfo>> _applyMethods = new Dictionary<Type, IDictionary<Type, MethodInfo>>();
private readonly ISet<string> _comments = new HashSet<string>();
public BlogPostCachedReflection(Guid aggregateRootId, IEnumerable<DomainEvent> domainEvents)
: base(aggregateRootId, domainEvents)
{
}
public string Title { get; private set; }
public IEnumerable<string> Comments
{
get { return _comments; }
}
private void Apply(Created domainEvent)
{
Title = domainEvent.Title;
}
private void Apply(CommentedOn domainEvent)
{
_comments.Add(domainEvent.Comment);
}
protected override void Replay(DomainEvent domainEvent)
{
var applyMethod = FindMethod(GetType(), domainEvent.GetType());
applyMethod.Invoke(this, new object[] { domainEvent });
}
private MethodInfo FindMethod(Type classType, Type domainEventType)
{
if (_applyMethods.ContainsKey(classType) && _applyMethods[classType].ContainsKey(domainEventType))
{
return _applyMethods[classType][domainEventType];
}
do
{
const BindingFlags BindingFlags = BindingFlags.Instance | BindingFlags.NonPublic | BindingFlags.Public;
var methodInfo = classType.GetMethod("Apply", BindingFlags, null, new[] { domainEventType }, null);
if (methodInfo == null)
{
classType = classType.BaseType;
}
else
{
if (!_applyMethods.ContainsKey(classType))
{
_applyMethods.Add(classType, new Dictionary<Type, MethodInfo>());
}
_applyMethods[classType].Add(domainEventType, methodInfo);
return methodInfo;
}
}
while (classType != typeof(AggregateRootBase));
return null;
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment