有界上下文之间的通信

时间:2013-05-24 15:08:35

标签: winforms architecture domain-driven-design bounded-contexts

我有一个WinForms应用程序,我希望将重构为使用DDD架构。首先,我试图真正地围绕建筑本身,我有埃文斯的书,我有弗农的书,我发现自己正在努力应对我将面临的三个场景。我担心在概念设计过程中我可能会过度思考或过于严格。

1。)利用DDD的Pluralsight教程中提供的示例,发言者指出不同的有界上下文应该由他们自己的解决方案来表示。但是,如果我有一个不面向服务的winforms应用程序(这最终会改变并且很多问题变得毫无意义)这似乎不可行。因此,我假设我将把它们分成不同的项目/命名空间,保持警惕没有相互依赖性。这是考虑它的正确方法还是我错过了一些明显的东西?

2。)我有一个导航UI,可以启动其他模块/窗口,这些模块/窗口属于不同有界上下文的单独表示层。想想在启动ERP应用程序时打开的第一个窗口。由于这不适合任何特定的BC,如何正确实现这样的事情。这应该属于共享内核吗?

3.)我有一个工作管理有限的背景和评级/成本计算有界的背景。创建作业时,业务流程的一部分是对其详细信息进行评级。这有自己的UI等,我觉得这个演示文稿仍然充分属于作业管理环境。但是,这些细节的实际评级过程绝对不应该。我不完全确定如何与评级/成本核算相关联,因为bc要彼此分开。我意识到我可以做消息传递,但对于非分布式应用来说,这似乎有些过分。每个BC都可以自己托管某种API,但这似乎有点过分,尽管这会让团队很好地在以后迁移到分布式架构。最后,我的最后一个想法是拥有某种共享依赖,这是一种各种事件存储。我不知道这是否与域事件相同,因为它们似乎有一个单独的关注。那么,这是否意味着这将属于共享内核或其他类型的解决方案?

提前谢谢。

3 个答案:

答案 0 :(得分:6)

1)关于与解决方案相对应的BC的指导只是指导,而不是硬性规则。但是,它确实提供了非常需要的隔离。您仍然可以使用WinForms项目。例如,假设您有一个名为Customers的BC。为它创建一个解决方案,并在其中创建一个名为Customers.Contracts的附加项目。该项目有效地容纳了BC的公共合同,其中包括DTO,命令和事件。外部BC应该只能使用此合同项目中定义的消息与Customers BC进行通信。让WinForms解决方案引用Customers.Contracts而不是Customers项目。

2)UI通常起着编写角色的作用,编排许多BC - 复合UI。一个典型的例子是亚马逊产品页面。需要来自不同BC的数百个服务来呈现页面。

3)同样,这似乎是一个需要复合UI的场景。表示层可以在不同的BC之间进行调解。 BC松散耦合,但BC之间仍然存在关系。有些是下游,有些是上游,甚至是两者。每个都有一个反腐败层,一个端口,与相关的BC集成。

答案 1 :(得分:3)

首先,当我看到你谈论消息总线时,我认为我们首先需要讨论BC集成。

您不需要消息总线在BC之间进行通信;这里是关于我如何整合不同BC的解释:

我在每个BC上公开一些公共接口(类似于域命令, - 查询和 - 事件),并在我的基础结构中有一个中间层,用于将此调用转换为另一个BC。

以下是BC中公开命令的示例界面:

public interface IHandleCommands
{
    void DoSomething(Guid SomeId,string SomeName);
}

我也有类似的曝光事件

public interface IPublishEvents 
{
   void SomethingHappened(Guid SomeId,string SomeName);
}

最后,对于我公开的数据(即CQ(R)S中的查询),我有另一个界面,请注意,这允许您在任何给定时间删除域模型和查询代码之间的耦合。

public interface IQueryState
{
    IEnumerable<SomeData> ActiveData(DateTime From=DateTime.Minvalue, ... );
}

我的实现看起来像这样:

public class SomeAR:IHandleCommands
{
    IPublishEvents Bus;

    public SomeAr(IPublishEvents Bus) 
    {
       this.Bus = Bus;
    }

    public void DoSomething(Guid x,string y)
    {
       Bus.SomethingHappened(SomeId: x,SomeName: y);
    }
}

毕竟,当你想到它时:域事件之类的事情也可以在没有消息传递的情况下完成;只需通过接口成员替换消息类,并通过注入BC的接口实现替换处理程序。

然后这些处理程序在其他BC上调用命令;它们就像将不同的BC绑定在一起的胶水(想想工作流/无状态传奇等)。

这可能是一个示例处理程序:

public class WorkFlow : IPublishEvents
{
  public void SomethingHappened(Guid SomeId,string SomeName) 
  {
     AnotherBC.DoSomething(SomeId,SomeName);
  }
}

这是一个简单的方法,不需要付出很多努力,我已经非常成功地使用了它。如果你想稍后切换到成熟的消息,那应该很容易做到。

回答有关用户界面的问题:

我认为你对此过于刻板。

只要我的域(或可以很容易地)与您的UI分离,您​​就可以轻松地从单个UI项目开始,然后在您开始遇到痛苦的那一刻将其拆分。但是,如果拆分代码,则应按BC划分,以便项目结构匹配。

我发现以这种方式构建UI对我来说是最有效的方式......

答案 2 :(得分:3)

我从这些问题中获得的感觉可以概括为:“从代码工件的角度来看,BC边界的理解方法是什么?我如何构建一个既可以查询又可以命令几个BC的UI?”。这取决于......

另一种尚未提及的方法可能是将UI视为单独的上下文。我怀疑这是一个非常受欢迎的POV,但它有时很有用。 UI可以使用例如UI来指示它需要什么。它自己的接口和数据结构,并让每个BC实现适当的接口(进行内部转换)。缺点是额外的翻译正在进行,但是当有足够的价值被收获时,它才有意义。值是在UI方面保持简单,而不必担心数据的来源和位置以及变化如何影响每个BC。这一切都可以在简单的外观背后处理。这个门面可以坐在几个地方(在客户端或服务器上)。不要被愚弄,“复杂性”刚刚落后于另一层。协调和努力工作仍然需要完成。

或者,您可能还想查看我称之为“对齐”的UI与BC公开的用例。正如Tom所提到的,工作流或传奇实现可能会在需要协调时将其拉下来。质疑一致性要求(这个其他BC何时需要了解给定的信息?)可能会为BC如何互操作带来新的见解。你看,UI是一个非常有用的反馈循环。当它与BC的用例不一致时,可能是用例有问题,或者在UI中如何设计它可能有问题,或者我们只是发现了一个不同的用例。这就是UI模型为讨论做出如此棒的工具的原因。它们提供了相同问题/解决方案的EXTRA视图。额外的“这不是您应该在与域专家的对话中使用的唯一可视化”。用户体验要求也是要求。他们应该照顾。

我个人发现,当我讨论UI时,我戴着另一顶帽子,而不是在讨论纯粹的功能时(你知道,那些不需要UI来解释应用程序在做什么/应该做什么的事情) 。我可能会在同一个对话中切换帽子,以找出不对中的情况。