在本系列的 第 1 部分 中,我介绍了将代码重构为基于微服务的方法的一些重要原因和考虑事项。我在第 1 部分末尾处,介绍了如何处理您的数据的问题。在大型企业应用程序中,数据通常是非常棘手的问题,而且值得深入处理。
在考虑您应用程序的结构时,选择管理数据的最佳方法通常可归结为此问题:“您将在数据库中实际存储何种数据?”
自上世纪 90 年代初以来,我帮助许多公司构建、维护和设置对象关系映射 (ORM) 框架。在某些情况下,公司存储的数据实际上没有 很好地与关系数据模型建立映射关系。在这些情况下,我们发现自己需要 “调整” 关系模型来适合它,甚至有可能要越过程序中的层层关卡,强制代码与关系数据存储相匹配。
现在,我们已进入一个混合持久化选择时代,我们可以重新审视这些早期的决策并制定更好的决策。具体地讲,我们将查看 4 种关系模型不是 最佳选择的情况,然后考虑一种关系模型是 最佳选择(且重构数据不是正确选择)的情况。
许多时候,在浏览企业系统的持久性代码后,我惊奇地发现,在企业的关系数据库中,实际存储的是序列化 Java™ 对象的二进制表示。这些对象通常存储在 “二进制大对象 (Blob)” 列中,这通常是因为团队屈服于尝试将其 Java 对象映射到关系表和列的复杂性。当然,Blob 存储的缺点是绝不会按列进行查询,速度缓慢,而且对 Java 对象本身的结构变化很敏感:如果对象结构发生重大变化,较老的数据可能无法读取。
因此,如果您的应用程序(或者更可能是您应用程序的子集)在关系数据库中使用了 Blob 存储,这是一个表明您可能最好使用键值存储(比如 Memcached 或 Redis)的好迹象。
另一方面,如果您的应用程序只存储了结构化 Java 对象(或许具有深层结构,而不是原生二进制),那么您最好使用 Cloudant 或 MongoDB 等文档存储。此外,通过花点精力考虑如何存储文档(例如,Cloudant 和 MongoDB 数据库都是 JSON 文档存储,JSON 解析器广泛可用且容易自定义),您在处理任何 “模式漂移” 问题时可以比采用 Blob 存储方法轻松得多,Blob 存储方法的存储机制的透明度要差得多。
几年前,在 Martin Fowler 编写 企业应用程序架构模式 时,我们就许多模式举行了活跃的交流和多场活跃的评审会议。具体地讲,我始终觉得有一种模式很奇怪:Active Record 模式。这种模式的奇怪之处在于,我个人从未遇到过它,但 Martin 向我保证它在 Microsoft .NET 编程社区中很常见。确切地讲,在看到它的一些 Java 实现使用 iBatis 等开源技术时,真正让我震惊的是,使用它的最佳时机似乎是对象为扁平对象时。
如果您映射到数据库的对象完全是扁平的,与其他对象没有任何关系(仅在有限的例外情况下具有嵌套对象),那么您或许没有充分利用关系模型的完整功能。事实上,您很可能会存储一些文档,比如客户满意度调查、问题通知单等纸质文档的电子版本。在这种情况下,Cloudant 或 MongoDB 等文档数据库或许更适合您。将您的代码拆分为处理该数据库类型的服务,您会获得更容易维护、更简单的代码。
我在对象关系映射系统中看到的另一种常见模式是 “表中的参考数据被吸入到内存型缓存中”。参考数据由不常(或从不)更新但不断被读取的数据组成。这方面的一个很好例子是美国的州和加拿大的省列表,其他例子还包括医疗代码和标准零部件清单。这种数据通常用于填充 GUI 中的下拉列表。常见的模式是在每次需要时,首先从一个表(通常是一个包含两列或三列的平面表)中读取该列表。但是,此模式的性能很差,所以系统在启动时会将它读入到内存型缓存中,比如 Ehcache。
当您遇到这个问题时,您可能希望将它重构为一种更简单、更快的缓存机制。同样地,在这种情形下,Memcached、Bluemix 上的 Data Cache 服务 或 Redis 非常适合。如果参考数据与您数据库结构中的剩余数据独立(而且它通常是松散耦合的),那么从系统的剩余部分中拆分数据及其服务可能很有帮助。
在我处理的一个客户系统上,我们执行了复杂的财务建模,只是创建可供程序操作的对象就需要非常复杂的查询(达到 6 或 7 项连接的级别)。更新甚至更为复杂。我们需要组合多个不同级别的乐观锁进行检查,这样做只是为了确定发生了哪些变化和数据库中的内容是否仍与我们创建和操作的结构匹配。
回顾过去,我们所做的工作可以更自然地建模为图表。类似这样的情形(在这种情况下,我们建模了各个基金部分,每部分包含不同类型的股票和债券,每种资产使用不同的货币定价,具有不同的到期时间,每种估值方法具有不同的规则)需要一种使您能够轻松完成真正想要做的事的数据结构:遍历图表并随意移动图表的各个部分。
此时,类似 Neo4J or Apache Tinkerpop(它是 Bluemix 上的 IBM Graph database 服务 的支持技术)这样的解决方案是一种不错的方法。通过将解决方案直接建模为图表,我们可以避免大量复杂的 Java 和 SQL 代码,与此同时,或许可以显著提高我们的运行时性能。
现在,我们已经了解了与关系模型的数据 “不一致” 的 4 种情况,我还想展示另一种情况,在这种情况下,您不 希望尝试将数据与主要企业数据存储分离。
我的一个客户遇到了以下问题:他们是一家为选民、居民提供服务的政府机构。但是他们的企业系统中有拥有居民的数百种表示。像 “问这个问题的人是否与填写这个表是同一个人” 这样的简单问题,该问题不可能找到答案。这让政府非常沮丧,也让居民很失望,而且让中途参与的 IT 人员很沮丧。他们知道他们必须尝试使用某种不同的方法。
只要我开始与客户谈论微服务,他们通常会说 “哦,我们无法这么做。我们在企业数据建模上投入了太多的资金。”从某种意义上讲,他们是对的:您不希望进入企业数据模型中,提取出与其他概念紧密联系的数据。这种工作是徒劳的。如果您的数据模型是有效的,而且没有对您造成麻烦,那么完全没有理由改变它。
不是所有数据模型都不会带来麻烦。事实上,还有另一种朝类似方向发展的趋势。这种同时存在的趋势不是来自开发人员,而是来自数据建模人员和 DBA。这是一种朝主数据管理 或 MDM 发展的趋势。从某种意义上讲,这是相同的理念:您不应在企业中拥有一个重要概念(比如 “客户”)的多个视图。MDM 工具可帮助您将所有这些不同概念视图组合起来,从而消除不必要的重复。
区别在于 MDM 解决方案是以数据为中心的,而不是以 API 为中心的。但是,应用 MDM 解决方案的一种常见结果是,通过 MDM 工具创建一个集中化的 API 集来表示对这些常见概念的访问。对于为什么无法将这些 API 视为基于微服务的方法的基础,没有原因。您只需要知道,实现不是在同一个工具中完成的。但到了最后,微服务架构的优势是,它是至关重要的 API 而不是实现。
在我们介绍的 4 种情形中,特定的 “代码风格” 会让您相信,在最深处,您仍具有可通过基于微服务的数据重构来解决的数据建模问题。如果您发现您有一种或多种这类的特定编码问题,您最好与现有企业数据存储分离,并采用不同类型的数据存储来重新构想它。
在本系列的下一篇和最后一篇文章中,我们将应用前两篇文章中学到的知识,展示如何按照一个循序渐进的过程,将您的现有应用程序从单体结构演变为微服务。
相关主题:重构到微服务,第 1 部分
相关主题:企业应用程序架构的模式
相关主题:developerWorks 上的微服务 TV 视频
BLUEMIX SERVICES USED IN THIS TUTORIAL: