Wednesday, 18 December 2013

Unit Of Work Abstraction For NHibernate or Entity Framework C# Example

Unit Of Work is another great pattern that every software engineer needs to have in his tool box. So what's a purpose of Unit Of Work?

 “Maintains a list of objects affected by a business transaction and coordinates the writing out of changes and the resolution of concurrency problems.” – Patterns of enterprise application architecture, Martin Fowler

Sounds like a lot of work, managing object state, relationships, concurrency and transactions can be cumbersome. However we are in luck, modern ORM's like NHibernate and Entity Framework support implicit Unit Of Work. In NHibernate it’s part of the ISession and in Entity Framework it’s part of the Context.

NHibernate
using(ISession session = sessionFactory.OpenSession()) 
using(ITransaction tx = session.BeginTransaction()) 
{ 
        Customer customer = new Customer { FirstName = "Peter" };
        session.Save(customer);
        tx.Commit(); 
} 

Entity Framework
using (SomeContext context = new SomeContext())
{
    Customer customer = new Customer { FirstName = "Peter" };
    context.Customer.Add(customer);
    context.SaveChanges();
}

ORM's have built in Unit Of Work, ideally these need to be abstracted away. Abstraction will allow us to move to a different ORM platform in the future and yield cleaner architecture. Lets take a look at the abstracted example:
Customer customer = new Customer { FirstName = "Peter"};
this.repositoryCustomer.Add(customer);
this.unitOfWork.Commit();

When it comes to working with databases it's important to read and write within a transaction scope. This ensures that there are no data inconsistencies, performance issues and second level cache is used. Please read this article for more information. So we need to open a transaction, but when do we do it?
  1. Open transaction manually in the service layer - Default Unit Of Work interface doesn't support .BeginTransaction, I don't believe it should be controlled by the user. Although this is a clean option.
  2. Open transaction on the page context creation - This feels dirty, each time some one hits your website your ORM will open a connection and begin a transaction. Not great for performance and is dangerous. 
  3. Open transaction in the repository on the first read or write - Best option, it's automatic, and the only down side is that there will be transaction null check each time (singleton). But you get cleaner service implementation.
In this article I am going to be focusing on point 3.

NHibernate Abstraction

Configuration Singleton
public static class NHConfigurationSingleton
    {
        private static Configuration configuration = null;
        private static object lockObj = new object();

        public static Configuration Configuration
        {
            get
            {
                lock (lockObj)
                {
                    if (configuration == null)
                    {
                        string[] resourceNames;
                        string nHResource = string.Empty;
                        Assembly[] asmArray = AppDomain.CurrentDomain.GetAssemblies();

                        foreach (Assembly asm in asmArray)
                        {
                            resourceNames = asm.GetManifestResourceNames();
                            nHResource = resourceNames.FirstOrDefault(x => x.ToLower().Contains("hibernate.config"));
                            if (!string.IsNullOrEmpty(nHResource))
                            {
                                using (Stream resxStream = asm.GetManifestResourceStream(nHResource))
                                {
                                    configuration = new Configuration();
                                    configuration.Configure(new XmlTextReader(resxStream));
                                }
                            }
                        }
                    }
                }

                return configuration;
            }
        }
    }

Session Factory Singleton
    public static class NHSessionFactorySingleton
    {
        private static ISessionFactory sessionFactory = null;
        private static object lockObj = new object();

        public static ISessionFactory SessionFactory
        {
            get
            {
                lock (lockObj)
                {
                    if (sessionFactory == null)
                    {
                        sessionFactory = NHConfigurationSingleton.Configuration.BuildSessionFactory();
                    }
                }

                return sessionFactory;
            }
        }
    }


Unit Of Work
    
public interface IUnitOfWork : IDisposable
    {
        void Commit();
        void Rollback();
    }

public class NHUnitOfWork : IUnitOfWork
    {
        private ISession session;
        private ITransaction transaction;
        public ISession Session { get { return this.session; } }

        public NHUnitOfWork() { }

        public void OpenSession()
        {
            if (this.session == null || !this.session.IsConnected)
            {
                if (this.session != null)
                    this.session.Dispose();

                this.session = NHSessionFactorySingleton.SessionFactory.OpenSession();
            }
        }

        public void BeginTransation(IsolationLevel isolationLevel = IsolationLevel.ReadCommitted)
        {
            if (this.transaction == null || !this.transaction.IsActive)
            {
                if (this.transaction != null)
                    this.transaction.Dispose();

                this.transaction = this.session.BeginTransaction(isolationLevel);
            }
        }

        public void Commit()
        {
            try
            {
                this.transaction.Commit();
            }
            catch
            {
                this.transaction.Rollback();
                throw;
            }
        }

        public void Rollback()
        {
            this.transaction.Rollback();
        }

        public void Dispose()
        {
            if (this.transaction != null)
            {
                this.transaction.Dispose();
                this.transaction = null;
            }

            if (this.session != null)
            {
                this.session.Dispose();
                session = null;
            }
        }
    }

Repository
  
public class NHRepository<TEntity> : IRepoistory<TEntity>
        where TEntity : IDomainEntity
    {
        readonly NHUnitOfWork nhUnitOfWork;

        public NHRepository(NHUnitOfWork nhUnitOfWork)
        {
            this.nhUnitOfWork = nhUnitOfWork;
        }

        public void Add(TEntity entity)
        {
            this.nhUnitOfWork.OpenSession();
            this.nhUnitOfWork.BeginTransation();
            this.nhUnitOfWork.Session.Save(entity);
        }

        public void Remove(TEntity entity)
        {
            this.nhUnitOfWork.OpenSession();
            this.nhUnitOfWork.BeginTransation();
            this.nhUnitOfWork.Session.Delete(entity);
        }

        public void Update(TEntity entity)
        {
            this.nhUnitOfWork.OpenSession();
            this.nhUnitOfWork.BeginTransation();
            this.nhUnitOfWork.Session.Update(entity);
        }
        
        public IEnumerable<TEntity> GetAll()
        {
            this.nhUnitOfWork.OpenSession();
            this.nhUnitOfWork.BeginTransation();
            return this.nhUnitOfWork.Session.Query<TEntity>();
        }
    }

Entity Framework Abstraction

Unit Of Work
 
   public class EFUnitOfWork : IUnitOfWork
    {
        DbContext context;
        DbContextTransaction transaction;
        public DbContext Context { get { return this.context; } }

        public EFUnitOfWork() 
        { 
            this.context = new ECommerceEntities();
        }

        public void OpenTransaction(IsolationLevel isolationLevel = IsolationLevel.ReadCommitted)
        {
            if (this.transaction == null)
            {
                if (this.transaction != null)
                    this.transaction.Dispose();

                this.transaction = this.context.Database.BeginTransaction(isolationLevel);
            }
        }

        public void Commit()
        {
            try
            {
                this.context.SaveChanges();
                this.transaction.Commit();
            }
            catch
            {
                this.transaction.Rollback();
                throw;
            }
        }

        public void Rollback()
        {
            this.transaction.Rollback();
        }

        public void Dispose()
        {
            if(this.transaction != null)
            {
                this.transaction.Dispose();
                this.transaction = null;
            }

            if(this.context != null)
            {
                this.context.Database.Connection.Close();
                this.context.Dispose();
                this.context = null;
            }
        }
    }

