4

我有一个 WinForms 应用程序,我希望将其重构为使用 DDD 架构。首先,我试图真正围绕架构本身进行思考,我有 Evans 的书和 Vernon 的书,我发现自己在努力应对我在应用程序中会立即面临的三个场景。我担心我在概念设计过程中可能会过度思考或过于严格。

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

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

3.) 我有一个 Job Management 有界上下文和一个 Rating/Costing 有界上下文。这是业务流程的一部分,当创建作业时,它的详细信息会被评级。它有自己的 UI 等,我觉得这个演示文稿仍然充分地落在作业管理上下文中。但是,这些细节的实际评级过程绝对不应该。我不完全确定如何与评级/成本上下文进行沟通,因为 bc 将彼此分开。我意识到我可以发送消息,但这对于非分布式应用程序来说似乎有点过头了。每个 BC 都可以自行托管某种 API 是可行的,但这似乎有点过头了,尽管这可以很好地为团队稍后迁移到分布式架构做好准备。最后,我的最后一个想法是拥有某种共享依赖关系,它是一种事件存储。我不知道这是否与领域事件相同,因为这些事件本身似乎有一个单独的关注点。那么,这是否意味着这也属于共享内核或其他类型的解决方案?

先感谢您。

4

3 回答 3

6

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

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

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

于 2013-05-24T20:51:54.483 回答
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 分离,您就可以轻松地从一个 UI 项目开始,然后在您开始在某个地方遇到痛苦的那一刻将其拆分。但是,如果您拆分代码,则应按 BC 拆分它,以便项目结构匹配。

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

于 2013-05-25T07:59:27.417 回答
3

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

另一种尚未提及的方法可能是将 UI 视为单独的上下文。我怀疑这是一个非常流行的 POV,但它有时会很有用。UI 可以使用它自己的接口和数据结构来规定它需要什么,并让每个 BC 实现适当的接口(进行内部翻译)。缺点是额外的翻译正在进行,但同样只有当有足够的价值可以收获时才有意义。其价值在于在 UI 方面保持简单,而不必担心数据的来源和来源或更改如何影响每个 BC。这一切都可以在一个简单的外观后面处理。这个外观可以放在几个地方(在客户端或服务器上)。不过不要上当,“复杂性”刚刚移到另一层后面。

或者,您可能还想研究我所说的 UI 与 BC 公开的用例的“对齐”。正如 Tom 提到的,当需要协调时,工作流或 saga 实现可能会派上用场。质疑一致性要求(这个其他 BC 什么时候需要知道给定信息?)可能会给 BCs 如何互操作带来新的见解。你看,UI 是一个非常有用的反馈循环。当它与 BC 的用例不一致时,可能是用例有问题,或者它在 UI 中的设计方式有问题,或者我们只是发现了一个不同的用例。这就是为什么 UI 模型成为讨论的绝佳工具的原因。他们对相同的问题/解决方案提供了额外的观点。额外如“

就我个人而言,我发现当我讨论 UI 时,我戴着另一顶帽子,而不是讨论纯功能(你知道,不需要 UI 来解释应用程序正在做什么/应该做什么的事情)。我可能会在同一次谈话中更换帽子,只是为了找出不一致的地方。

于 2013-05-26T11:14:52.130 回答