业务建模:BoundedContext(有界上下文)

12-10-11 clonalman
有界上下文BoundedContext在DDD是一个拓展的概念,在Evans经典DDD的书里应该是没有这个概念的,Jdon论坛上面的很少涉及BoundedContext文章。

就BoundedContext的设计上,提出几个自己的在这方面看法,抛砖引玉。

1、BoundedContext不同于Role(DCI)

它们看起来有点相似,但BoundedContext的描述语意会更加广泛一点。Role(DCI)根据角色直接捕捉角色与系统交互行为,更接近于业务需求,BoundedContext更强调则业务分析避免歧义.

比如,Security模块的User,Hr模块的Employee,Project模块的Member等,当涉及“系统使用者”的时候,系统管理员会认为说的是系统用户,行政总监会认为说的是员工,项目经理会说的是项目成员,这时候Role与BoundedContext就很类似了,除了类似Role这样的概念,BoundedContext还可以其他业务概念的歧义问题,如“Account”他可能是财务的概念也可能是其他业务的概念。

2、BoundedContext与Repository

领域内的Entity都应该置于一定的上下文语境下面,而Entity的重建在Repository内完成的,所以Repository对BoundedContext存在一定的依赖关系

3、BoundedContext与Domain Service

业务之间或业务模块之间存在一定的交互关系,而这种交互关系最终体现为BoundedContext之间的交互关系,

业务的交互可以领域服务(Domain Service)来实现。

一个系统,当从数据的角度考察的时候,表现为一系列查询和命令,当从业务的角度考察的时候,表现为一系列业务行为,

这个视角的不同最终体现为不同的Context之间的差异,其交互通过QueryService、CommandService、UnitOfWorkService来实现。

4、BoundedContext可做为CommandHandler与EventHandler的实现。

架构CQRS有太多概念很容易模糊掉关注领域核心的视线,所以才会把很多架构里元素放到BoundedContext中。

[该贴被clonalman于2012-10-11 23:33修改过]

         

7
clonalman
2012-10-12 08:01
BoundedContext的交互通过Domain Service来实现,BoundedContext只需要承载一个相关的Ioc容器,

