/// <summary>
        /// Discover all command handlers associated with any locatable <see cref="Aggregate"/> type.
        /// </summary>
        /// <param name="aggregateStore">The <see cref="Aggregate"/> store.</param>
        /// <param name="typeLocator">The type locator use to retrieve all known <see cref="Aggregate"/> types.</param>
        /// <param name="serviceProvider">The service locator used to retrieve singleton command handler dependencies.</param>
        private static Dictionary<Type, CommandHandler> DiscoverCommandHandlers(IStoreAggregates aggregateStore, ILocateTypes typeLocator, IServiceProvider serviceProvider)
        {
            var knownHandleMethods = DiscoverHandleMethods(typeLocator, serviceProvider);
            var result = new Dictionary<Type, CommandHandler>();
            var logMessage = new StringBuilder();

            logMessage.AppendLine("Discovered command handlers:");
            foreach (var aggregateMapping in knownHandleMethods.OrderBy(kvp => kvp.Key.FullName))
            {
                logMessage.Append("    ");
                logMessage.Append(aggregateMapping.Key);
                logMessage.AppendLine();

                foreach (var handleMethodMapping in aggregateMapping.Value.OrderBy(kvp => kvp.Key.FullName))
                {
                    logMessage.Append("        ");
                    logMessage.Append(handleMethodMapping.Key);
                    logMessage.AppendLine();

                    if (result.ContainsKey(handleMethodMapping.Key))
                        throw new MappingException(Exceptions.HandleMethodMustBeAssociatedWithSingleAggregate.FormatWith(aggregateMapping.Key, handleMethodMapping.Key));

                    result.Add(handleMethodMapping.Key, new CommandHandler(aggregateMapping.Key, handleMethodMapping.Key, aggregateStore, handleMethodMapping.Value));
                }
            }

            Log.Debug(logMessage.ToString);

            return result;
        }
예제 #2
0
        /// <summary>
        /// Discover all command handlers associated with any locatable <see cref="Aggregate"/> type.
        /// </summary>
        /// <param name="aggregateStore">The <see cref="Aggregate"/> store.</param>
        /// <param name="typeLocator">The type locator use to retrieve all known <see cref="Aggregate"/> types.</param>
        /// <param name="serviceProvider">The service locator used to retrieve singleton command handler dependencies.</param>
        private static Dictionary <Type, CommandHandler> DiscoverCommandHandlers(IStoreAggregates aggregateStore, ILocateTypes typeLocator, IServiceProvider serviceProvider)
        {
            var knownHandleMethods = DiscoverHandleMethods(typeLocator, serviceProvider);
            var result             = new Dictionary <Type, CommandHandler>();
            var logMessage         = new StringBuilder();

            logMessage.AppendLine("Discovered command handlers:");
            foreach (var aggregateMapping in knownHandleMethods.OrderBy(kvp => kvp.Key.FullName))
            {
                logMessage.Append("    ");
                logMessage.Append(aggregateMapping.Key);
                logMessage.AppendLine();

                foreach (var handleMethodMapping in aggregateMapping.Value.OrderBy(kvp => kvp.Key.FullName))
                {
                    logMessage.Append("        ");
                    logMessage.Append(handleMethodMapping.Key);
                    logMessage.AppendLine();

                    if (result.ContainsKey(handleMethodMapping.Key))
                    {
                        throw new MappingException(Exceptions.HandleMethodMustBeAssociatedWithSingleAggregate.FormatWith(aggregateMapping.Key, handleMethodMapping.Key));
                    }

                    result.Add(handleMethodMapping.Key, new CommandHandler(aggregateMapping.Key, handleMethodMapping.Key, aggregateStore, handleMethodMapping.Value));
                }
            }

            Log.Debug(logMessage.ToString);

            return(result);
        }