Repository
 
 public class EFRepository<TEntity> : IRepoistory<TEntity>
        where TEntity : class, IDomainEntity
    {
        readonly EFUnitOfWork unitOfWork;

        public EFRepository(EFUnitOfWork unitOfWork)
        {
            this.unitOfWork = unitOfWork;
        }

        public void Add(TEntity entity)
        {
            this.unitOfWork.OpenTransaction();
            this.unitOfWork.Context.Set<TEntity>().Add(entity);
        }

        public void Remove(TEntity entity)
        {
            this.unitOfWork.OpenTransaction();
            this.unitOfWork.Context.Set<TEntity>().Remove(entity);
        }

        public IEnumerable<TEntity> GetAll()
        {
            this.unitOfWork.OpenTransaction();
            return this.unitOfWork.Context.Set<TEntity>().ToList();
        }
    }

Example usage (see SQL trace output below)
    
public class CustomerService
    {
        readonly IUnitOfWork unitOfWork;
        readonly IRepoistory<Customer> repositoryCustomer;

        public CustomerService(IUnitOfWork unitOfWork, IRepoistory<Customer> repositoryCustomer)
        {
            this.unitOfWork = unitOfWork;
            this.repositoryCustomer = repositoryCustomer;
        }

  public CustomerDto Add(string firstName, string lastName)
        {
            int existingWithSameName = this.repositoryCustomer.GetAll()
                    .Count(customer => customer.FirstName == firstName && customer.LastName == lastName);

            if (existingWithSameName != 0)
                throw new Exception("User already exists with this name");

            Customer customerNew = new Customer(firstName, lastName);
            
            this.repositoryCustomer.Add(customerNew);
            this.unitOfWork.Commit();

            return new CustomerDto(customerNew.Id, customerNew.FirstName, customerNew.LastName);
        }

    }

NHibernate SQL Trace Output
To see exactly what's going on i've decided to go through SQL logs. It seems to behave correctly, it connects, starts a transaction, performs required operations, commits transaction and closes all connections.
2013-12-17 23:24:24.09 spid51      ODS Event: Login connection 51
2013-12-17 23:24:24.09 spid51      ODS Event: Remote_ods : Xact 0 ORS#: 1, connId: 0
2013-12-17 23:24:24.09 spid51      Xact BEGIN for Desc: 3300000001
2013-12-17 23:24:25.19 spid51      ODS Event: language_exec : Xact 3300000001 ORS#: 1, connId: 0
2013-12-17 23:24:25.19 spid51         Text:select customer0_.Id as Id0_, customer0_.FirstName as FirstName0_, customer0_.LastName as LastName0_ from ECommerce.dbo.Customer customer0_
2013-12-17 23:24:25.35 spid51      Parameter# 0: Name=,Flags=0,Xvt=231,MaxLen=166,Len=166,Pxvar Value=INSERT INTO ECommerce.dbo.Customer (FirstName, LastName, Id) VALUES (@p0, @p1, @p2)
2013-12-17 23:24:25.36 spid51      Parameter# 1: Name=,Flags=0,Xvt=231,MaxLen=116,Len=116,Pxvar Value=@p0 nvarchar(4000),@p1 nvarchar(4000),@p2 uniqueidentifier
2013-12-17 23:24:25.36 spid51      Parameter# 2: Name=@p0,Flags=0,Xvt=231,MaxLen=8000,Len=10,Pxvar Value=ZanNH
2013-12-17 23:24:25.36 spid51      Parameter# 3: Name=@p1,Flags=0,Xvt=231,MaxLen=8000,Len=14,Pxvar Value=70ba671
2013-12-17 23:24:25.36 spid51      Parameter# 4: Name=@p2,Flags=0,Xvt=36,MaxLen=16,Len=16,Pxvar Value=E5EF2FD7-532B-4CEA-80F4-8B095D1F983A
2013-12-17 23:24:25.36 spid51          IPC Name: sp_executesql
2013-12-17 23:24:25.36 spid51      ODS Event: execrpc : Xact 3300000001 ORS#: 1, connId: 0
2013-12-17 23:24:25.36 spid51      ODS Event: Remote_ods : Xact 3300000001 ORS#: 1, connId: 0
2013-12-17 23:24:25.36 spid51      Xact COMMIT for Desc: 3300000001
2013-12-17 23:24:27.71 Server      ODS Event: Exit spid 51
2013-12-17 23:24:27.71 spid51      ODS Event: Logout connection 51
2013-12-17 23:24:27.71 Server      ODS Event: Close conn 0 on spid 51

Entity Framework SQL Trace Output
2013-12-17 23:27:06.21 spid51      ODS Event: Login connection 51
2013-12-17 23:27:06.22 Server      Connection 0x00000001EE6433F0 connId:1, spid:51     New MARS Session 
2013-12-17 23:27:06.22 spid51      ODS Event: Remote_ods : Xact 0 ORS#: 1, connId: 1
2013-12-17 23:27:06.22 spid51      Xact BEGIN for Desc: 3300000001
2013-12-17 23:27:08.06 Server      Connection 0x00000001F2F317A0 connId:2, spid:51     New MARS Session 
2013-12-17 23:27:08.06 spid51      ODS Event: language_exec : Xact 3300000001 ORS#: 1, connId: 2
2013-12-17 23:27:08.06 spid51         Text:SELECT 
    [Extent1].[Id] AS [Id], 
    [Extent1].[FirstName] AS [FirstName], 
    [Extent1].[LastName] AS [LastName]
    FROM [dbo].[Customer] AS [Extent1]
2013-12-17 23:27:08.56 spid51      Parameter# 0: Name=,Flags=0,Xvt=231,MaxLen=154,Len=154,Pxvar Value=INSERT [dbo].[Customer]([Id], [FirstName], [LastName])
VALUES (@0, @1, @2)

2013-12-17 23:27:08.56 spid51      Parameter# 1: Name=,Flags=0,Xvt=231,MaxLen=102,Len=102,Pxvar Value=@0 uniqueidentifier,@1 varchar(256),@2 varchar(256)
2013-12-17 23:27:08.56 spid51      Parameter# 2: Name=@0,Flags=0,Xvt=36,MaxLen=16,Len=16,Pxvar Value=4318564C-304D-4792-85C5-F0113A873700
2013-12-17 23:27:08.56 spid51      Parameter# 3: Name=@1,Flags=0,Xvt=167,MaxLen=256,Len=5,Pxvar Value=ZanEF
2013-12-17 23:27:08.56 spid51      Parameter# 4: Name=@2,Flags=0,Xvt=167,MaxLen=256,Len=7,Pxvar Value=2c22266
2013-12-17 23:27:08.56 spid51          IPC Name: sp_executesql
2013-12-17 23:27:08.56 spid51      ODS Event: execrpc : Xact 3300000001 ORS#: 1, connId: 2
2013-12-17 23:27:08.58 spid51      ODS Event: Remote_ods : Xact 3300000001 ORS#: 1, connId: 1
2013-12-17 23:27:08.58 spid51      Xact COMMIT for Desc: 3300000001
2013-12-17 23:27:08.61 Server      ODS Event: Close conn 1 on spid 51
2013-12-17 23:27:08.61 Server      ODS Event: Close conn 2 on spid 51
2013-12-17 23:27:08.61 Server      ODS Event: Exit spid 51
2013-12-17 23:27:08.61 spid51      ODS Event: Logout connection 51
2013-12-17 23:27:08.61 Server      ODS Event: Close conn 0 on spid 51


