业务建模: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修改过]
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等
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修改过]
我这些实现可能跟现有的很多设计思路很不一样,主要是试图对上面这个图
进行解构,让非领域的技术架构概念隐藏在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修改过]
比如:添加一个产品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修改过]
猜你喜欢