【问题标题】:In a layered architecture, how can the Application layer know about Web URLs?在分层架构中,应用层如何知道 Web URL?
【发布时间】:2021-07-02 14:53:45
【问题描述】:

我目前正在开发一个使用分层架构(Web/应用程序/基础架构/域)的 .NET 5 应用程序。如果我要遵循洋葱/干净架构模式,则依赖项应该只向一个方向流动,例如:

Web -> 应用程序 -> 基础设施 -> 域

我现在发现自己需要从应用层发送几封包含特定前端 URL 的电子邮件。这意味着Application层会知道Web层,打破依赖流。

一个示例用例流程是:

  1. 用户发出请求,由 Web 层中的控制器处理
  2. 控制器调用应用层的处理程序
  3. 应用层使用来自基础设施层的电子邮件服务来发送电子邮件

在第 3 步中,我在应用层,但需要 Web URL 来构建电子邮件正文。

我该如何解决这个问题?

【问题讨论】:

  • 网址实际上只是数据。这与对 Web 的依赖不同,除非您只能通过调用 Web 来获取它们
  • 如果您需要内层中的外层信息,请通过调用从外层到内层将信息从外层移动到内层。
  • 您要么需要通过层向下传递它,要么将其存储在appsettings.json 以获取更多信息check this
  • @Crowcoder 我猜是这样,尽管能够从它的方法中解析控制器 URL 以防万一路由发生变化会很好,所以一切都会按预期继续工作。
  • @Ralf 我也想到了这一点,但有时通过几种不会使用它的方法向下传递数据似乎并不理想。会考虑一下,谢谢。

标签: c# .net .net-5 clean-architecture onion-architecture


【解决方案1】:

我最近在我的组织中解决了这个问题。在我们的例子中,我们有一个供整个公司使用的 API“市场”,然后是紧密集成的客户端使用的反向代理,最后是 API 容器的内部负载均衡器。

这是我的 Web 层和 App 层不应该知道的 3 层 URL 知识(即使在 Web 层它也不应该知道这一点,因为这会使我们的 Web 层承担多个责任,而不仅仅是一个路由器(例如通过 Mediatr))。

在基础架构中使用重写器

这就是 Z. Danev 的answer 的全部意义所在。这行得通,但是您必须维护这些层中的每一层的所有规则,并且每次重写都可能会增加开销。此外,根据您返回的数据的复杂性,这些规则可能会变得很棘手。

一个有效的解决方案。根据您的组织,这可能是一件容易的事情,也可能是一件困难的事情,因为它由其他团队维护,需要工作票等才能完成工作。

好吧,如果你不能或不想这样做,那么......

应用层依赖倒置和模式

免责声明:此解决方案对我们来说非常好,但它确实有一个缺点:在某种程度上,您必须维护了解上述层的东西。所以告诫购买者。

我上面描述的情况与您的问题大致相似,尽管可能更复杂(您可以这样做,但要简化它)。在不违反您的架构原则的情况下,您需要提供一个接口(或多个接口),该接口可以作为应用程序服务注入您的应用程序层。

我们调用了我们的ILinkBuilderService 并创建了一个LinkBuilderService,它本身可以通过具有单独ILinkBuilder 实现的DI 容器进行连接。这些实现中的每一个都可以是MarketPlaceBuilderGatewayBuilder 等,并将根据Chain of ResponsibilityStrategy 模式从最外层代理到最内层进行排列。

通过这种方式,构建器检查 Web 上下文(标头、请求等)以确定应由哪一个负责构建链接。您的应用程序层(例如您的电子邮件发件人)只需使用关键数据调用链接构建服务接口,这用于生成面向客户端的 URL,而不会将应用程序层暴露给 Web 上下文。

在不涉及太多细节的情况下,这些构建器会在 HTTP 请求到达每个端点时检查诸如 X-Forwarded-For 之类的标头、自定义标头以及代理提供的其他详细信息。责任链是关键,因为它允许应用层生成正确的 URL无论请求来自哪个层

那么这如何不破坏单向流呢?

好吧,您将这些构建器单向下推到您的应用程序层。从技术上讲,它们确实可以返回到 Web 层以获取上下文,但这是被封装的。 这没关系,不会违反您的架构。这就是依赖倒置的意义所在。

【讨论】:

  • 嘿@Kit,感谢您的详细回复。你的意思是我会在 Web 层中定义 ILinkBuilderService 的实现,以便我可以访问 HttpContext 并构建我的 URL?您是否发现许多应用程序服务/Mediatr 请求现在需要这些服务作为应用程序层中相应处理程序的有效负载?我想这是一种比传递特定 URL 更漂亮/更灵活的方式,因为我现在可以访问构建我需要的任何内容。
  • 是的,实现将在 Web 层中。它们可以在运行时通过 Mediatr 传递,或者在向 IoC 容器注册后简单地注入应用层。构建器将获取上下文的一部分(例如向外的主机、路径等),并从中构建相关的 URL。例如,如果您有https://apigateway.com/v1/orders/2,您可以构建https://apigateway.com/v1/orders/items...v1/users/joe,即使您的API 确实存在于http://random.host.com:8000/v1。这避免了将 URL 传递给服务;您正在传递上下文感知构建器。
【解决方案2】:

考虑在 Web 基础架构级别(例如网关或负载均衡器)配置“众所周知的 url”,以便您可以在电子邮件中包含“mycompany.com/user-action-1”,这将转换为正确的端点你的网络应用程序。

【讨论】:

  • 您的意思是定义 URL 的较低级别和“实现”它们的 Web 层?那么 /user-action-1 将是 Web 层应该为其找到 URL 的应用程序级概念?
  • 是的,我建议 URL 是基础设施问题。考虑拥有不同 url、负载平衡器、dns 服务器、ssl 的 dev/test/prod 环境......
猜你喜欢
  • 1970-01-01
  • 2015-08-22
  • 1970-01-01
  • 2012-09-04
  • 2019-08-31
  • 2013-12-29
  • 2018-08-27
  • 2011-05-05
  • 1970-01-01
相关资源
最近更新 更多