/// <summary>
        /// Save the specified <paramref name="context"/> changes for the given aggregate.
        /// </summary>
        /// <param name="aggregate">The current aggregate version for which the context applies.</param>
        /// <param name="context">The command context containing the aggregate changes to be applied.</param>
        public SaveResult Save(Aggregate aggregate, CommandContext context)
        {
            Verify.NotNull(aggregate, nameof(aggregate));
            Verify.NotNull(context, nameof(context));

            var copy          = aggregate.Copy();
            var aggregateType = aggregate.GetType();
            var key           = String.Concat(aggregateType.GetFullNameWithAssembly(), "-", aggregate.Id);

            using (var aggregateLock = new AggregateLock(aggregateType, aggregate.Id))
            {
                aggregateLock.Aquire();

                try
                {
                    var result = aggregateStore.Save(copy, context);

                    memoryCache.Set(key, copy, CreateCacheItemPolicy());

                    return(result);
                }
                catch (ConcurrencyException)
                {
                    memoryCache.Remove(key);
                    throw;
                }
            }
        }
        /// <summary>
        /// Retrieve the aggregate of the specified <paramref name="aggregateType"/> and aggregate <paramref name="id"/>.
        /// </summary>
        /// <param name="aggregateType">The type of aggregate to retrieve.</param>
        /// <param name="id">The unique aggregate id.</param>
        public Aggregate Get(Type aggregateType, Guid id)
        {
            Verify.NotNull(aggregateType, nameof(aggregateType));

            var key = String.Concat(aggregateType.GetFullNameWithAssembly(), "-", id);

            using (var aggregateLock = new AggregateLock(aggregateType, id))
            {
                aggregateLock.Aquire();

                //NOTE: We do not want to use AddOrGetExisting due to internal global cache lock while doing aggregate lookup.
                var aggregate = (Aggregate)memoryCache.Get(key);
                if (aggregate == null)
                {
                    memoryCache.Add(key, aggregate = aggregateStore.Get(aggregateType, id), CreateCacheItemPolicy());
                }

                //NOTE: Given that aggregate state is only applied during `Save`, we can return the cached instance.
                //      This avoids making a copy of the aggregate when no state changes will be applied.
                return(aggregate);
            }
        }
        /// <summary>
        /// Retrieve the aggregate of the specified <paramref name="aggregateType"/> and aggregate <paramref name="id"/>.
        /// </summary>
        /// <param name="aggregateType">The type of aggregate to retrieve.</param>
        /// <param name="id">The unique aggregate id.</param>
        public Aggregate Get(Type aggregateType, Guid id)
        {
            Verify.NotNull(aggregateType, nameof(aggregateType));

            var key = String.Concat(aggregateType.GetFullNameWithAssembly(), "-", id);
            using (var aggregateLock = new AggregateLock(aggregateType, id))
            {
                aggregateLock.Aquire();

                //NOTE: We do not want to use AddOrGetExisting due to internal global cache lock while doing aggregate lookup.
                var aggregate = (Aggregate)memoryCache.Get(key);
                if (aggregate == null)
                    memoryCache.Add(key, aggregate = aggregateStore.Get(aggregateType, id), CreateCacheItemPolicy());

                //NOTE: Given that aggregate state is only applied during `Save`, we can return the cached instance.
                //      This avoids making a copy of the aggregate when no state changes will be applied.
                return aggregate;
            }
        }
        /// <summary>
        /// Save the specified <paramref name="context"/> changes for the given aggregate.
        /// </summary>
        /// <param name="aggregate">The current aggregate version for which the context applies.</param>
        /// <param name="context">The command context containing the aggregate changes to be applied.</param>
        public SaveResult Save(Aggregate aggregate, CommandContext context)
        {
            Verify.NotNull(aggregate, nameof(aggregate));
            Verify.NotNull(context, nameof(context));

            var copy = aggregate.Copy();
            var aggregateType = aggregate.GetType();
            var key = String.Concat(aggregateType.GetFullNameWithAssembly(), "-", aggregate.Id);
            using (var aggregateLock = new AggregateLock(aggregateType, aggregate.Id))
            {
                aggregateLock.Aquire();

                try
                {
                    var result = aggregateStore.Save(copy, context);

                    memoryCache.Set(key, copy, CreateCacheItemPolicy());

                    return result;
                }
                catch (ConcurrencyException)
                {
                    memoryCache.Remove(key);
                    throw;
                }
            }
        }