We can pass NHUnitOfWork or EFUnitOfWork in to CustomerService and it will work as expected. Do avoid calling Unit Of Work within the Repository i.e. this.repositoryCustomer.Commit(), I've seen this a lot around the web. Unit Of Work should not be bound directly to it.

*Note: Code in this article is not production ready and is used for prototyping purposes only. If you have suggestions or feedback please do comment.


References: 

Saturday, 7 December 2013

Publish Subscribe / Pipeline Pattern For MVP Example C#

Publish Subscribe is a great pattern, it's one of the patterns that really got me in to patterns in the first place.

The Problem
Not so long ago I was working with windows forms and web forms. Web form applications can potentially have many user controls on a page, if something happens in user control A then user control B or a web page needs to be updated. Windows forms data grids and controls might need to be refreshed if something happens on the child form.

You can use delegates/event handlers, and subscribe to changes. This is kind of a viable approach, but you are creating direct dependencies between different Presenters. Additionally, if you have many controls managing these delegates and handlers can become cumbersome.

Publish Subscribe Pattern C# - Before
Event handlers subscribing to different user controls, what a mess...


The Solution
Solution is very logical, inject a single pub/sub pipeline that will live for the lifetime of what's being rendered.

Publish Subscribe Pattern C# - After
Presenters subscribing to a single pub/sub pipeline. 

    public interface IPubSub : IDisposable
    {
        void Subscribe(IReceiver receiver, int messageId);

        void Publish<TEventArgs>(object sender, TEventArgs e, int messageId) 
            where TEventArgs : EventArgs;

        void Unsubscribe(IReceiver receiver, int messageId);
    }

 public sealed class PubSub : IPubSub
    {
        private readonly Dictionary<int, List<IReceiver>> messageIdToReceiver;

        public PubSub()
        {
            this.messageIdToReceiver = new Dictionary<int, List<IReceiver>>();
        }

        public void Subscribe(IReceiver receiver, int messageId)
        {
            List<IReceiver> receivers;

            if (this.messageIdToReceiver.TryGetValue(messageId, out receivers))
            {
                receivers.Add(receiver);
            }
            else
            {
                this.messageIdToReceiver.Add(messageId, new List<IReceiver>() { receiver });
            }
        }

        public void Publish<TEventArgs>(object sender, TEventArgs e, int messageId) 
            where TEventArgs : EventArgs
        {
            List<IReceiver> receivers;

            if (this.messageIdToReceiver.TryGetValue(messageId, out receivers))
            {
                foreach (IReceiver receiver in receivers)
                {
                    IReceiver<TEventArgs> receiverToReceive = receiver as IReceiver<TEventArgs>;

                    if (receiverToReceive != null)
                    {
                        receiverToReceive.Receive(sender, e, messageId);
                    }
                }
            }
        }

        public void Unsubscribe(IReceiver receiver, int messageId) 
        {
            List<IReceiver> receivers;

            if (this.messageIdToReceiver.TryGetValue(messageId, out receivers))
            {
                if (receivers.Count > 1)
                {
                    receivers.Remove(receiver);
                }
                else if(receivers.Count == 1)
                {
                    this.messageIdToReceiver.Remove(messageId);
                }
            }
        }

        public void Dispose()
        {
            this.messageIdToReceiver.Clear();
        }
    }


    public interface IReceiver<TEventArgs> : IReceiver
        where TEventArgs : EventArgs
    {
        void Receive(object sender, TEventArgs e, int messageId);
    }

    public interface IReceiver : IDisposable
    {

    }


The Example
  
 public class PresenterA : IReceiver<EventArgs>, IDisposable
    {
        readonly IPubSub pubSub;

        public PresenterA(IPubSub pubSub)
        {
            this.pubSub = pubSub;
            this.pubSub.Subscribe(this, (int)PubSubMsg.AppInstl);
        }

        public void Receive(object sender, EventArgs e, int messageId)
        {
            if ((PubSubMsg)messageId == PubSubMsg.AppInstl)
            {
                //Now that you received the message, update the UI, etc...
            }
        }

        public void Dispose()
        {
            this.pubSub.Unsubscribe(this, (int)PubSubMsg.AppInstl);
        }
    }

    public class PresenterB
    {
        readonly IPubSub pubSub;

        public PresenterB(IPubSub pubSub)
        {
            this.pubSub = pubSub;
        }

        public void btnApplicationRemove(object sender, EventArgs e)
        {
            //Do what you need to do and then publish the message
            this.pubSub.Publish<EventArgs>(this, e, (int)PubSubMsg.AppInstl);
        }
    }

    public enum PubSubMsg
    {
        AppInstl = 1
    }

PubSub class instance can be created by the IoC container. It should be instantiated for the life time of the page context. This means that it will be isolated from the rest of the application and messages will only be distributed around a single parent view.

When it comes to a Win Form it becomes a bit more complicated. Single PubSub class instance is created and shared through out the entire application. This is great, imagine that you are 4 to 7 screens in from the main home screen. If home screen has subscribed to these messages it will receive them and update it self. When users comes back the home screen there is no need to do anything as it's already synchronized.

The Conclusion
It's an elegant solution for cross Presenter communication. It removes dependencies, and enables memory based synchronous communications.

Friday, 6 December 2013

MongoDB For Analytics C# Example


I was one of the Co-Founders of Appacts Open Source Mobile Analytics. I've wrote the original version on top of SQL Server 2008 R2. It was something I felt very comfortable with. I've heard of MongoDB about 4 years prior to Appacts. As many developers who got used to RDBMS systems I didn't see much point in MongoDB, I thought I get it and there is no point to it. It's a Document storage thing, who cares, right?

The bias towards technology has impaired my judgment, I even made assumptions that I understood how it works. It's a shame that for so many years I ignored it, and stayed away from it. It's so powerful. Let's find out what made me get out my comfort zone.

The Problem
I am getting thousands of raw data requests coming through (per second). I need to record event data in a cost effective way, additionally I need to be able to look this data up within milliseconds to give user an amazing experience.

This is how a raw data object looks like:
    
  public class Event
    {
        public string Data { get; set; }
        public EventType EventTypeId { get; set; }
        public string EventName { get; set; }
        public Int64 Length { get; set; }
        public string ScreenName { get; set; }
        public DateTime DateCreated { get; set; }
        public DateTime DateCreatedOnDevice { get; set; }
        public DateTime Date { get; set; }
        public Guid SessionId { get; set; }
        public string Version { get; set; }
        public Guid ApplicationId { get; set; }
        public Guid DeviceId { get; set; }
        public PlatformType PlatformId { get; set; }
    }

Imagine storing this for every request and then trying to query this over months of data.


The Solution
MongoDB has the following functions that are incredibly powerful:

