考虑以下针对在线商店项目的微服务:
用户服务保存有关商店用户的帐户数据(包括名字,姓氏,电子邮件地址等)
购买服务会跟踪用户购买的详细信息。
每项服务都提供用于查看和管理其相关实体的UI。
购买服务索引页面列出了购买。每个购买项目应包含以下字段:
id,购买用户的全名,购买的商品名称和价格
此外,作为索引页面的一部分,我希望有一个搜索框让商店经理通过购买用户名来搜索购买。
我不清楚如何获取采购服务不具备的数据 - 例如:用户的全名。 当尝试通过购买用户名来进行更复杂的搜索购买时,问题会变得更糟。
我认为我可以通过在用户创建时广播某种事件(并且只保存购买服务端的相关用户属性)来同步两个服务之间的用户来解决这个问题。从我的角度来看,这远非理想。当你有数百万用户时,你如何处理这个问题?你会在每个消费用户数据的服务中创建数百万条记录吗?
另一个明显的选择是在用户服务端公开API,它根据给定的ID返回用户详细信息。这意味着购买服务中的每个页面加载,我都必须调用用户服务才能获得正确的用户名。不理想,但我可以忍受它。
如何根据用户名实施购买搜索?我总是可以在Users Service端公开另一个API端点,它接收查询项,在Users Service中对用户名执行文本搜索,然后返回符合条件的所有用户详细信息。在采购服务中,将相关的ID映射回正确的名称并在页面中显示它们。这种方法也不理想。
我错过了什么吗?有没有其他方法来实现上述?也许我遇到这个问题的事实是一种代码味道?我很乐意听到其他解决方案。
答案 0 :(得分:14)
当进入微服务时,这似乎是一个非常普遍和核心的问题。我希望有一个很好的答案: - )
关于这里已经提到的建议模式,我将使用术语数据非规范化而不是多语言持久性,因为它不一定需要在不同的持久性技术中。关键是每个服务都处理自己的数据。是的,你有数据重复,你通常需要某种事件总线来跨服务共享数据。
还有另一种选择,这是第一种选择 - 将搜索本身作为单独的服务。
因此,在您的示例中,您拥有用于管理用户的用户服务。购买服务管理购买。每个都处理自己的数据,只处理它需要的数据(例如,购买服务并不真正需要用户名,只需要ID)。并且您有第三项服务 - 搜索服务 - 消耗其他服务产生的数据,并创建搜索"查看"来自综合数据。
答案 1 :(得分:4)
将适当的数据保存在不同的数据库中是完全没问题的,它被称为Polyglot Persistence。是的,您希望单独保留有关购买的用户数据和数据,并使用消息队列进行同步。数以百万计的用户对我来说似乎很好,它的可扩展性,而不是设计问题; - )
如果是搜索 - 你可能想搜索的不只是用户名,对吗?因此,如果您使用消息队列来更新服务之间的数据,您也可以轻松地将此数据路由到ElasticSearch。从ElasticSearch的角度来看,索引哪个字段 - 用户名或产品标题并不重要。
答案 2 :(得分:1)
我通常使用这两种方法。有时我有另一项服务,它坐在x其他服务的顶部,并结合数据。我真的不喜欢这种方法,因为它导致服务之间的依赖关系和耦合。所以总的来说,在我上一期的项目中,我们试图坚持多语言持久性。
还要考虑一下,如果您需要在某种中间件服务中使用x sub http请求来组合数据,它会导致更高的延迟。我们总是试图减少一个任务的请求数量,并通过异步队列处理所有可能的事情。 (尤其是数据同步)
答案 3 :(得分:0)
如果您将模块概念化为它们处理的数据的所有者和控制者,那么您的模型还必须将该数据从该模块中传达给其他人。相比之下,制造过程中的模块可以访问变更数据,而无需拥有和控制变更数据。
与大多数代码一样,微服务是一种用于分布式处理的体系结构,其中模块将数据传递到周围进行处理。从《哈佛商业评论》和麦肯锡撰写的关于拥有供应链成员的主题的经典文章中,我发现了这种模型带来的复杂性,并写了一篇文章告诉程序员您需要了解的知识:http://www.powersemantics.com/p.html
制造是用于集成处理的体系结构,其中模块处理数据,而又不从点到点传递数据。这可以通过将模块配置为访问相同的内存,文件或数据库表来实现。我的体系结构展示了如何通过引用属性在内存上完成此操作。
当您考虑“在用户服务端公开一个API时,该API会根据给定的ID返回用户的详细信息”时,您需要知道会造成HBR所谓的“不可逆”复杂性,我称之为集中化复杂性。不要构建A-> B(分布式)系统,因为在无法分离需求之后,您以后就无法将它们分散。生产流程中的需求代表用户说明,而集中式模块仅使您能够更改错误的用户流程。换句话说,集中式模块不会记录用户组或将它们与派生产品用户区分开。