Ioc Container 实现(C#):

public interface IDependencyInjectionContainer: IServiceProvider
    {
        void Register<TService>(string key, Func<TService> factory);
        void Register<TService, TResult>(string key, Func<TService, TResult> factory);
        void Register<TService>(Func<TService> factory);
        void Register<TService>(Func<IDependencyInjectionContainer, TService> factory);
        void Unregister(object instance);
        TService Resolve<TService>();
        TService Resolve<TService>(IDictionary arguments);
        TService Resolve<TService>(string key);
        TService Resolve<TService>(string key, IDictionary arguments);
        TService[] ResolveAll<TService>();
    }
<p>

internal class DependencyInjectionContainer : IDependencyInjectionContainer, IServiceProvider
    {
        private readonly IDictionary<string, object> factories = new Dictionary<string, object>();

        public void Register<TService>(Func<IDependencyInjectionContainer, TService> factory)
        {
            Register(() => factory(this));
        }

        public void Register<TService>(Func<TService> factory)
        {
            Register(typeof(TService).AssemblyQualifiedName, factory);
        }

        public void Register<TService, TResult>(string key, Func<TService, TResult> factory)
        {
            Register(key, () => factory(Resolve<TService>()));
        }

        public void Register<TService>(string key, Func<TService> factory)
        {
            factories[key] = factory;
        }

        public void Unregister(object instance)
        {
            if (factories.ContainsKey(instance.GetType().AssemblyQualifiedName))
            {
                factories.Remove(instance.GetType().AssemblyQualifiedName);
            }
            else
            {
                IDependencyInjectionContainer container = Resolve<IDependencyInjectionContainer>();
                if (container != null)
                {
                    container.Unregister(instance);
                }
            }
        }

        public TService Resolve<TService>()
        {
            object obj;
            if (factories.TryGetValue(typeof(TService).AssemblyQualifiedName, out obj))
            {
                return ((Func<TService>)obj)();
            }
            else
            {
                IDependencyInjectionContainer container = Resolve<IDependencyInjectionContainer>();
                if (container != null)
                {
                    return container.Resolve<TService>();
                }
            }
            return default(TService);
        }

        public TService Resolve<TService>(IDictionary arguments)
        {
            object obj;
            if (factories.TryGetValue(typeof(TService).AssemblyQualifiedName, out obj))
            {
                return ((Func<TService>)obj)();
            }
            else
            {
                IDependencyInjectionContainer container = Resolve<IDependencyInjectionContainer>();
                if (container != null)
                {
                    return container.Resolve<TService>(arguments);
                }
            }
            return default(TService);
        }

        public TService Resolve<TService>(string key)
        {
            object obj;
            if (factories.TryGetValue(key, out obj))
            {
                return ((Func<TService>)obj)();
            }
            else
            {
                IDependencyInjectionContainer container = Resolve<IDependencyInjectionContainer>();
                if (container != null)
                {
                    return container.Resolve<TService>(key);
                }
            }
            return default(TService);
        }

        public TService Resolve<TService>(string key, IDictionary arguments)
        {
            object obj;
            if (factories.TryGetValue(key, out obj))
            {
                return ((Func<TService>)obj)();
            }
            else
            {
                IDependencyInjectionContainer container = Resolve<IDependencyInjectionContainer>();
                if (container != null)
                {
                    return container.Resolve<TService>(key, arguments);
                }
            }
            return default(TService);
        }

        public TService[] ResolveAll<TService>()
        {
            List<TService> services = new List<TService>();
            foreach (Func<TService> obj in factories.Values.OfType<Func<TService>>())
            {
                services.Add(obj());
            }
            IDependencyInjectionContainer container = Resolve<IDependencyInjectionContainer>();
            if (container != null)
            {
                services.AddRange(container.ResolveAll<TService>());
            }
            return services.ToArray();
        }

        public object GetService(Type serviceType)
        {
            object obj;
            if (factories.TryGetValue(serviceType.AssemblyQualifiedName, out obj))
            {
                Delegate deleg = (Delegate)obj;
                if (deleg != null)
                {
                    return deleg.DynamicInvoke();
                }
            }
            else
            {
                IDependencyInjectionContainer container = Resolve<IDependencyInjectionContainer>();
                if (container != null)
                {
                    return container.GetService(serviceType);
                }
            }
            return null;
        }

    }
<p>

public static class DI
    {
        static DI()
        {
            Current = new DependencyInjectionContainer();
            RegisterCoreDependencies(Current);
        }

        public static IDependencyInjectionContainer Current
        {
            [MethodImpl(MethodImplOptions.Synchronized)]
            get;
            private set;
        }

        private static void RegisterCoreDependencies(IDependencyInjectionContainer di)
        {

        }
    }
<p>

除了具备简单容器功能,主要加载第三方容器如Spring或Windsor等

clonalman
2012-10-12 08:24
不同上下文(BoundedContext)

1、通用上下文BoundedContext,适用于任何情况,具体业务场景使用的基类

C#

    public class BoundedContext : MarshalByRefObject, IDisposable
    {
        protected BoundedContext(IDependencyInjectionContainer container) 
        {
            Guard.NotNullArgument(container, "container");
            Container = container;
        }

        public BoundedContext() :
            this(DI.Current)
        {
            
        }

        public IDependencyInjectionContainer Container
        {
            get;
            private set;
        }
        .....
}

<p>

2、系统只是一个CRUD系统或CQS系统的数据上下文,从通用上下文继承。

C#代码:

   public sealed class NHibernateContext : BoundedContext, IDisposable
    {
        IWindsorContainer m_Container;

        public NHibernateContext(IWindsorContainer container)
            : base(container.Resolve<IDependencyInjectionContainer>())
        {
            m_Container = container;
        }
       ....
     }
<p>

    public sealed class NHibernateQueryService : IQueryService
    {
        public NHibernateQueryService(NHibernateContext context)
        {
            this.Context = context;
        }

        public NHibernateContext Context
        {
            get;
            private set;
        }
       ....
     }
<p>

    public sealed class NHibernateCommandService: ICommandService
    {
        public NHibernateCommandService(NHibernateContext context)
        {
            this.Context = context;
        }

        public NHibernateContext Context
        {
            get;
            private set;
        }

        void ICommandService.Execute(ICommand command)
        {
            command.Execute(Context);
        }
    }
<p>

NHibernateContext, NHibernateCommandService, NHibernateQueryService 均在容器中

3、业务场景上下文:如InventoryContext

    public class InventoryContext: BoundedContext
    {
       public InventoryContext(){

       }
       ......
    }
<p>

Container属性可以轻松获取ICommandService与IQueryService,实现与NHibernateContext的交互;InventoryContext与NHibernateContext的交互主要实现数据的查询与持久化等功能

另外InventoryContext还可以于其他的业务系统的Context进行交互,由业务上的服务来实现。。

[该贴被clonalman于2012-10-12 08:59修改过]

clonalman
2012-10-12 08:34

我这些实现可能跟现有的很多设计思路很不一样,主要是试图对上面这个图

进行解构,让非领域的技术架构概念隐藏在DDD领域概念下,或者说是让架构的概念实现或演变为领域概念,以实现架构与领域的分离。

BoundedContext、IQueryService、ICommandService放在基础设施层,应用层有复杂的查询,可以直接越过领域层,直接访问基础设施层的IQueryService,领域层数据的访问不直接调用IQueryService、ICommandService,而是通过Repository进行封装,具体体现为领域对象的重建。

    public class RepositoryBase : IDisposable
    {

        public RepositoryBase() :
            this(new BoundedContext())
        {
            
        }

        protected RepositoryBase(BoundedContext context)
        {
            Guard.NotNullArgument(context, "context");
            Context = context;
        }

        protected BoundedContext Context
        {
            get;
            private set;
        }

        protected virtual void Dispose(bool disposing)
        {
            
        }

        void IDisposable.Dispose()
        {
            Dispose(true);
            GC.SuppressFinalize(this);
        }

        ~RepositoryBase()
        {
            Dispose(false);
        }
    }

<p>

[该贴被clonalman于2012-10-12 09:04修改过]

[该贴被admin于2012-10-12 14:45修改过]

[该贴被admin于2012-10-12 14:46修改过]

clonalman
2012-10-12 10:35
系统各Context之间的相互交互关系,构成了系统Context Map,Command可以沿这Context Map在不同Context之间传递

比如:添加一个产品Product,提交的AddCommand从ProductContext接受处理转移到NHibernateContext来持久化处理。

    /// <summary>
    /// The Command Pattern 
    /// </summary>
    public abstract class Command : ICommand, IDisposable
    {
        public Command()
        {
            
        }

        public void Execute(BoundedContext context)
        {
            try
            {
                OnExecute(context);
            }
            catch (Exception ex)
            {
                Exception = ex;
                throw ex;
            }
            
        }

        public Exception Exception
        {
            get;
            set;
        }

        protected abstract void OnExecute(BoundedContext context);

        protected virtual void Dispose(bool disposing)
        {
            
        }

        public void Dispose()
        {
            Dispose(true);
        }

        ~Command()
        {
            Dispose(false);
        }
    }
<p>

[该贴被clonalman于2012-10-12 10:55修改过]

[该贴被clonalman于2012-10-12 11:22修改过]

猜你喜欢
6Go 1 2 3 4 ... 6 下一页