从 CRUD 迁移到事件溯源的秘诀 - eventstore

VSole2022-08-08 10:12:07

事件溯源是高性能协作域的一种很好的架构风格,可以保证它增加的复杂性。但正如我之前所说,就像任何其他原则或实践一样,即使是事件溯源也有利有弊。而且它不是顶级架构。您系统的某些部分可能会从中受益,但其他部分可能不会。话虽如此,如果您需要事件溯源,并且您有一个现有的、更传统的(又名 CRUD)应用程序,您可以遵循大致三种策略:

  1. 保持一切原样,仅使用事件溯源构建系统的新部分
  2. 通过并排重建现有子系统或域来隐藏它。
  3. 然后,在重建完成后,切换所有现有消费者并自动迁移数据。
  4. 对现有域进行逐个实体的逐步迁移

大约七年前,我们逐渐将使用命令查询职责分离 (CQRS) 模式设计的现有 .NET 应用程序转换为事件溯源。由于前两个场景已经写了很多,让我分享我们为后者采取的秘诀。 

让我们从建立术语开始。在更传统的系统中,您的域由实体组成。在事件溯源世界中,您经常会看到几个相关实体形成了一个事务边界。在领域驱动设计中,这称为聚合。大多数事件存储使用术语流来捕获该聚合中曾经发生的所有事件。并且该聚合中通常有一个实体作为唯一的入口点。这是聚合根,由唯一编号或键(流 ID )标识。现在我们已经解决了这个问题,这里有一些实用的步骤来帮助你前进。

  1. 弄清楚您当前的域是否依赖于跨多个实体的事务,以及事件存储实现是否支持跨聚合(或跨流)事务。
  2. 仔细决定哪些实体将形成聚合。
  3. 如果您的聚合太大,并且您还没有准备好采用事件合并技术,则会增加用户运行在乐观并发问题中的机会。
  4. 如果您的聚合太小,并且您的事件存储不支持跨聚合事务,则您必须以功能方式处理这些业务规则,例如,使用补偿操作。
  5. 这就是为什么让这些不变量帮助您定义聚合的边界如此重要。
  6. 确定哪个实体应作为聚合根、聚合的入口点,并向其添加版本。
  7. 确保对聚合内实体的任何更改都会影响版本。
  8. 如果那里已经有一个版本,我们建议通过将事件数添加到原始版本号来计算新版本。
  9. 确保没有其他代码可以在不首先通过聚合根的情况下改变聚合内实体的状态。
  10. 将子实体上的可写属性和公共方法替换为根上的方法,因此根控制访问,可以保护业务规则,生成唯一的子 ID 并提高版本。
  11. 删除跨聚合的实体之间的直接依赖关系。
  12. 例如,在对象关系映射器支持的许多域中,具有延迟加载属性是很常见的。
  13. 您需要重构任何依赖于它的代码,或者引入和注入存储库抽象。
  14. 确保实体不知道持久性并且不直接访问数据库。
  15. 要么将其移动到处理来自您的 API 的传入请求的命令处理程序,要么为此引入存储库抽象。
  16. 为该聚合确定一个自然分区键,这样您就可以在事件存储变得非常大并导致性能问题或存储问题的情况下拆分事件。
  17. 一个很好的分区键是以这样一种方式分离数据的东西,您不需要跨分区处理业务规则。
  18. 例如,您的域可能是按地理区域或公司组织的。
  19. 在多租户域中,租户 ID 将是一个很好的候选者。
  20. 由于您不应修改历史记录,因此事件溯源中的删除概念略有不同。
  21. 尽管您在技术上可以从底层事件存储中删除事件,但您通常会采用更实用的方法并使用事件将聚合标记为已删除。
  22. 因此,任何用于请求实体的特定实例并准备好找不到任何内容的查询都必须明确采用或通过某种抽象采用。
  23. 一个常见的解决方案是将 IsDeleted 属性添加到存储库实现可以检查的聚合根。
  24. 考虑数据导入需求。
  25. 如果您习惯于直接通过表导入数据,则必须将其更改为 CLI 或 HTTP API 之类的内容。
  26. 还要决定是要通过现有的“属性更改”事件还是通过专门的“数据已导入”事件来处理该导入。
  27. 仔细确定如何将实体的原始键映射到流 ID。
  28. 大多数事件存储支持使用字符串作为流 ID,但如果不经过一些更复杂的循环,就不可能在事后更改 ID。
  29. 如果您的商店仅使用 GUID,您可以使用像这样的确定性 Guid 生成器。
  30. 并且不要忘记内部密钥与您在域外公开的密钥之间存在差异。
  31. 与此密切相关的是,在事件溯源中保证唯一性的工作方式略有不同。
  32. 因此,如果您的域依赖于数据库模式来保护唯一约束,您将需要找到替代方案(例如使用流 ID)。
  33. 引入用于从/向事件存储加载和保存聚合的基础结构,并从持久化事件中重新混合聚合。
  34. 您可以在此处、此处和此处找到一些有关如何执行此操作的示例以及 .NET 中聚合根的基类。
  35. 到目前为止,我们主要使用这些参考作为示例,而不是作为框架来构建我们的域。
  36. 如果您有存储库抽象,请确保它知道哪些实体已转换并需要从事件存储中加载,哪些仍需要从原始表中加载。
  37. 为此,我们使用了标记接口或 .NET 属性。
  38. 推迟诸如快照之类的决定,直到您需要它们为止。
  39. 对于最终具有大量事件的聚合来说,快照是一种有效的解决方案。
  40. 但是,在您获得足够的性能结果来保证这种复杂性之前,不要去那里。
  41. 决定如何将存储在数据库中的现有实体转换为事件源聚合。
  42. 过去,我们试图将现有记录映射到单个的、更多“属性更改”的事件中。
  43. 回想起来,我们应该已经定义了一次性转换事件。
  44. 确定您是否希望使投影代码在事务上与聚合发出的事件一致,以及这是否会给您可接受的性能。
  45. 如果您不这样做,并且所有投影表都是异步构建的,请确保代码库的其余部分不希望投影表上的查询保持一致。
  46. 设计将现有数据转换为新的事件源模型的策略。
  47. 例如,这就是我们所做的:
  48. [list=1]
  49. 使用临时名称重命名现有表及其子表
  50. 一一读取记录并使用您在前面步骤中设计的事件构建新的聚合
  51. 将这些新事件投影到一组新的表中,这些表的名称和结构与迁移开始前的样子相同
  52. 转换和投影后立即从临时表中删除每条记录
  53. 删除临时表
  • 对其余实体重复前面的步骤,但不要犹豫,在生产中发布中间步骤。
  • 根据您的需要构建更优化的投影。
  • 但不要忘记,第一个目标是转换您现有的代码库。