예제 #3
0
        /// <summary>
        /// Initializes a new instance of <see cref="CommandHandlerRegistry"/> with the specified <paramref name="typeLocator"/> and <paramref name="serviceProvider"/>.
        /// </summary>
        /// <param name="aggregateStore">The <see cref="Aggregate"/> store.</param>
        /// <param name="typeLocator">The type locator use to retrieve all known <see cref="Aggregate"/> types.</param>
        /// <param name="serviceProvider">The service locator used to retrieve singleton command handler dependencies.</param>
        public CommandHandlerRegistry(IStoreAggregates aggregateStore, ILocateTypes typeLocator, IServiceProvider serviceProvider)
        {
            Verify.NotNull(typeLocator, nameof(typeLocator));
            Verify.NotNull(serviceProvider, nameof(serviceProvider));

            knownCommandHandlers = DiscoverCommandHandlers(aggregateStore, typeLocator, serviceProvider);
        }
        /// <summary>
        /// Initializes a new instance of <see cref="CommandHandlerRegistry"/> with the specified <paramref name="typeLocator"/> and <paramref name="serviceProvider"/>.
        /// </summary>
        /// <param name="aggregateStore">The <see cref="Aggregate"/> store.</param>
        /// <param name="typeLocator">The type locator use to retrieve all known <see cref="Aggregate"/> types.</param>
        /// <param name="serviceProvider">The service locator used to retrieve singleton command handler dependencies.</param>
        public CommandHandlerRegistry(IStoreAggregates aggregateStore, ILocateTypes typeLocator, IServiceProvider serviceProvider)
        {
            Verify.NotNull(typeLocator, nameof(typeLocator));
            Verify.NotNull(serviceProvider, nameof(serviceProvider));

            knownCommandHandlers = DiscoverCommandHandlers(aggregateStore, typeLocator, serviceProvider);
        }
        /// <summary>
        /// Initializes a new instance of <see cref="CachedAggregateStore"/> using the specified <paramref name="slidingExpiration"/>.
        /// </summary>
        /// <param name="aggregateStore">The underlying <see cref="IStoreAggregates"/> implementation to be decorated.</param>
        /// <param name="slidingExpiration">The maximum time an <see cref="Aggregate"/> may existing in the cache without being accessed.</param>
        /// <param name="memoryCache">The underlying cache implementation.</param>
        internal CachedAggregateStore(IStoreAggregates aggregateStore, TimeSpan slidingExpiration, MemoryCache memoryCache)
        {
            Verify.NotNull(memoryCache, nameof(memoryCache));
            Verify.NotNull(aggregateStore, nameof(aggregateStore));
            Verify.GreaterThanOrEqual(TimeSpan.FromSeconds(1), slidingExpiration, nameof(aggregateStore));

            this.memoryCache = memoryCache;
            this.aggregateStore = aggregateStore;
            this.slidingExpiration = slidingExpiration;
        }
예제 #6
0
        /// <summary>
        /// Initializes a new instance of <see cref="HookableAggregateStore"/> with <paramref name="pipelineHooks"/> safe to enumerate multiple times.
        /// </summary>
        /// <param name="aggregateStore">The underlying <see cref="IStoreAggregates"/> implementation to be decorated.</param>
        /// <param name="pipelineHooks">The set of zero or more <see cref="PipelineHook"/> implementations used to extend <see cref="IStoreAggregates"/> behavior.</param>
        private HookableAggregateStore(IStoreAggregates aggregateStore, IList <PipelineHook> pipelineHooks)
        {
            Verify.NotNull(aggregateStore, nameof(aggregateStore));

            this.aggregateStore = aggregateStore;
            this.preGetHooks    = pipelineHooks.Where(pipelineHook => pipelineHook.ImplementsPreGet).ToArray();
            this.postGetHooks   = pipelineHooks.Where(pipelineHook => pipelineHook.ImplementsPostGet).Reverse().ToArray();
            this.preSaveHooks   = pipelineHooks.Where(pipelineHook => pipelineHook.ImplementsPreSave).ToArray();
            this.postSaveHooks  = pipelineHooks.Where(pipelineHook => pipelineHook.ImplementsPostSave).Reverse().ToArray();
        }
        /// <summary>
        /// Initializes a new instance of <see cref="HookableAggregateStore"/> with <paramref name="pipelineHooks"/> safe to enumerate multiple times.
        /// </summary>
        /// <param name="aggregateStore">The underlying <see cref="IStoreAggregates"/> implementation to be decorated.</param>
        /// <param name="pipelineHooks">The set of zero or more <see cref="PipelineHook"/> implementations used to extend <see cref="IStoreAggregates"/> behavior.</param>
        private HookableAggregateStore(IStoreAggregates aggregateStore, IList<PipelineHook> pipelineHooks)
        {
            Verify.NotNull(aggregateStore, nameof(aggregateStore));

            this.aggregateStore = aggregateStore;
            this.preGetHooks = pipelineHooks.Where(pipelineHook => pipelineHook.ImplementsPreGet).ToArray();
            this.postGetHooks = pipelineHooks.Where(pipelineHook => pipelineHook.ImplementsPostGet).Reverse().ToArray();
            this.preSaveHooks = pipelineHooks.Where(pipelineHook => pipelineHook.ImplementsPreSave).ToArray();
            this.postSaveHooks = pipelineHooks.Where(pipelineHook => pipelineHook.ImplementsPostSave).Reverse().ToArray();
        }
        /// <summary>
        /// Initializes a new instance of <see cref="CachedAggregateStore"/> using the specified <paramref name="slidingExpiration"/>.
        /// </summary>
        /// <param name="aggregateStore">The underlying <see cref="IStoreAggregates"/> implementation to be decorated.</param>
        /// <param name="slidingExpiration">The maximum time an <see cref="Aggregate"/> may existing in the cache without being accessed.</param>
        /// <param name="memoryCache">The underlying cache implementation.</param>
        internal CachedAggregateStore(IStoreAggregates aggregateStore, TimeSpan slidingExpiration, MemoryCache memoryCache)
        {
            Verify.NotNull(memoryCache, nameof(memoryCache));
            Verify.NotNull(aggregateStore, nameof(aggregateStore));
            Verify.GreaterThanOrEqual(TimeSpan.FromSeconds(1), slidingExpiration, nameof(aggregateStore));

            this.memoryCache       = memoryCache;
            this.aggregateStore    = aggregateStore;
            this.slidingExpiration = slidingExpiration;
        }
