Click here to Skip to main content
15,885,985 members
Articles / Programming Languages / C#
Tip/Trick

PoC-ing Domain Events with DotNetRules

Rate me:
Please Sign up or sign in to vote.
5.00/5 (3 votes)
19 Jul 2013CPOL4 min read 9.5K   52   5  
A short evaluation whether Domain Events can be done with DotNetRules

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.

C#
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:

C#
/// <summary>
/// The guy that does all the talking with the client
/// </summary>
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();
    }
    // more logic for bob follows here
}
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:

C#
 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 = () => { /* d'oh */ }
    }
}

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:

C#
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");
        });
    }

    // all the other stuff
}

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.  

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)


Written By
Software Developer (Senior) AutoScout24 GmbH
Austria Austria
Born, raised and killed by skynet I stopped worrying and started to code my own skynet whose sole purpose it will be to revenge my death by running over the terminator with a bobby car

Comments and Discussions

 
-- There are no messages in this forum --