Introduction
There has been a lot of buzz around DDD and Domain Events. And whenever someone talked about
Domain Events, he usually used “when”-“then”, which sounded like a very natural
way to express this. And one thing DotNetRules is capable of is saying “then”
in the nicest way I know: literally.
So I
wondered: Could DotNetRules be used to deliver events inside an application to
all interested parties? It turned out it can. Well, basically that's exactly what it does. DotNetRules uses the Pub/Sub Pattern, and while there are
more things to it when it comes to a true and pure event sourcing, for the sake of
simplicity I really only wanted to proof the concept and to find the pitfalls if I ever want to do this more correct.
Background
Domain Events are a concept that allow our Domain Model to have aggregates that are root consistent. Basically they establish a Pub/Sub Pattern which allows the Objects to communicate without knowing from each other.
This can be done inside the application (so basically only in memory) which we are doing here, persistent in a database or distributed over a network.
What's the Goal?
We are going to create a small demo console application called "Talking Bob". It parses user input, and creates/deletes new users and roles.
For user feedback, it will subscribe to the events of the role and user service.
The role service subscribes to the user events as well, and assigns a user to a default role whenever a new user is created.
Publishing Domain Events with DotNetRules
The easy part in DotNetRules is publishing the Event. All we have to do is to create a new instance of the Event and then notify the policies.
public void CreateNewUser(string name)
{
_users.Add(name);
new UserCreated(name).NotifyPolicies();
}
That's really all what's to it.
Note: NotifyPolicies
will notify all policies in an asynchronous matter. The method ends before all policies have been executed.
Subscribing to Domain Events
Subscribing to the event is easy as well. Create a class, add attribute a base class to it, type in your "Then" and you are ready to roll:
class TalkingBob
{
[Policy(typeof(UserCreated))]
class WhenUserIsCreated : PolicyBase<UserCreated>
{
Then print_the_name_to_console = () =>
Console.WriteLine("User '{0}' has been created!", Subject.Name);
}
readonly UserService _userService;
public TalkingBob()
{
_userService = new UserService();
}
public void Talk()
{
_userService.CreateNewUser("Bob");
Console.ReadLine();
}
}
class Program
{
static void Main()
{
new TalkingBob().Talk();
}
}
When you start the Program, these lines of code will give you the following output:
> User "Bob" has been created!
And the great thing - the UserService
has absolutely no idea who TalkingBob or what a Console is!
The tricky part is when it comes to the point where you want to access the parent object. As DotNetRules Classes are static
, we cannot easily access another object. However, there is a workaround.
Let's take a look into the goals for our upcoming RoleService
. It is supposed to add a new User to the standard role. When we write that down, we'd end up with something like this:
public class RoleService
{
Dictionary<string, List<string>> Enrolements = new Dictionary<string, List<string>>();
public bool ExistRole(string role)
{
return Enrolements.ContainsKey(role);
}
public void CreateRole(string role)
{
Enrolements.Add(role, new List<string>());
}
public void AddUserToRole(string user, string role)
{
if (!Enrolements[role].Contains(user)) Enrolements[role].Add(user);
}
[Policy(typeof(UserCreated))]
class WhenUserIsCreated : PolicyBase<UserCreated>
{
Then add_user_to_standard_group = () => { }
}
}
Weeeeeeell... what next? .NET gives us no possibility to get access to the instance of the parent class, and with good reason - how would .NET decide which instance of the RoleService
to call?
Luckily, we can use some methods from DotNetRules that can help us here. First, we can register the RoleService
with the Executor of DotNetRules, after which we can easily execute functions on all the instances of the RoleService
. Our changes would look like the following:
public class RoleService
{
public RoleService()
{
Executor.RegisterObject(this);
}
[Policy(typeof(UserCreated))]
class WhenUserIsCreated : PolicyBase<UserCreated>
{
Then add_user_to_standard_group = () =>
Executor.ExecuteOn<RoleService>(roleController =>
{
if (!roleController.ExistRole("standard"))
roleController.CreateRole("standard");
roleController.AddUserToRole(Subject.Name, "standard");
});
}
}
There we are... The only thing we might want to watch is that the executor will execute the action on all RoleControllers
. Which usually is fine, because you'd want to subscribe all your RoleServices
to the UserCreated
event anyway.
Now we can add a new "RoleCreated
" event, publish it, subscribe to it from Bob, and when we start again, our console looks like the following:
> User "Bob" has been created!
> Role "standard" has been created!
> User "Bob" is now in group "standard"
Everybody talks to everybody, while UserService
has no idea about RoleService
, and nobody knows Bob. The only thing everybody has to know are the Events. It's the level of low coupling I prefer.
That's It
I hope I was able to give you a short introduction to this field, and to show you some new use cases for DotNetRules.
The idea of Domain Events seems simple, but when you start with it, you'll soon discover that event driven development may lead (at least sometimes) to a clearer model, and knowledge about the object. Consider that with Domain Events, you may as well store all the Events to a store and gain a Business Intelligence Solution right out of the box as well!
Points of Interest
Domain Events have a lot more to them, like Streams, Versions, Stream_Versions. Reading "Implementing Domain-Driven Design" by Vaughn Vernon might give you a good start; furthermore on Vaughn GitHub Page you can find a waaaay more sophisticated implementation of Domain Events - which still may prove insufficient for a productive environment, mainly due to concurrency and data safety.
Another good start would be Martin Fowler's draft on EventSourcing, which will give you an introduction on the idea of not just getting the state of an object, but the history as well - by using Events.
History
-
18.07.2013 - Created
- 19.07.2013 - Added Note to describe that policies will be notified asynchronous.