本作品采用《CC 协议》,转载必须注明作者和本文链接
事件溯源是高性能协作域一种很好架构风格,可以保证它增加复杂性。但正如我之前所说,就像任何其他原则或实践一样,即使是事件溯源也有利有弊。而且它不是顶级架构。您系统某些部分可能会从中受益,但其他部分可能不会。话虽如此,如果您需要事件溯源,并且您有一个现有、更传统(又名 CRUD)应用程序,您可以遵循大致三种策略:
批处理用于每天为企业处理数十亿交易。Spring Batch是一个轻量级,全面批处理框架,旨在开发对企业系统日常运营至关重要强大批处理应用程序。大批量批处理作业可以高度可扩展方式利用该框架来处理大量信息。以某种方式处理数据。什么是JobJob和Step是spring batch执行批处理任务最为核心两个概念。其中Job是一个封装整个批处理过程一个概念。
这篇技术干货 带你了解深信服AI技术
RedisJSON 横空出世
2021-12-21 16:18:23
近期官网给出了RedisJson(RedisSearch)性能测试报告,可谓碾压其他NoSQL,下面是核心报告内容,先上结论: 对于隔离写入(isolated writes),RedisJSON 比 MongoDB 快 5.4 倍,比 ElasticSearch 快 200 倍以上。 对于隔离读取(isolated reads),RedisJSON 比 MongoDB 快 12.7 倍,比
领域服务建议边界是子域,因为对于单个团队来说,边界不应太大。业务域通常是由多个子域组成大部分业务区域。实际上,它们在域中每个服务都代表该域一部分,我称之为子域,但是您可以决定将其命名为不同东西。新构架,新问题毫无疑问,微服务已证明命名很重要。包括领域服务,API端点及其代表概念。去年,Uber在Uber上发布了面向领域微服务,向我们展示了微服务发展,使其更加注重领域。
业务同学抱怨业务开发没有技术含量,用不到设计模式、Java 高级特性、OOP,平时写代码都在堆?CRUD,个人成长无从谈起。其实,我认为不是这样。设计模式、OOP 是前辈们在大型项目中积累下来经验,通过这些方法论来改善大型项目可维护性。
//把Map购物车转换为Item列表??//应付总价=商品总价+运费总价-优惠??然后实现针对 VIP 用户购物车逻辑。所以,这部分代码只需要额外处理多买折扣部分:public?
InfoWorld 公布了 2022 年最佳开源软件榜单。InfoWorld 2022 年 Bossie 奖旨在表彰年度最重要和最具创新性应用程序开发、devops、数据分析和机器学习工具。Redwood 允许针对各种部署环境,包括 Vercel 和 Netlify 等无服务器平台。它允许拖放组件来构建仪表板、使用 JavaScript 对象编写逻辑并连接任何 API、数据库或 GraphQL 源。Spinnaker 是一个持续交付平台,它定位于将产品快速且持续部署多种云平台上。Spinnaker 主要特性:配置一次,随时运行;随地部署,集中化管理;开源。
目前,多数项目会有多数据源要求,或者是主从部署要求,所以我们还需要引入mybatis-plus关于多数据源依赖:。#设置默认数据源或者数据源组,默认值即为master. true未匹配指定数据源时抛异常,false使用默认数据源。表名注解,用于标识实体类对应表。其说明如下,关于这些书写,常规情况基本很少用到,不做多余解释了:@Documented
MyBatis-Plus是一个 MyBatis 增强工具,在 MyBatis 基础上只做增强不做改变,为简化开发、提高效率而生。真实开发中,version(乐观锁),deleted、gmt_create、gem_mo
VSole
网络安全专家