Search code examples
asp.net-mvcdomain-driven-designdomain-events

Handling a domain event in an asp.net mvc controller


I'm looking into using Domain Events to bubble information from operations occuring deep inside of my domain model in order to signal certain events at the controller level. Unfortunately, I haven't been able to find an example of how to properly wire this on an asp.net mvc controller.

In short, this is what I'm looking for inside of my action:

service.doSomethingComplicated();
var model = new ViewModel();
model.somethingComplicatedCausedSomethingElse = <true-if-my-domain-event-was-raised>;
return View(model);

Can anyone offer me some guidance?

Update

Just to be clear, I understand how I would raise and handle the domain event in the controller; I'm just looking for an implementation of registering for the event that will be safe to use in the context I've described.


Solution

  • Based on the example you linked to, where the author does this:

    Customer preferred = null;
    
    DomainEvents.Register<CustomerBecamePreferred>(
        p => preferred = p.Customer
            );
    
    c.DoSomething();
    

    You should be able to do this:

        var model = new ViewModel();
    
        // Register a handler that sets your bool to true if / when the event is raised
        DomainEvents.Register<YourDomainEvent>(e => model.somethingComplicatedCausedSomethingElse = true);
        // EDIT: If using the singleUseActions modification, pass the second parameter
        // DomainEvents.Register<YourDomainEvent>(e => model.somethingComplicatedCausedSomethingElse = true, true);
    
        // Call the service. If it raises the event, the handler you just registered will set your bool
        service.doSomethingComplicated();
    
        return View(model);
    

    Edit (DomainEvents modification) This is untested and written in the StackOverflow edit box, but it's where I'd start. I'm using an optional parameter so that existing calls need not be modified, and a separate list "singleUseActions" to leave the existing guts as untouched as possible. Hope it helps.

    public static class DomainEvents
      { 
          [ThreadStatic] //so that each thread has its own callbacks
          private static List<Delegate> actions;
    
          [ThreadStatic] //so that each thread has its own callbacks
          private static List<Delegate> singleUseActions;
    
          public static IContainer Container { get; set; } //as before
    
          //Registers a callback for the given domain event
          public static void Register<T>(Action<T> callback, bool isSingleUse = false) where T : IDomainEvent
          {
             List<Delegate> targetList;
             if (isSingleUse)
             {
                 if (singleUseActions == null) singleUseActions = new List<Delegate>();
                 targetList = singleUseActions;
             }
             else
             {
                 if (actions == null) actions = new List<Delegate>();
                 targetList = actions;
             }
    
             targetList.Add(callback);
         }
    
         //Clears callbacks passed to Register on the current thread
         public static void ClearCallbacks ()
         {
             actions = null;
             singleUseActions = null;
         }
    
         //Raises the given domain event
         public static void Raise<T>(T args) where T : IDomainEvent
         {
            if (Container != null)
               foreach(var handler in Container.ResolveAll<Handles<T>>())
                  handler.Handle(args);
    
            if (actions != null)
                foreach (var action in actions)
                    if (action is Action<T>)
                        ((Action<T>)action)(args);
    
            if (singleUseActions != null)
                // Don't foreach because we are going to modify the collection
                for (int index = singleUseActions.Count - 1; index > -1; index--)
                {
                    var singleUseAction = singleUseActions[index];
                    if (singleUseAction is Action<T>)
                    {
                        ((Action<T>)singleUseAction)(args);
                        singleUseActions.RemoveAt(index);
                    }
                }
    
    
      }
    }