예제 #9
0
        /// <summary>
        /// Initializes a newly created aggregate instance.
        /// </summary>
        /// <typeparam name="TAggregate">The type of aggregate to initialize.</typeparam>
        /// <param name="aggregateStore">The aggregate repository to which the aggregate is to be saved.</param>
        /// <param name="aggregate">The newly created aggregate.</param>
        /// <param name="initializer">The aggregate initializer.</param>
        private static TAggregate Create <TAggregate>(this IStoreAggregates aggregateStore, TAggregate aggregate, Action <TAggregate> initializer)
            where TAggregate : Aggregate
        {
            var context = CommandContext.GetCurrent();

            using (var createContext = new CommandContext(aggregate.Id, context.Headers, new CommandEnvelope(aggregate.Id, context.Command)))
            {
                initializer.Invoke(aggregate);

                return((TAggregate)aggregateStore.Save(aggregate, createContext).Aggregate);
            }
        }
예제 #10
0
        /// <summary>
        /// Initializes a new instance of <see cref="CommandHandler"/>.
        /// </summary>
        /// <param name="aggregateType">The aggregate type.</param>
        /// <param name="commandType">The command type.</param>
        /// <param name="aggregateStore">The aggregate store.</param>
        /// <param name="executor">The command handler executor.</param>
        public CommandHandler(Type aggregateType, Type commandType, IStoreAggregates aggregateStore, Action<Aggregate, Command> executor)
        {
            Verify.NotNull(executor, nameof(executor));
            Verify.NotNull(commandType, nameof(commandType));
            Verify.NotNull(aggregateType, nameof(aggregateType));
            Verify.NotNull(aggregateStore, nameof(aggregateStore));
            Verify.TypeDerivesFrom(typeof(Command), commandType, nameof(commandType));
            Verify.TypeDerivesFrom(typeof(Aggregate), aggregateType, nameof(aggregateType));

            this.aggregateStore = aggregateStore;
            this.aggregateType = aggregateType;
            this.commandType = commandType;
            this.executor = executor;
        }
예제 #11
0
        /// <summary>
        /// Initializes a new instance of <see cref="CommandHandler"/>.
        /// </summary>
        /// <param name="aggregateType">The aggregate type.</param>
        /// <param name="commandType">The command type.</param>
        /// <param name="aggregateStore">The aggregate store.</param>
        /// <param name="executor">The command handler executor.</param>
        public CommandHandler(Type aggregateType, Type commandType, IStoreAggregates aggregateStore, Action <Aggregate, Command> executor)
        {
            Verify.NotNull(executor, nameof(executor));
            Verify.NotNull(commandType, nameof(commandType));
            Verify.NotNull(aggregateType, nameof(aggregateType));
            Verify.NotNull(aggregateStore, nameof(aggregateStore));
            Verify.TypeDerivesFrom(typeof(Command), commandType, nameof(commandType));
            Verify.TypeDerivesFrom(typeof(Aggregate), aggregateType, nameof(aggregateType));

            this.aggregateStore = aggregateStore;
            this.aggregateType  = aggregateType;
            this.commandType    = commandType;
            this.executor       = executor;
        }
