My Silverlight 4 Reference Architecture: The Domain Model

Edit this page | 3 minute read

The Domain Model is roughly designed according to the DDD principles where cross-aggregate logic is handled by the command handlers instead of the more traditional domain services. One thing that I changed after I attended Greg Young's DDD/CQRS training is that I no longer allow my domain entities to enter an invalid state. So rather than having some kind of interceptor that does the validation as part of the persistency layer, all validation happens while making the changes:

    public class Agreement : VersionedEntity

    {

        public virtual string Title

        {

            get { return title; }

            set

            {

                EnsureChangesAreAllowed();

 

                if (string.IsNullOrEmpty(value))

                {

                    throw new RuleViolationException("A new agreement requires at least a title.");

                }

 

                title = value;

            }

        }

    }

I’m currently using this approach in a client project, and admit I still have to build up more experience to see if you get away with this approach. Up to now it works for me, but I wouldn’t be surprised if I run into an exception (don’t we all?).

As a side note, the VersionedEntity you see up here is just a base class including an Id and Version property for concurrency detection. Since most systems have some entities that rarely change (a.k.a. reference data), no version detection is necessary. Hence the difference between the Entity and VersionedEntity base classes.

As mentioned before, I use NHibernate accompanied by LINQ and Fluent NHibernate, but I still use the Repository pattern to hide all the LINQ stuff that goes on inside the repositories. The current request-specific ISession is tracked by a session manager class that the Command Handlers and Service Actions will create as part of their execution.

I know that I, at least for a while, advocated that all repository interfaces should expose IQueryable<T> so that you can easily execute LINQ queries from your higher layers. But I've found that testing that your code has created the proper LINQ query is virtually impossible, and now stick to exposing explicit well-named operations only.

    public interface IAgreementRepository : IRepository<Agreement>

    {

        Agreement Get(long id, long version);

        void Add(Agreement agreement);

        bool ExistsForCpiCaseNumber(string cpiCaseNumber);

        IEnumerable<AgreementValidityInCountry> Find
           
(AgreementValidityPerCountrySpecification
specification);

    }

And now that I’m discussing repositories anyway, one recurring design issue is whether to allow an entity to access any repositories or other services. My personal opinion is a pragmatic one: yes, as long as the related dependency injection doesn’t affect the performance significantly (and yes, you need to measure that). If you don’t allow this, you may very well end up with an anemic domain model.

Another aspect of my domain model approach is that to prevent any unrelated logic creeping in a command handler just because a change to an aggregate root affects another aggregate, I use Udi's approach to raise Domain Events that are handled by Event Handlers.

   public static class DomainEvents

   {

      [ThreadStatic]

      private static List<Delegate> actions;

 

      public static void Register <T>(Action<T> callback) where T : IDomainEvent

      {

         if (actions == null)

         {

            actions = new List<Delegate>();

         }

         actions.Add(callback);

      }

 

      public static void ClearCallbacks()

      {

         actions = null;

      }

 

      public static void Raise <T>(T args) where T : IDomainEvent

      {

         if (actions != null)

         {

            foreach (Delegate action in actions)

            {

               if (action is Action<T>)

               {

                  ((Action<T>) action)(args);

               }

            }

         }

      }

   }

If you want to subscribe to a particular event, do this:

DomainEvents.Register<StockCorrectedEvent>(StockCorrectedHandler.Handle);

Where the method reference resolves to an Event Handler class that implements this interface.

   public interface IHandle<T> where T : IDomainEvent

   {

      void Handle(T args);

   }

To raise a domain event from inside your domain entity, simply call the Raise method like this:

DomainEvents.Raise(new StockCorrectedEvent(this, location, originalAmount, newAmount));

Next time I'll dive into the details of my Silverlight client application and my usage of Model-View-ViewModel and the Caliburn Micro framework.

Leave a Comment