By Chris Scott
Presentation on github
Repository: LEM-Concept-Starter
Created using reveal.js
It starts with a user completing a step...
Then what?
ALL of these require modifying the ProcessingEngine code to do additional activities
Too many different people have worked on this, there is not enough documentation for this to be maintainable
Complete badge:
//user completes step
MessageHub.Hub.Publish(new BadgeCompletedEvent
{
Step = completedStep,
User = user
});
Handle it!
//email manager
MessageHub.Hub.Subscribe<BadgeCompletedEvent>(e =>
{
Email.send(e.User.Mentor, string.Format("{0} completed {1}", e.User.Name, e.Step.Name));
});
//notification manager
MessageHub.Hub.Subscribe<BadgeCompletedEvent>(e =>
{
db.Notifications.Add(new Notification(e.User.Mentor, e.User, e.Step));
});
This is great and all, but right now all it gets us is a lot of work rewriting our existing system.
What if...
//push notification manager
MessageHub.Hub.Subscribe<BadgeCompletedEvent>(e =>
{
applePush.NewNotification(/* ... */).Send();
androidPush.NewNotification(/* ... */).Send();
});
Adding this does not require touching the core processing framework, AT ALL
This is a great way to decouple these two tools.
But how do we actually register the PushNotificationManager to listen for events?
It ruins the "not touching core code" if we have to manually call all of these different RegisterForEvents methods
[AttributeUsage(AttributeTargets.Class)]
public class ManagerAttribute : Attribute
{
}
This is a custom attribute, that will be written as [Manager] that gets added at the class level
public interface IManager
{
void Startup();
}
All of our managers will implement this interface, which gives us a handle on starting them up.
[Manager]
public class EmailManager : IManager
{
public void Startup()
{
MessageHub.Hub.Subscribe<SampleBadgeCompleted>(e =>
{
});
//etc
}
}
foreach (var assembly in AppDomain.CurrentDomain.GetAssemblies())
{
foreach (var type in assembly.GetTypes())
{
if (type.GetCustomAttributes(typeof(ManagerAttribute), true).Length > 0)
{
//do this as a blind cast. if it fails the app should hard crash, anyway.
var manager = (IManager) Activator.CreateInstance(type);
manager.Startup();
}
}
}
This gets all of the types in the system with the [Manager] attribute
Then calls .Startup()
Adding new managers now no longer requires ANY modification of the core classes.