예제 #12
0
        /// <summary>
        /// Gets or creates an aggregate of type <typeparamref name="TAggregate"/> identified by <paramref name="id"/>.
        /// </summary>
        /// <typeparam name="TAggregate">The type of aggregate to retrieve or create.</typeparam>
        /// <param name="aggregateStore">The aggregate store to extend.</param>
        /// <param name="id">The unique aggregate id.</param>
        /// <param name="initializer">The aggregate initializer.</param>
        public static TAggregate GetOrCreate <TAggregate>(this IStoreAggregates aggregateStore, Guid id, Action <TAggregate> initializer)
            where TAggregate : Aggregate
        {
            Verify.NotNull(aggregateStore, nameof(aggregateStore));
            Verify.NotNull(initializer, nameof(initializer));

            var aggregate = aggregateStore.Get <TAggregate>(id);

            if (aggregate.Version > 0)
            {
                return(aggregate);
            }

            return(aggregateStore.Create(aggregate, initializer));
        }
예제 #13
0
        /// <summary>
        /// Creates an aggregate of type <typeparamref name="TAggregate"/> identified by <paramref name="id"/> if does not already exist; otherwise throws an <see cref="InvalidOperationException"/>.
        /// </summary>
        /// <typeparam name="TAggregate">The type of aggregate to retrieve.</typeparam>
        /// <param name="aggregateStore">The aggregate repository from which the aggregate is to be retrieved.</param>
        /// <param name="id">The unique aggregate id.</param>
        /// <param name="initializer">The aggregate initializer.</param>
        public static TAggregate Create <TAggregate>(this IStoreAggregates aggregateStore, Guid id, Action <TAggregate> initializer)
            where TAggregate : Aggregate
        {
            Verify.NotNull(aggregateStore, nameof(aggregateStore));
            Verify.NotNull(initializer, nameof(initializer));

            var aggregate = aggregateStore.Get <TAggregate>(id);

            if (aggregate.Version > 0)
            {
                throw new InvalidOperationException(Exceptions.AggregateAlreadyExists.FormatWith(typeof(TAggregate), id));
            }

            return(aggregateStore.Create(aggregate, initializer));
        }
예제 #14
0
 public CustomerCommandsHandler(IStoreAggregates aggregateEventStore)
 {
     _aggregateEventStore = aggregateEventStore;
     _aggregateEventStore.Publish += OnEventPublished ;
 }
 /// <summary>
 /// Initializes a new instance of <see cref="CachedAggregateStore"/>.
 /// </summary>
 /// <param name="aggregateStore">The underlying <see cref="IStoreAggregates"/> implementation to be decorated.</param>
 public CachedAggregateStore(IStoreAggregates aggregateStore)
     : this(aggregateStore, Settings.AggregateStore.CacheSlidingExpiration, new MemoryCache("AggregateCache"))
 {
 }
예제 #16
0
 public CustomerCommandsHandler(IStoreAggregates aggregateEventStore)
 {
     _aggregateEventStore          = aggregateEventStore;
     _aggregateEventStore.Publish += OnEventPublished;
 }
예제 #17
0
 /// <summary>
 /// Initializes a new instance of <see cref="CachedAggregateStore"/>.
 /// </summary>
 /// <param name="aggregateStore">The underlying <see cref="IStoreAggregates"/> implementation to be decorated.</param>
 public CachedAggregateStore(IStoreAggregates aggregateStore)
     : this(aggregateStore, Settings.AggregateStore.CacheSlidingExpiration, new MemoryCache("AggregateCache"))
 {
 }
 /// <summary>
 /// Initializes a new instance of <see cref="HookableAggregateStore"/>.
 /// </summary>
 /// <param name="aggregateStore">The underlying <see cref="IStoreAggregates"/> implementation to be decorated.</param>
 /// <param name="pipelineHooks">The set of zero or more <see cref="PipelineHook"/> implementations used to extend <see cref="IStoreAggregates"/> behavior.</param>
 public HookableAggregateStore(IStoreAggregates aggregateStore, IEnumerable<PipelineHook> pipelineHooks)
     : this(aggregateStore, pipelineHooks.EmptyIfNull().OrderBy(hook => hook.Order).ThenBy(hook => hook.GetType().FullName).ToList())
 {
 }
예제 #19
0
 /// <summary>
 /// Initializes a new instance of <see cref="HookableAggregateStore"/>.
 /// </summary>
 /// <param name="aggregateStore">The underlying <see cref="IStoreAggregates"/> implementation to be decorated.</param>
 /// <param name="pipelineHooks">The set of zero or more <see cref="PipelineHook"/> implementations used to extend <see cref="IStoreAggregates"/> behavior.</param>
 public HookableAggregateStore(IStoreAggregates aggregateStore, IEnumerable <PipelineHook> pipelineHooks)
     : this(aggregateStore, pipelineHooks.EmptyIfNull().OrderBy(hook => hook.Order).ThenBy(hook => hook.GetType().FullName).ToList())
 {
 }