These little functions change everything and document databases are great for data portioning.
Take the raw data example above, what do we really need to get from it? We don't actually need to store it in a pure raw format and we don't need to aggregate it. Instead we can create a new portioned aggregate object.

 public class ContentLoadSummary : Summary
    {
        public List<ContentDurationAggregate> Loads { get; set; }

        public ContentLoadSummary()
        {

        }

        public ContentLoadSummary(Event eventItem)
            : base(eventItem)
        {
            this.Loads = new List<ContentDurationAggregate>();
            this.Loads.Add(new ContentDurationAggregate(eventItem.ScreenName, 
                eventItem.EventName, eventItem.Length / 1000));
        }

    }

    public class ContentDurationAggregate : DurationAggregate
    {
        public string Content { get; set; }
        public string ScreenContent { get; set; }

        public ContentDurationAggregate(string screen, string content, long seconds)
            : base(screen, seconds)
        {
            this.Content = content;
            this.ScreenContent = String.Concat(screen, content);
        }

        public ContentDurationAggregate CopyOnlyKey()
        {
            return new ContentDurationAggregate(this.Key, this.Content, 0);
        }
    }

    public class DurationAggregate : Aggregate<string>
    {
        public long Seconds { get; set; }

        public DurationAggregate(string key, long seconds)
            : base(key)
        {
            this.Seconds = seconds;
        }
    }

 public class Aggregate<TKey>
    {
        public TKey Key { get; set; }
        public int Count { get; set; }

        public Aggregate()
        {

        }

        public Aggregate(TKey key)
        {
            this.Key = key;
        }

        public Aggregate<TKey> CopyOnlyKeys()
        {
            return new Aggregate<TKey>(this.Key);
        }
    }


Summary is partition base, this allows us to create new or append existing document with different dates, application versions, application platforms, etc.
    
   public abstract class Summary
    {
        public ObjectId Id { get; set; }
        public string Version { get; set; }
        public DateTime Date { get; set; }
        public Guid ApplicationId { get; set; }
        public PlatformType PlatformId { get; set; }
        public int Count { get; set; }

        public Summary()
        {

        }

        public Summary(Item item)
        {
            this.Version = item.Version;
            this.Date = DateTime.SpecifyKind(item.Date, DateTimeKind.Utc);
            this.ApplicationId = item.ApplicationId;
            this.PlatformId = item.PlatformId;
            this.Count++;
        }

        public Summary(DeviceInfo deviceInfo, ApplicationInfo applicationInfo)
        {
            this.Date = DateTime.SpecifyKind(deviceInfo.Date, DateTimeKind.Utc);
            this.ApplicationId = applicationInfo.ApplicationId;
            this.PlatformId = deviceInfo.PlatformType;
            this.Version = applicationInfo.Version;
            this.Count++;
        }
    }

The Example
 

 //You create a query, you are trying to see if you can find the data in the collection that matches your parameters.
                IMongoQuery queryBase = Query.And
                    (
                        Query<ContentLoadSummary>.EQ<DateTime>(mem => mem.Date, entity.Date),
                        Query<ContentLoadSummary>.EQ<Guid>(mem => mem.ApplicationId, entity.ApplicationId),
                        Query<ContentLoadSummary>.EQ<string>(mem => mem.Version, entity.Version),
                        Query<ContentLoadSummary>.EQ<PlatformType>(mem => mem.PlatformId, entity.PlatformId)
                    );

                // If you query and data doesn't exist then you will need to create a new document with the parameters, if it does exist then you just want to .Inc(=>) (Increment) a count
                IMongoUpdate update = Update<ContentLoadSummary>
                    .SetOnInsert(x => x.Version, entity.Version)
                    .SetOnInsert(x => x.Date, entity.Date)
                    .SetOnInsert(x => x.ApplicationId, entity.ApplicationId)
                    .SetOnInsert(x => x.PlatformId, entity.PlatformId)
                    .SetOnInsert(x => x.Loads, new List<ContentDurationAggregate>())
                    .Inc(mem => mem.Count, entity.Count);

                this.GetCollection<ContentLoadSummary>().FindAndModify(queryBase, SortBy.Descending("Date"), update, false, true);


//You need to extend your original query to also do a look up on the collection.
IMongoQuery queryLoadInsert = Query.And
                    (
                        queryBase,
                        Query.NE("Loads.ScreenContent", BsonValue.Create(entity.Loads.First().ScreenContent))
                    );

                IMongoUpdate insertLoad = Update
                    .Push("Loads", BsonValue.Create(entity.Loads.First().CopyOnlyKey().ToBsonDocument()));

//Does a look up on the query and then pushes a record in to the collection if it doesn't find the record
                this.GetCollection<ContentLoadSummary>().Update(queryLoadInsert, insertLoad);

                //Does a look on the record, if it finds it then it Increments Seconds and Count
                IMongoQuery queryLoadUpdate = Query.And
                    (
                        queryBase,
                        Query.EQ("Loads.ScreenContent", BsonValue.Create(entity.Loads.First().ScreenContent))
                    );

                IMongoUpdate updateContentLoad = Update
                    .Inc("Loads.$.Seconds", entity.Loads.First().Seconds)
                    .Inc("Loads.$.Count", 1);

                this.GetCollection<ContentLoadSummary>().Update(queryLoadUpdate, updateContentLoad);

The Output
 
{ 
    "ApplicationId" : BinData(3,"JyVgNisUD0S/P4VvblnRpg=="), 
    "Count" : 3, 
    "Date" : ISODate("2013-05-25T00:00:00Z"), 
    "Loads" : 
           [ 
              {       "Key" : "Searching", 
                      "Count" : 10,    
                      "Seconds" : NumberLong(102),      
                      "Content" : "ContentLookup", 
                       "ScreenContent" : "SearchingContentLookup" 
              },
              {       "Key" : "Saving", 
                      "Count" : 3,    
                      "Seconds" : NumberLong(7),      
                      "Content" : "CalledNumber", 
                      "ScreenContent" : "SavingCalledNumber" 
              } 
           ], 
    "PlatformId" : 6, 
    "Version" : "1.6", 
    "_id" : ObjectId("51a26322fef1ae002dd60a73") 
}

The Conclusion
MongoDB is very powerful and it's here to stay. Polyglot persistence is the way forward and companies/individuals need to embrace it. It's all about using the right tool for the right job and trying not to be too biased. It's time to get out of your comfort zone.

Saturday, 16 November 2013

Applied Domain-Driven Design (DDD), Part 6 - Application Services

We have developed our domain, setup our infrastructure, now we need to expose our domain to Applications. This is where Application Service comes in.

Any kind of application should be able to consume your Application Service and use it, mobile, desktop or web. It's good to keep this in mind when you setup your Application Service and Distributed Interface Layer (Web Service).

Application Service Contract (lives in a separate project):
    public interface ICartService
    {
        CartDto Add(Guid customerId, CartProductDto cartProductDto);
        CartDto Remove(Guid customerId, CartProductDto cartProductDto);
        CartDto Get(Guid customerId);
        CheckOutResultDto CheckOut(Guid customerId);
    }

   //Dto's are Data Transfer Objects, they are very important as they allow you to input and get the output from Application Services without exposing the actual Domain.
    public class CartDto
    {
        public Guid CustomerId { get; set; }
        public List<CartProductDto> Products { get; set; }
        public DateTime Created { get; set; }
        public DateTime Modified { get; set; }
    }

    public class CartProductDto
    {
        public Guid ProductId { get; set; }
        public int Quantity { get; set; }
    }

    public class CheckOutResultDto
    {
        public Nullable<Guid> PurchaseId { get; set; }
        public Nullable<CheckOutIssue> CheckOutIssue { get; set; }
    }



