在模块化应用程序架构中共享类型

时间:2011-09-28 18:50:55

标签: architecture modularity n-tier-architecture

我正在开发一个遵循模块化应用程序设计的应用程序。有一个“核心”垂直堆栈,提供通用的中央服务,如身份验证/授权,日志记录,数据访问等。特定于上下文的区域在模块中实现,这些模块引用同一逻辑层中的核心程序集。

该应用程序是一个服务应用程序,具有外观层,域层和包含数据访问代码的基础设施代码。每个模块都将实现域层组件。如果模块具有公共API,它还将实现外观程序集。

例如,解决方案包含:

  • Core.Api - 核心API的Facade程序集
  • 核心 - 核心服务,实体等的域组装
  • Core.Data - 核心数据访问组件
  • ModuleA.Api - 模块A的外观装配(参考Core.Api)
  • ModuleA - 模块A的域组装(参考核心)
  • ModuleB.Api - 模块B的外观装配(参考Core.Api)
  • ModuleB - 模块B的域组装(引用Core)
  • enter image description here

    (依此类推。完成后我们将有大约24个模块。)

    使用DI和Unity作为IoC容器将所有这些绑定在一起。 WebHost项目定义了各种实现映射到它们满足的接口的配置。

    这种方法不仅仅是出于逻辑目的,而是为了让我们在实现方面具有灵活性(即我们可以在不破坏甚至触及其他代码的情况下更改模块的实现)。它还可以非常轻松地跨团队划分工作,并使用新模块扩展应用程序,而其他代码中几乎没有回归错误的风险。

    我的问题是当ModuleB需要ModuleA中定义的一种或多种类型时该怎么办? ModuleB可以引用ModuleA还是我需要将ModuleA移动到Core?

    更新

    在考虑实施下面讨论的一些变化时,我遇到了一个让我得到澄清的问题......

    我的模块有两个相互依赖的地方。按照下面讨论的方法,我可以将我希望从Module1共享的接口和类型移动到Core,这允许ModuleB在不紧密耦合ModuleA和ModuleB的情况下使用它们。但是,我还需要使用ModuleA.Api中定义的一些数据协定来进行ModuleB.Api中的服务操作。这让我质疑设计。

    由于IP,我无法描述我们的确切用例,但足以说ModuleA有一个EntityA域对象。 ModuleA.Api定义公共API公开的EntityContractA数据协定(DTO)。 ModuleB.Api有一个方法,需要EntityContractA作为参数,然后委托给ModuleB中接受EntityA作为参数的方法。

    同样,将EntityA移动到Core中可以解决域层问题,但对Facade层没有帮助。将EntityContractA移动到Core.Api会让整个东西闻起来有点气味。

    这是我需要完成的一个例子:

    在ModuleB.Api.ServiceFacadeB中:

    public EntityContractB FilterBy(EntityContractA contractA)
    {
        var entityA = Mapper.Map<EntityContractA, EntityA>(contractA);
    
        var entityB = domainService.FilterBy(entityA);
    
        var contractB = Mapper.Map<EntityB, EntityContractB>(entityB);
    
        return contractB;
    }
    

    其中domainService是ModuleA.ServiceB:

    public EntityB FilterBy(EntityA entityA)
    {
        // Do work
    }
    

    3 个答案:

    答案 0 :(得分:0)

    Module2需要引用Module1。模块之间的相互依赖性并不能将部件移动到通用核心代码。

    答案 1 :(得分:0)

    好消息:你所描述的实际上是一个很好的架构,清晰地分离了关注点和模块化模式。它允许您的团队开发彼此独立的模块,并且只依赖于彼此的接口(facade / API)。这是最好的做法!

    (潜在的)坏消息:如果你从Module2直接引用Module1开始,你将一手打破这个美丽的架构。创建Module1的团队只需关心保持其接口稳定,但由于直接引用,现在还需要小心改变它们的实现。

    我的建议:如果你需要Module2中现在在Module1中定义的类型,请确保将其移动到Core API或Module1 API类中。这就形成了这样一个事实,即你的装配依赖于这些类型,并明确表明它们需要保持稳定。

    答案 2 :(得分:0)

      

    ModuleB可以引用ModuleA还是我需要移动   ModuleA进入Core?

    不,我会在某个与特定模块无关的地方重新定义共享类型。只需确保无论您在何处定义这些共享类型都没有依赖关系。

    [警告,自我推销:]在5-Layer Architecture世界观中,他们会进入“公共”层。