CQRS学习——IOC,配置,仓储隔离以及QueryEntry[其三]

从IoC开始说起

博主最早开始用的IoC容器叫AutoFac,那时候用它主要是为了生命周期管理——将EF上下文的生命周期限定为每请求。当然也总是每每听到IoC的好处,但是仍然不能理解其优势。最近在学习和实现Cqrs的时候,却是有了明显的体会了。我们以前分层的时候,用的Service和Reponsitory。通常的情形是:Service需要访问一些数据,就得在Reponsitory中添加一些方法,否则无法完成。往往这是很困惑的,Service经常成了Reponsitory的简单的包装。同时,Service是依赖于Reponsitory的,仓储提供了什么,服务才能继续做什么。同时——模型是被仓储引用的,也就是模型不是和Service紧密关联的。我们也尝试过将仓储和服务提取为接口,然而并没有直观的改善——因为我们依赖关系没有变(仓储接口<-仓储<-服务接口<-服务)。学习CQRS的时候,曾经看过一些例子,模型是和业务紧密结合的。当时博主用自己的依赖关系代入进去发现行不通。细看才发现,仓储的接口是和服务定义在一起的(因为只有服务会/允许调用这个)。仓储的实现引用了服务,实现了仓储接口。这个时候,情形变为了:服务去要求仓储实现这些方法。依赖关系为:(模型<-仓储接口<-服务)<-仓储实现。这里引出的问题是,服务本身是不清楚具体实现是哪个的,需要调用方来指定,IoC容器就起了这个作用,通过注册,可以为特定的接口指派特定的实现。

时过境迁,由于总总原因,博主现在开始使用Unity作为容器了。

仓储与QueryEntry

就博主目前对CQRS的理解而言,实现CQRS的一个目标是从架构上将数据的查询和提交分离。所以引入了一个QueryEntry,查询入口。查询入口也需要仓储支持,且具有更多的灵活性,比如对于SQL而言,直接提交SQL查询以改善联查性能。为了实现好的隔离性,查询入口也通过接口定义。在目前的处理中,QueryEntry和Reponsitory的接口放在同一层(领域层),其实现放在同一层(Storage层)。这样,如果针对SQL,那么特殊的查询使用SQL语句完成,如果针对Oracle就使用Oracle的查询语句完成。而任何实现了这些接口的DLL都可以替换原有的DLL,领域层就不关心持久化的事情了。

其实也考虑过QueryEntry直接使用实现类来处理,但是这样的话,仓储就要暴露给应用程序层(web)。

配置

在整个解决方案中,博主使用一个Configuration项目来配置各个组件(实现)的关系,这好比是前面博文中的Configuration类的放大。为了独立管理配置,所以引入了一个扩展类,将Unity的各种配置按照语义具体化。

    public class ComponentContainer
    {
        protected UnityContainer Container = new UnityContainer();

        public UnityContainer GetContainer()
        {
            return Container;
        }

        //public static ComponentContainer Config()
        //{
        //    return new ComponentContainer();
        //}

        public T Construct<T>()
        {
            return Container.Resolve<T>();
        }

        public T Construct<T>(Type registedType)
        {
            return (T) Container.Resolve(registedType);
        }

        public void Use<T, TI>(LifetimeManager manager = null) where TI : T
        {
            if (manager == null)
            {
                Container.RegisterType<T, TI>();
            }
            else
            {
                Container.RegisterType<T, TI>(manager);
            }
        }

        public void Use<T>(Type iType, LifetimeManager manager = null)
        {
            if (manager == null)
            {
                Container.RegisterType(typeof (T), iType);
            }
            else
            {
                Container.RegisterType(typeof(T), iType, manager);    
            }
        }

        public void Use(Type type, Type iType, LifetimeManager manager = null)
        {
            if (manager == null)
            {
                Container.RegisterType(type, iType);
            }
            else
            {
                Container.RegisterType(type, iType, manager);
            }
        }

        public void UseInstance<T, TI>() where TI : T, new()
        {
            var instance = new TI();
            Container.RegisterInstance(typeof (T), instance);
        }

        public void SpecifyLifeTime<T>(LifetimeManager manager = null)
        {
            SpecifyLifeTime(typeof (T), manager);
        }

        public void SpecifyLifeTime(Type type, LifetimeManager manager = null)
        {
            if (manager == null)
            {
                Container.RegisterType(type);
            }
            else
            {
                Container.RegisterType(type);
            }
        }
    }
ComponentContainer

总的来数,Unity会被解决方案中的所有项目引用。而在所有的组件中,有以下几个需要通过IoC配置:ICommandBus(单例),IEventBus(单例),仓储,QueryEntry(查询入口)...,在CQRS层的Configuration名称空间下添加以下接口表示配置支持:

 public interface ICommandHandlerRegister
    {
        void RegisterCommandHandlers(ComponentContainer container);
    }

 public interface IEventHandlerRegister
    {
        void RegisterEventHandlers(ComponentContainer container);
    }

public interface IQueryEntryRegister
    {
        void RegisterQueryEntries(ComponentContainer container);
    }

 public interface IStorageRegister
    {
        void RegisterReponsitories(ComponentContainer container);
        /*在这里完成工作单元的映射*/
        void RegisterUnitOfWork(ComponentContainer container);
    }