Application Service Implementation:
 

    public class CartService : ICartService
    {
        readonly IRepository<Customer> repositoryCustomer;
        readonly IRepository<Product> repositoryProduct;
        readonly IUnitOfWork unitOfWork;
        readonly ITaxDomainService taxDomainService;

        public CartService(IRepository<Customer> repositoryCustomer, 
            IRepository<Product> repositoryProduct, IUnitOfWork unitOfWork, ITaxDomainService taxDomainService)
        {
            this.repositoryCustomer = repositoryCustomer;
            this.repositoryProduct = repositoryProduct;
            this.unitOfWork = unitOfWork;
            this.taxDomainService = taxDomainService;
        }

        public CartDto Add(Guid customerId, CartProductDto productDto)
        {
            CartDto cartDto = null;
            Customer customer = this.repositoryCustomer.FindById(customerId);
            Product product = this.repositoryProduct.FindById(productDto.ProductId);

            this.validateCustomer(customerId, customer);
            this.validateProduct(product.Id, product);

            decimal tax = this.taxDomainService.Calculate(customer, product);

            customer.Cart.Add(CartProduct.Create(customer.Cart, product, productDto.Quantity, tax));

            cartDto = Mapper.Map<Cart, CartDto>(customer.Cart);
            this.unitOfWork.Commit();
            return cartDto;
        }

        public CartDto Remove(Guid customerId, CartProductDto productDto)
        {
            CartDto cartDto = null;
            Customer customer = this.repositoryCustomer.FindById(customerId);
            Product product = this.repositoryProduct.FindById(productDto.ProductId);

            this.validateCustomer(customerId, customer);
            this.validateProduct(productDto.ProductId, product);

            customer.Cart.Remove(product);
            cartDto = Mapper.Map<Cart, CartDto>(customer.Cart);
            this.unitOfWork.Commit();
            return cartDto;
        }

        public CartDto Get(Guid customerId)
        {
            Customer customer = this.repositoryCustomer.FindById(customerId);
            this.validateCustomer(customerId, customer);
            return Mapper.Map<Cart, CartDto>(customer.Cart);

        }

        public CheckOutResultDto CheckOut(Guid customerId)
        {
            CheckOutResultDto checkOutResultDto = null;
            Customer customer = this.repositoryCustomer.FindById(customerId);
            this.validateCustomer(customerId, customer);

            Nullable<CheckOutIssue> checkOutIssue = customer.Cart.IsCheckOutReady();

            if (!checkOutIssue.HasValue)
            {
                Purchase purchase = customer.Cart.Checkout();
                checkOutResultDto = Mapper.Map<Purchase, CheckOutResultDto>(purchase);
                this.unitOfWork.Commit();
            }

            return checkOutResultDto;
        }

        //this is just an example, don't hard code strings like this, use reference data or error codes
private void validateCustomer(Guid customerId, Customer customer)
        {
            if (customer == null)
                throw new Exception(String.Format("Customer was not found with this Id: {0}", customerId));
        }

        private void validateProduct(Guid productId, Product product)
        {
            if (product == null)
                throw new Exception(String.Format("Product was not found with this Id: {0}", productId));
        }
    }

Example usage:
            
            this.cartService.Add(
                this.customer.Id, 
                new CartProductDto()
                {
                    ProductId = viewModel.ProductId,
                    Quantity = 2
                }
            );

            CheckOutResultDto checkoutResult = this.cartService.CheckOut(this.customer.id);



Summary:
  • Application Service is a gateway in to your Domain Model Layer via Dto's (Data Transfer Objects)
  • Application Service should not encapsulate any domain logic, it should be really thin 
  • Application Service method should do only one thing and do it well with one region of the domain, don't mix it to "make it more performance efficient for the Application that's consuming it".
  • To access Application Service you expose interface and Dto's for inputs and outputs (it's important not to expose your Domain Entity in a raw format, Dto is a proxy and it protects your domain)
  • Presenter (mobile app, desktop or web), should call different services to get data it needs and manipulate it to suit the UI. This might seem inefficient, or wasteful at first. You will realise that actually it's just as fast (if not faster), easier to test and maintain. 

Tips:
  • Use AutoMapper to map your Domain Entity to Dto's, don't waste your time with manual mapping. It clutters your implementation code and maintenance becomes a nightmare. 
  • Don't think of screens when it comes to exposing Application Services, it's an API, think how a mobile app would access it, or how external potential customers would use it.
  • Realise that you will end up writing Application Services that suit your UI. This is only natural as this is what you been doing for a while. It will take a few goes before you change your thinking.
  • Application Service can be consumed directly if you don't need distribution i.e. your MVC app will just reference Application Service directly, you can then just try and catch errors in your Controller.
  • Application Service can be exposed via Web Service (Distributed Interface Layer). This further abstraction give you ability to "try and catch" errors so they can be exposed in a friendlier manner. Additionally it allows you to future proof your application e.g. versioning.

Useful links:
  • SOA.com Service-oriented architecture explained in 7 steps

*Note: Code in this article is not production ready and is used for prototyping purposes only. If you have suggestions or feedback please do comment. 

Applied Domain-Driven Design (DDD), Part 4 - Infrastructure

If you come from database centric development (where database is the heart of the application) then this is going to be hard for you. In domain-driven design database and general data sources are not important, your application is persistence ignorant.

Put your infrastructure interfaces in to Domain Model Layer. Your domain will use them to get data, it doesn't need to care how, it just knows there is an interface exposed and it will use it. This simplifies things and allows you to focus on your actual Domain rather worrying about what database you will be using, where data is coming from, etc.

Infrastructure Contracts:
    public interface IEmailDispatcher
    {
        void Dispatch(MailMessage mailMessage);
    }

    public interface INewsletterSubscriber
    {
        void Subscribe(Customer customer);
    }
    //this lives in the core library and you can inherit from it and extend it e.g. ICustomerRepository : IRepository<Customer> then you can add some custom methods to your new interface. This can be useful if you want to uselize some of rich features of the ORM that you are using (should be a very rare case)
    public interface IRepository<TEntity> 
        where TEntity : IDomainEntity
    {
        TEntity FindById(Guid id);
        TEntity FindOne(ISpecification<TEntity> spec);
        IEnumerable<TEntity> Find(ISpecification<TEntity> spec);
        void Add(TEntity entity);
        void Remove(TEntity entity);
    } 



Infrastructure Implementation (lives in the Infrastructure Layer):
 

public class NHibernateRepository<TEntity> : IRepository<TEntity>
        where TEntity : IDomainEntity
    {
        public TEntity FindById(Guid id)
        {
            throw new NotImplementedException();
        }

        public TEntity FindOne(ISpecification<TEntity> spec)
        {
            throw new NotImplementedException();
        }

        public IEnumerable<TEntity> Find(ISpecification<TEntity> spec)
        {
            throw new NotImplementedException();
        }

        public void Add(TEntity entity)
        {
            throw new NotImplementedException();
        }

        public void Remove(TEntity entity)
        {
            throw new NotImplementedException();
        }
    }

    public class SmtpEmailDispatcher : IEmailDispatcher
    {
        public void Dispatch(MailMessage mailMessage)
        {
            throw new NotImplementedException();
        }
    }

    public class WSNewsletterSubscriber : INewsletterSubscriber
    {
        public void Subscribe(Customer customer)
        {
            throw new NotImplementedException();
        }
    }


