【问题标题】:How to choose the appropriate event abstraction level如何选择合适的事件抽象级别
【发布时间】:2026-01-29 11:45:01
【问题描述】:

我猜这个问题可能与任何事件系统有关,例如事件溯源/DDD/Lambda 架构、ESB、Actor...我标记了这个问题,以便有这些系统经验的人可以回答。

我目前正在尝试我的初创公司an original way to build user interfaces,使用 DDD / 事件源世界中经常使用但应用于 javascript 单页应用程序的概念。我想要的是一个纯粹的功能 UI,我可以在其中重播内存中的事件日志以恢复 UI 状态(事件日志不会在浏览器关闭时持久化,它只是为了让事情变得容易推理并可能启用很酷的功能像免费的 UI 撤消/重做,像 ELM 中的时间旅行调试器......)


有时我只是想知道如何为事件选择正确的抽象级别,因为在我看来,刚刚发生的事情可能有多种“解释”。

基本上,在 JS SPA 中,许多事件是在用户单击按钮、链接或类似内容后触发的。所以我可以在我的事件日志中添加一个条目作为这个低级事件(在设法使其可序列化之后)。但是这个事件也可以被解释为用户在高级抽象中实际所做的事情的表示。

举个例子,假设我的 UI 上有一个弹出窗口,它会在某个时候显示。 当用户点击弹出窗口之外的任何地方时,它应该被关闭。 现在让我们想象一下,用户点击弹出窗口外的div 以关闭它。我应该将什么事件抽象级别添加到事件日志中?

  • 用户点击了div
  • 用户在弹出窗口外点击了吗?
  • 用户已关闭弹出窗口?

这 3 个事件抽象对我来说似乎是正确的:它们描述了过去发生的事情,但在不同的抽象级别。所以我问自己一些问题,比如:

  • 我必须在 3 个抽象级别中选择一个吗?如果可以怎么选?
  • 我可以或应该触发所有 3 个事件吗?如果是这样,事件日志会不会变得有点乱?
  • 我是否应该使用 DDD Saga 之类的概念,以便在收到“用户单击 div”事件时,Saga 可以触发“关闭弹出”命令或其他什么?

【问题讨论】:

    标签: domain-driven-design akka esb actor event-sourcing


    【解决方案1】:

    在所描述的示例中,我建议根本不生成任何事件,因为您没有执行任何命令(更改应用程序状态的操作)。您可能希望仅在实际具有保存它的实际价值的情况下进行此类事件,或者您可以认为它在最近的将来变得有价值,因此您可以使用所有这些信息来生成某种报告,该报告将成为对您的业务有价值。

    这背后没有硬性规定,这只是常识问题。你想解决什么样的问题?如果它正在实现重做/撤消,事件溯源不是您应该寻找的第一件事,“命令”模式可能是一个更好的选择。仅通过事件源,您将无法“免费”撤消/重做。考虑将您的答案分成多个答案,并更具体地说明实际目标。

    另外,我的建议是尽可能远离 SAGA。在可能的情况下同步执行总是更好,否则你的代码很快就会变得一团糟(可读性降低,语义距离增加)。如果你必须让它异步,更好的选择是使用“延续传递风格”,“承诺”。 Sagas 应该被视为最后的手段,并在所有其他方法都无法工作时使用......但是当你可爱的 noSql 事物不支持 ACID 事务时,这种情况更有可能出现在服务器端,例如:p。

    如果我错了,请纠正我,但看起来你只是想玩一种“新的时尚方法”并尝试它而没有任何实际理由。没关系,只要您可以使您的应用程序变得比应有的更复杂。 (开发和支持成本更高)

    【讨论】:

    • ILCH 提供一些背景信息,我们已经有了一个使用这些概念的生产运行应用程序,并取得了巨大成功,并且更容易开发和推理我们以前使用 Backbone 的生产应用程序。我们已经使用 Q 之类的库来处理异步操作。
    • 您谈论“生成某种报告”,但实际上呈现给用户的 UI 是(某种)报告:我们“查询”状态以重绘 UI。所以任何改变状态,从而改变报告/用户界面的东西都应该来自一个事件(这里的叠加点击应该关闭弹出窗口,所以叠加点击事件确实会改变一些状态)。我正在尝试将后端概念转换为 UI 开发,所以我冒昧地假设有一些“自我验证命令”。例如,如果用户在文本输入中键入了一些字符,是否值得为此触发命令?
    • 关于免费撤消/重做,实际上我们在生产应用程序中确实有它:我们可以恢复以前显示的任何 UI(为此我们可以使用事件源,甚至状态源,因为事件的投影在浏览器会话期间不会更新)。但是,这可能不是您所说的撤消/重做,因为我们仅将视图恢复到以前的本地状态,但我们不会“恢复”用户意图:撤消时,永远不会调用后端 API所以你最终可能会显示一个陈旧的 UI。所以兴趣有限(见youtube.com/watch?v=DMtwq3QtddY
    • 顺便说一句,我认为您对 Sagas 的看法是正确的:它引入了复杂性,并且比同步处理的事件更难推理。但在某些 UI 场景中,它似乎是一个合适的用例。我发现它最有用的地方是我们的应用程序载入:用户可以在此载入中执行实际操作(不仅仅是呈现应用程序的轮播),但 Saga 添加了一些额外的逻辑,只应在入职正在运行。
    • >>例如,如果用户在文本输入中键入了一些字符,是否值得为此触发命令?我认为答案是——如果你想对单个字符执行“重做”操作,那么可以。但是,如果您永远不会使用这些事件,那么触发单独的命令并为每个字符生成单独的事件又有什么意义呢?
    最近更新 更多