C# – Breaking the “ubiquitous language” by having an IoC Container in Domain Model

cdomain-driven-designioc-containers

I am a bit new to DDD and bear with me if my understanding seems way off.

My question is about Udi's solution to domain events, particularly the class DomainEvents (see code below)

An excerpt from Udi's code. It lives domain model as a static class.

public static class DomainEvents
{ 
   [ThreadStatic] //so that each thread has its own callbacks
   private static List<Delegate> actions;

   public static IContainer Container { get; set; } //as before

   //Registers a callback for the given domain event
   public static void Register<T>(Action<T> callback) where T : IDomainEvent
   {
      if (actions == null)
         actions = new List<Delegate>();

      actions.Add(callback);
  }

  //Clears callbacks passed to Register on the current thread
  public static void ClearCallbacks ()
  { 
      actions = 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);
  }
} 

Based on the code above, in order for the DomainEvents to be used by the domain model, both must first be in the same assembly. Which makes the DomainEvents part of the domain model right? (I may be wrong here)

So my question is: Does DomainEvents itself breaks the rule "ubiquitous language of DDD"? Because it's implementation does not pertain to any domain.

My other concern is that the static member IContainer creates an ioc-container-dependency in the domain model. Though I am not really sure if Udi's IContainer is an interface or an actual IoC container.

My 2nd question is: What is this IContainer in the DomainEvents class? If it is truly an IoC container then doesn't it break the rule of "DDD should not have an infrastructure in the domain"? Is my understanding correct that an IoC-Container is considered an infrastructure? (Please correct me if I'm wrong)

If you may find any of this confusing, please say so.

EDIT:

I have built my projects where the domain model is separated on its own assembly (I call this business layer) with absolutely no references to any infrastructure components. See onion architecture.

enter image description here

Now I want to incorporate the domain events pattern. But doing so forces me to add infrastructure components to my business layer. Components being the DomainEvents and an IoC framework just to satisfy the IContainer, both having no relation to the domain whatsoever.

Isn't one of the idea of DDD is about separating the infrastructure from the domain?

Now I will play the pragmatic programmer, I just wanted to know that is it generally ok to do so? are there alternatives? What are you thoughts on this approach? Am I missing something basic here?

Best Answer

I agree with you for the first sub-question:

From my point of view the class DomainEvents is infrasturcure code that should not be implemented in the domain itself.

So instead of a static class DomainEvents in the domain layer i would prefer a nonstatic class DomainEvents in an infrastructure layer that implements an IEventHandling interface (in the domain layer) with methods Register, Raise, ...

The ioc container injets the implementation of IEventHandling as singleton to every class that needs DomainEvents.

 > My 2nd question is: What is this IContainer in the DomainEvents class?

The IOC-Container is infrastructure code so it is hidden behind an interface.

From architectual point of view i prefer referecing the ioc container only in one initialisation module and nowhere else. Therefore the icontainer should not be referenced from the class DomainEvents. Instead i would prefer to have a method DomainEvents.RegisterHandler and i would rename DomainEvents.Register to DomainEvents.RegisterEvent.

The ioc container initialisation module is repsonsible to register all handlers.

Related Topic