Example usage:
            

    public class CustomerCreatedHandle : Handles<CustomerCreated>
    {
        readonly INewsletterSubscriber newsletterSubscriber;
        readonly IEmailDispatcher emailDispatcher;

        public CustomerCreatedHandle(INewsletterSubscriber newsletterSubscriber, IEmailDispatcher emailDispatcher)
        {
            this.newsletterSubscriber = newsletterSubscriber;
            this.emailDispatcher = emailDispatcher;
        }

        public void Handle(CustomerCreated args)
        {
            //example #1 calling an interface email dispatcher this can have differnet kind of implementation depending on context, e.g
            // smtp = SmtpEmailDispatcher (current), exchange = ExchangeEmailDispatcher, msmq = MsmqEmailDispatcher, etc... let infrastructure worry about it
            this.emailDispatcher.Dispatch(new MailMessage());

            //example #2 calling an interface newsletter subscriber  this can differnet kind of implementation e.g
            // web service = WSNewsletterSubscriber (current), msmq = MsmqNewsletterSubscriber, Sql = SqlNewsletterSubscriber, etc... let infrastructure worry about it

            this.newsletterSubscriber.Subscribe(args.Customer);
        }
    }


Summary:
  • Infrastructure contains implementation classes that actually talks to the infrastructure IO, Sql, Msmq, etc.
  • Domain is the heart of the application not the Infrastructure (this can be hard to grasp if you come from DBA background).
  • Infrastructure is not important in Domain-design design, it facilitates the application development doesn't lead it.
  • Infrastructure should not contain any domain logic, all domain logic should be in the domain. (i guarantee that when you first start out, you will put logic in there without knowing it)