View Code
/*为流畅接口定义的扩展,配置语义化*/

    public static class FluentConfiguration
    {
        public static CqrsConfigurationResolver ConfigLifetimeManager<T>(this CqrsConfigurationResolver resolver, LifetimeManager manager)
        {
            if (resolver == null)
                throw new ArgumentNullException("resolver");
            resolver.SpecifyLifeTime(typeof (T), manager);
            return resolver;
        }

        public static CqrsConfigurationResolver UseEventBus<T>(this CqrsConfigurationResolver resolver) where T : IEventBus
        {
            if (resolver == null)
                throw new ArgumentNullException("resolver");
            /*生命周期与容器的生命周期保持一致,故而实现了单例*/
            resolver.Use<IEventBus, T>(new ContainerControlledLifetimeManager());
            return resolver;
        }

        public static CqrsConfigurationResolver UseCommandBus<T>(this CqrsConfigurationResolver resolver) where T : ICommandBus
        {
            if (resolver == null)
                throw new ArgumentNullException("resolver");
            resolver.Use<ICommandBus, T>(new ContainerControlledLifetimeManager());
            return resolver;
        }

        public static CqrsConfigurationResolver UseEventHandlerProvider<T>(this CqrsConfigurationResolver resolver)
            where T : IEventHandlerProvider
        {
            if (resolver == null)
                throw new ArgumentNullException("resolver");
            resolver.Use<IEventHandlerProvider, T>(new ContainerControlledLifetimeManager());
            return resolver;
        }

        public static CqrsConfigurationResolver UseCommandHandlerProvider<T>(this CqrsConfigurationResolver resolver)
            where T : ICommandHandlerProvider
        {
            if (resolver == null)
                throw new ArgumentNullException("resolver");
            resolver.Use<ICommandHandlerProvider, T>(new ContainerControlledLifetimeManager());
            return resolver;
        }

        public static CqrsConfigurationResolver UseStorageRegister<T>(this CqrsConfigurationResolver resolver)
            where T : IStorageRegister, new()
        {
            if (resolver == null)
                throw new ArgumentNullException("resolver");
            var register = new T();
            register.RegisterReponsitories(CqrsConfigurationResolver.Config);
            register.RegisterUnitOfWork(CqrsConfigurationResolver.Config);
            return resolver;
        }

        public static CqrsConfigurationResolver UseCommandHandlerRegister<T>(this CqrsConfigurationResolver resolver)
            where T : ICommandHandlerRegister, new()
        {
            if (resolver == null)
                throw new ArgumentNullException("resolver");
            var register = new T();
            register.RegisterCommandHandlers(CqrsConfigurationResolver.Config);
            return resolver;
        }

        public static CqrsConfigurationResolver UseEventHandlerRegister<T>(this CqrsConfigurationResolver resolver)
            where T : IEventHandlerRegister, new()
        {
            if (resolver == null)
                throw new ArgumentNullException("resolver");
            var register = new T();
            register.RegisterEventHandlers(CqrsConfigurationResolver.Config);
            return resolver;
        }

        public static CqrsConfigurationResolver UseQueryEntryRegister<T>(this CqrsConfigurationResolver resolver)
            where T : IQueryEntryRegister, new()
        {
            if (resolver == null)
                throw new ArgumentNullException("resolver");
            var register = new T();
            register.RegisterQueryEntries(CqrsConfigurationResolver.Config);
            return resolver;
        }

        public static CqrsConfigurationResolver UseAuditingStore<T>(this CqrsConfigurationResolver resolver)
            where T : IAuditingStore, new()
        {
            if (resolver == null)
                throw new ArgumentNullException("resolver");
            resolver.Use<IAuditingStore,T>();
            return resolver;
        }

        public static CqrsConfigurationResolver UseAuditingConfiguration<T>(this CqrsConfigurationResolver resolver)
            where T : IAuditingConfiguration, new()
        {
            if (resolver == null)
                throw new ArgumentNullException("resolver");
            resolver.Use<IAuditingConfiguration, T>();
            return resolver;
        }

        public static CqrsConfigurationResolver UseUnitOfWork<T>(this CqrsConfigurationResolver resolver)
            where T : IUnitOfWork
        {
            if (resolver == null)
                throw new ArgumentNullException("resolver");
            resolver.Use<IUnitOfWork, T>(new PerThreadLifetimeManager());
            return resolver;
        }

        public static CqrsConfigurationResolver UseSession<T>(this CqrsConfigurationResolver resolver, LifetimeManager manager)
            where T : IDpfbSession
        {
            if (resolver == null)
                throw new ArgumentNullException("resolver");
            resolver.Use<IDpfbSession, T>(manager);
            return resolver;
        }

        public static CqrsConfigurationResolver UseAuditInfoProvider<T>(this CqrsConfigurationResolver resolver)
            where T : IAuditInfoProvider
        {
            if (resolver == null)
                throw new ArgumentNullException("resolver");
            resolver.Use<IAuditInfoProvider, T>();
            return resolver;
        }
    }
View Code

这里是一个调用的例子:

public static class ConfigurationInitializer
    {
        public static void Initial()
        {
            CqrsConfigurationResolver.Config
                .UseCommandBus<DpfbCommandBus>()
                .UseEventBus<DpfbEventBus>()
                .UseCommandHandlerProvider<InterfaceBasedCommandHandlerProvider>()
                .UseEventHandlerProvider<InterfaceBasedEventHandlerProvider>()
                //.UseStorageRegister<FakeStorageRegister>()
                .UseStorageRegister<EntityFrameworkStorageRegister>()
                .UseQueryEntryRegister<QueryEntryRegister>()
                .UseCommandHandlerRegister<DefaultCommandHandlerRegister>()
                .UseEventHandlerRegister<DefaultEventHandlerRegister>()
                .UseAuditingStore<MemoryAuditingStore>()
                .UseAuditInfoProvider<NullAuditInfoProvider>()
                .UseSession<HttpDpfbSession>(new PerHttpRequestOrThreadLifetimeManager())
                .UseAuditingConfiguration<EnabledAuditingConfiguration>();
        }
    }
View Code

...

【想到什么再补充】

原文地址:https://www.cnblogs.com/lightluomeng/p/4774175.html