Tips:
  • When it comes to repositories try and just use a generic repository and stay away from custom implementations as much as possible i.e. IRepository<Customer> = good, ICustomerRepository = bad (it's never that simple, but a good general rule to work with).
  • When you first start out with infrastructure implementations, force your self not to put any if statements in to it. This will help your mind adjust to leaving all logic out of the Infrastructure Layer.
  • Take your time and try and understand what persistence ignorance really means, also try and research polyglot persistence this will expand your understanding.

Useful links:

*Note: Code in this article is not production ready and is used for prototyping purposes only. If you have suggestions or feedback please do comment. 

Tuesday, 12 November 2013

Applied Domain-Driven Design (DDD), Part 5 - Domain Service

Domain Service is not be confused with Application Service or Web Service. Domain Service lives in the Domain Model Layer. Unlike Application or Web Service, Domain Service should not be called each time to access Domain Model Layer. You can call your repository interface in the Application Layer and get the Domain Entity directly.

Requirement:
  • Your business is responsible for collecting and paying Value Added Tax (VAT) based off your business location, your customer's location, and the type of product you are selling.

Domain Service Sample:
 
public class TaxDomainService : ITaxDomainService
    {
        readonly IRepository<ProductTax> productTax;
        readonly IRepository<CountryTax> countryTax;
        readonly Settings settings;

        public TaxDomainService(Settings settings, IRepository<ProductTax> productTax, IRepository<CountryTax> countryTax)
        {
            this.productTax = productTax;
            this.countryTax = countryTax;
            this.settings = settings;
        }

        public decimal Calculate(Customer customer, Product product)
        {
            CountryTax customerCountryTax = this.countryTax.FindById(customer.Country.Id);
            CountryTax businessCountryTax = this.countryTax.FindById(settings.BusinessCountry.Id);
            ProductTax productTax = this.productTax.FindById(product.Code.Id);
            return (product.Cost * customerCountryTax.Percentage) + 
                (product.Cost * businessCountryTax.Percentage) + (product.Cost * productTax.Percentage);
        }
}


Example usage:
            Customer customer = this.repositoryCustomer.FindById(customerId);
            Product product = this.repositoryProduct.FindById(productId);

            decimal tax = this.taxDomainService.Calculate(customer, product);

            customer.Cart.Add(CartProduct.Create(customer.Cart, product, productQuantity, tax));



Summary:
  • Domain Service allows you to capture logic that doesn't belong in the Domain Entity.
  • Domain Service allows you to orchestrate between different Domain Entities.

Tips:
  • Don't create too many Domain Services, most of the logic should reside in the domain entities, event handlers, etc. 
  • It's a great place for calculation and validation as it can access entities, and other kind of objects (e.g. Settings) that are not available via the entity graph.
  • Methods should return primitive types, custom enums are fine too.

*Note: Code in this article is not production ready and is used for prototyping purposes only. If you have suggestions or feedback please do comment. 

Wednesday, 2 October 2013

Applied Domain-Driven Design (DDD), Part 3 - Specification Pattern

Specification pattern is great, David Fancher wrote a great piece on it, i suggest you read it before you continue.

In short, specification pattern allows you to chain business queries.

Example:
   
            ISpecification<Customer> spec = 
                new CustomerRegisteredInTheLastDays(30).And(new CustomerPurchasedNumOfProducts(2));

Entity from previous posts in this series:
  
 public class Customer : IDomainEntity
    {
        private List<Purchase> purchases = new List<Purchase>();

        public Guid Id { get; protected set; }
        public string FirstName { get; protected set; }
        public string LastName { get; protected set; }
        public string Email { get; protected set; }
        public string Password { get; protected set; }
        public DateTime Created { get; protected set; }
        public bool Active { get; protected set; }

        public ReadOnlyCollection<Purchase> Purchases { get { return this.purchases.AsReadOnly(); } }

        public static Customer Create(string firstname, string lastname, string email)
        {
            Customer customer = new Customer()
            {
                FirstName = firstname,
                LastName = lastname,
                Email = email,
                Active = true,
                Created = DateTime.Today
            };

            DomainEvents.Raise<CustomerCreated>(new CustomerCreated() { Customer = customer });
            return customer;
        }

        public Purchase Checkout(Cart cart)
        {
            Purchase purchase = Purchase.Create(this, cart.Products);
            this.purchases.Add(purchase);
            DomainEvents.Raise<CustomerCheckedOut>(new CustomerCheckedOut() { Purchase = purchase });
            return purchase;
        }
    }
Specification Examples:
public class CustomerRegisteredInTheLastDays : SpecificationBase<Customer>
    {
        readonly int nDays;

        public CustomerRegisteredInTheLastDays(int nDays)
        {
            this.nDays = nDays;
        }

        public override Expression<Func<Customer,bool>>  SpecExpression
        {
            get 
            { 
                return customer => customer.Created >= DateTime.Today.AddDays(-nDays)
                    && customer.Active;
            }
        }
    }

    public class CustomerPurchasedNumOfProducts : SpecificationBase<Customer>
    {
        readonly int nPurchases;

        public CustomerPurchasedNumOfProducts(int nPurchases)
        {
            this.nPurchases = nPurchases;
        }

        public override Expression<Func<Customer,bool>>  SpecExpression
        {
            get 
            { 
                return customer => customer.Purchases.Count == this.nPurchases 
                    && customer.Active;
            }
        }
    }

Abstract Repository Query Example:
            IRepository<Customer> customerRepository = new Repository<Customer>();

            ISpecification<Customer> spec = 
                new CustomerRegisteredInTheLastDays(30).And(new CustomerPurchasedNumOfProducts(2));

            IEnumerable<Customer> customers = customerRepository.Find(spec);

Abstract Repository Example:
 
    public interface IRepository<TEntity> 
        where TEntity : IDomainEntity
    {
        TEntity FindById(Guid id);
        TEntity FindOne(ISpecification<TEntity> spec);
        IEnumerable<TEntity> Find(ISpecification<TEntity> spec);
        void Add(TEntity entity);
        void Remove(TEntity entity);
    }


Summary:
  • Specification allows you to query data in a abstract way i.e. you can query memory collections or an RDBMS. This ensures persistence/infrastructure ignorance.
  • Specification encapsulates a business rule in one spec.
  • Specification pattern allows you to chain your business rules up.
  • Specification makes your domain layer DRY i.e. you don't need to write same LINQ all over again.
  • Specifications are easy to unit test.
  • Specifications are stored in the domain layer, this provides full visibility.
  • Specifications are super elegant.
Tips:
  • Break complex business logic rules down in your specification as NHibernate might struggle to interpret them in to a SQL query. This is a generally good tip as you don't want messy SQL hitting your database. 
  • Query data around the entity properties, don't try and change the properties on the entity i.e. instead of writing customer.Created.AddDays(30) >= DateTime.Today write customer.Created >= DateTime.Today.AddDays(-30). The former will try and compile it as a SQL and will fail as it's too complex, the latter will convert the value to a parameter. 
  • As specifications are logical queries they should not change state of the caller or the calling objects. i.e. don't call state changing methods, such as customer.Checkout(....) && customer.Active == true. This tip goes hand in hand with the tip above.

Useful links:

*Note: Code in this article is not production ready and is used for prototyping purposes only. If you have suggestions or feedback please do comment. 

Thursday, 26 September 2013

Applied Domain-Driven Design (DDD), Part 2 - Domain Events

In my last post we have addressed DDD thought process and constant refining/re-factoring. In this post we are going to talk about domain events. There are many articles on this out there (see references at the bottom), so i will be brief.

When something has happened in the domain, domain event can be raised. It can be from a trivial property change to an overall object state change. This is a fantastic way to describe an actual event in your domain, i.e. customer has checked out, customer was created, etc.

Let's extend our previous e-commerce example:

    public class Product
    {
        public Guid Id { get; protected set; }
        public string Name { get; protected set; }
        public int Quantity { get; protected set; }
        public DateTime Created { get; protected set; }
        public DateTime Modified { get; protected set; }
        public bool Active { get; protected set; }
    }

    public class Cart
    {
        private List products;

        public ReadOnlyCollection Products
        {
            get { return products.AsReadOnly(); }
        }

        public static Cart Create(List products)
        {
            Cart cart = new Cart();
            cart.products = products;
            return cart;
        }
    }

    public class Purchase
    {
        private List products = new List();

        public Guid Id { get; protected set; }
        public ReadOnlyCollection Products
        {
            get { return products.AsReadOnly(); }
        }
        public DateTime Created { get; protected set; }
        public Customer Customer { get; protected set; }

        public static Purchase Create(Customer customer, ReadOnlyCollection products)
        {
            Purchase purchase = new Purchase()
            {
                Id = Guid.NewGuid(),
                Created = DateTime.Now,
                products = products.ToList(),
                Customer = customer
            };
            return purchase;
        }
    }

    public class Customer
    {
        private List purchases = new List()

        public Guid Id { get; protected set; }
        public string FirstName { get; protected set; }
        public string LastName { get; protected set; }
        public string Email { get; protected set; }
        public ReadOnlyCollection Purchases { get { return this.purchases.AsReadOnly(); } }

        public Purchase Checkout(Cart cart)
        {
            Purchase purchase = Purchase.Create(this, cart.Products);
            this.purchases.Add(purchase);
            DomainEvents.Raise(new CustomerCheckedOut() { Purchase = purchase });
            return purchase;
        }

        public static Customer Create(string firstName, string lastName, string email)
        {
            Customer customer = new Customer()
            {
                 FirstName = firstName,
                 LastName = lastName,
                 Email = email
            };
            return customer;
        }
    }

    public class CustomerCheckedOut : IDomainEvent
    {
        public Purchase Purchase { get; set; }
    }

    public class CustomerCheckedOutHandle : Handles CustomerCheckedout
    {
        public CustomerCheckedOutHandle()
        {
      
        }

        public void Handle(CustomerCheckedOut args)
        {
            //send notifications, emails, update third party systems, etc
        }
    }

For example in our case, when customer calls Customer.Checkout(...) we raise "CustomerCheckedOut" event. When event is handled it should not change the purchase object state, it should facilitate additional behavior only. For example sending out an email, updating financial monthly balance sheet, calling third party API, etc.

This is how you could use it:
    public class CustomerCheckedOut : IDomainEvent
    {
        public Purchase Purchase { get; set; }
    }

    public class CustomerCheckedOutHandle : Handles CustomerCheckedOut
    {
        readonly IEmailSender emailSender;

        public CustomerCheckedOutHandle(IEmailSender emailSender)
        {
            this.emailSender = emailSender;
        }

        public void Handle(CustomerCheckedOut args)
        {
            this.emailSender.Send(args.Purchase.Customer, EmailTemplate.PurchaseMade);
            //send notifications, update third party systems, etc
        }
    }

    public interface IEmailSender
    {
        void Send(Customer customer, EmailTemplate template);
    }

    public enum EmailTemplate
    {
        PurchaseMade,
        CustomerCreated
    }

It can be confusing, what do you put in to Customer.Checkout(...) method and what do you put in to the handler?

In my opinion Customer.Checkout(...) method should do what it needs to do with the access to the properties/fields that it has access to. So creating a purchase object and adding it to the collection, incrementing purchase count on the customer, updating last purchase date on the customer object, you get the idea.

What does handler have access to? All of the infrastructure layer interfaces. This makes it a great place to send out emails and notifications, synchronize with third party services, create audit records, etc.


Summary:
  • Domain event handlers should not change the state of the object that caused domain event to be triggered.
  • Domain events should be raised inside the domain.
  • Domain events handlers should be looked at as a side effect / chain reaction facilitator.

Useful links: 

*Note: Code in this article is not production ready and is used for prototyping purposes only. If you have suggestions or feedback please do comment. 

Applied Domain-Driven Design (DDD), Part 1 - Basics

When I started learning domain-driven design there was a lot of theory to take in, Eric Evans did a great job explaining it from theoretical point of view. As a software engineer I wanted to see some code and just to follow some examples, I found very little resource out there that showed applied domain-driven design in C#.

Over the coming weeks I will be posting series of articles on the subject, it will be my attempt to make domain-driven design simpler and easier follow. Articles that are published:


Domain Driven Design Architecture
Domain Driven Design Architecture (it's simpler then it looks) 


Before we get started let's see why DDD is so great:
  • Development becomes domain oriented not UI/Database oriented 
  • Domain layer captures all of the business logic, making your service layer very thin i.e. just a gateway in to your domain via DTO's
  • Domain oriented development allows you to implement true service-oriented architecture i.e. making your services reusable as they are not UI/Presentation layer specific 
  • Unit tests are easy to write as code scales horizontally and not vertically, making your methods thin and easily testable
  • DDD is a set of Patterns and Principles, this gives developers a framework to work with, allowing everyone in the development team to head in the same direction

Through this series of articles I will be focusing on a simple and made up e-commerce domain.

So, let's see some code:
    
    public class Product
    {
        public Guid Id { get; set; }
        public string Name { get; set; }
        public int Quantity { get; set; }
        public DateTime Created { get; set; }
        public DateTime Modified { get; set; }
        public bool Active { get; set; }
    }

    public class Customer
    {
        public Guid Id { get; set; }
        public string FirstName { get; set; }
        public string LastName { get; set; }
        public string Email { get; set; }
        public List Purchases { get; set; }
  }

    public class Purchase
    {
        public Guid Id { get; set; }
        public List Products { get; set; }
        public DateTime Created { get; set; }
        public Customer Customer { get; set; }
    }

Code above represents anemic classes. Some developers would stop here, and use these classes to pass data to service and then bind this data to the UI. Let's carry on and mature our models.

When a customer shops online they choose items first, they browse around, and then eventually they will make a purchase. So we need something that will hold the products, lets call it a Cart, this object will have no identity and it will be transient.

Cart is my value object:
    
    public class Cart
    {
        public List Products { get; set; }
    }

Cart simply contains a list of products. Customer can go ahead and checkout these products when they are ready.

We can use what was said above as a business case "Customer can go ahead and checkout these products when they are ready".

Code would look like:
            Cart cart = new Cart() 
            { 
                Products = new Product[]
                { 
                    new Product(), 
                    new Product() 
                } 
            };

            Customer customer = new Customer()
            {
                FirstName = "Josh",
                LastName = "Smith"
            };

            Purchase purchase = customer.Checkout(cart);

What's going on here? Customer checks-out the product and gets a purchase back. Normally in business context you get a receipt back, this provides basic information about the purchase, discounts, and acts as a guarantee that i can use to refer back to my purchase.

I could rename Purchase to Receipt, but wait, what's does purchase mean in the business context?

"to acquire by the payment of money or its equivalent; buy." - Dictionary.com

(Returning purchase object would make sense if customer actually made a purchase i.e. we pre-authenticated a customer and then simply passed payment authentication code to the checkout, but to keep this simple we are not going to do this)

Customer can now checkout, when they checkout they will get back a Purchase object (this will allow further data manipulation).

Code above needs to be re-factored, if we return back a purchase are we going to then add it to the collection of the customer i.e. Customer.Purchases.Add(...)? This seems strange, if we have a method Customer.Checkout(...) we should aim to capture relevant data right there and then. Customer should only expose business methods, if we have to expose something else in order to capture data then we are not doing it right.

Lets refine further:
  
    public class Customer
    {
        public Guid Id { get; set; }
        public string FirstName { get; set; }
        public string LastName { get; set; }
        public string Email { get; set; }
        public List Purchases { get; set; }

        public Purchase Checkout(Cart cart)
        {
            Purchase purchase = new Purchase()
            {
                 Customer = this,
                 Products = cart.Products,
                 Created = DateTime.Now
            };

            this.Purchases.Add(purchase);
            return purchase;
        }
    }

Ok, so now when customer checks-out a cart, purchase will be added to the purchase collection and also returned  so it can be used further in our logic. This is great, but another software engineer can go in and compromise our domain. They can just add Orders directly to the customer without checking out i.e. Customer.Orders.Add(...).

Lets refine further:
    public class Customer
    {
        private List purchases = new List();

        public Guid Id { get; set; }
        public string FirstName { get; set; }
        public string LastName { get; set; }
        public string Email { get; set; }
        public ReadOnlyCollection Purchases { get { return this.purchases.AsReadOnly(); } }

        public Purchase Checkout(Cart cart)
        {
            Purchase purchase = new Purchase()
            {
                 Customer = this,
                 Products = cart.Products,
                 Created = DateTime.Now
            };
            this.purchases.Add(purchase);
            return purchase;
        }
    }

Now orders can't be compromised, and code forces software engineers to checkout a cart. What about other properties? They are not protected. We know customer state can't just be changed, we have to go through a process. You need to ask your self, when personal information is changed on the customer, do we need to send an email out? Do we need to call some 3rd party API to sync up our records? Right now you might not have a requirement from your business analysts to do anything like this, so lets not worry about it, lets just protect these fields so they can't be changed.
  
    public class Customer
    {
        private List purchases = new List();

        public Guid Id { get; protected set; }
        public string FirstName { get; protected set; }
        public string LastName { get; protected set; }
        public string Email { get; protected set; }
        public ReadOnlyCollection Purchases { get { return this.purchases.AsReadOnly(); } }
        public Purchase Checkout(Cart cart)
        {
            Purchase purchase = new Purchase()
            {
                 Customer = this,
                 Products = cart.Products,
                 Created = DateTime.Now
            };
            this.purchases.Add(purchase);
            return purchase;
        }
    }

That's great, now other software engineers in the team can't change personal information without adding a new method such as Customer.ChangeDetails(...).

Taking in to account what was said above, thinking process, constant re-factoring and making the model match the actual business domain, this is what I've got so far:


    public class Product
    {
        public Guid Id { get; protected set; }
        public string Name { get; protected set; }
        public int Quantity { get; protected set; }
        public DateTime Created { get; protected set; }
        public DateTime Modified { get; protected set; }
        public bool Active { get; protected set; }
    }

    public class Cart
    {
        private List products;

        public ReadOnlyCollection Products
        {
            get { return products.AsReadOnly(); }
        }

        public static Cart Create(List products)
        {
            Cart cart = new Cart();
            cart.products = products;
            return cart;
        }
    }

    public class Purchase
    {
        private List products = new List();

        public Guid Id { get; protected set; }
        public ReadOnlyCollection Products
        {
            get { return products.AsReadOnly(); }
        }
        public DateTime Created { get; protected set; }
        public Customer Customer { get; protected set; }

        public static Purchase Create(Customer customer, ReadOnlyCollection products)
        {
            Purchase purchase = new Purchase()
            {
                Id = Guid.NewGuid(),
                Created = DateTime.Now,
                products = products.ToList(),
                Customer = customer
            };
            return purchase;
        }
    }

    public class Customer
    {
        private List purchases = new List()

        public Guid Id { get; protected set; }
        public string FirstName { get; protected set; }
        public string LastName { get; protected set; }
        public string Email { get; protected set; }
        public ReadOnlyCollection Purchases { get { return this.purchases.AsReadOnly(); } }

        public Purchase Checkout(Cart cart)
        {
            Purchase purchase = Purchase.Create(this, cart.Products);
            this.purchases.Add(purchase);
            return purchase;
        }

        public static Customer Create(string firstName, string lastName, string email)
        {
            Customer customer = new Customer()
            {
                 FirstName = firstName,
                 LastName = lastName,
                 Email = email
            };
            return customer;
        }
    }

Example usage:
   
            Cart cart = Cart.Create(new List() { new Product(), new Product() });
            Customer customer = Customer.Create("josh", "smith", "josh.smith@microsoft.com");
            Purchase purchase = customer.Checkout(cart);


Summary: 
  • DDD is all about capturing business logic in the domain i.e. entities, aggregate roots, value objects and domain service.
  • DDD is all about thought process and challenging where should what go and what is most logical.
  • DDD is all about constant re-factoring and maturing your model as you get further requirements. More requirements your receive the better and stronger your domain will be. Therefore requirements are gold and something that software engineers should always strive to understand.

Useful links:


*Note: Code in this article is not production ready and is used for prototyping purposes only. If you have suggestions or feedback please do comment.