面向对象中关键聚合组合_面向聚合的微服务是死胡同

面向对象中关键聚合组合

Previously, I wrote about the Aggregate, an important but underappreciated microservice design pattern. Often when I introduce this pattern, I hear a common concern: if we adopt an Aggregate-oriented architecture, won’t we be closing the door to searching our data?

之前,我写过关于Aggregate的文章,Aggregate是一种重要的但未被重视的微服务设计模式。 通常,当我介绍这种模式时,我会听到一个共同的担忧:如果我们采用面向聚合的体系结构,我们会不会为搜索数据关上大门?

The answer is, emphatically, no.

答案是肯定的。

In this article, we’ll recap that Aggregate pattern, show why (on first glance) it might appear that the pattern precludes searching, and then explain why that’s not actually the case.

在本文中,我们将概述该聚合模式,并说明为什么(乍看之下)该模式可能会阻止搜索,然后解释为什么实际上并非如此。

The Aggregate is an important design pattern when it comes to designing microservices. I’d described this pattern, its benefits, and how to adopt it in a previous article. But let’s summarize it here.

在设计微服务时, 聚合是一种重要的设计模式。 在上一篇文章中 ,我已经描述了这种模式,它的好处以及如何采用它。 但是让我们在这里总结一下。

Briefly, an Aggregate is a group of related entities that is treated as a single, atomic unit. By definition, an Aggregate consists of:

简而言之,集合是一组相关的实体,被视为单个原子单位。 根据定义,聚合包括:

  • Some number of related entities

    一些相关实体
  • A boundary that clearly defines the entities that are contained in the Aggregate (as well as those that are not)

    明确定义聚合中包含的实体(以及不包含在聚合中的实体)的边界
  • A single root entity, which is the only entity within the Aggregate that is directly accessible from the outside world

    单个实体,它是集合中唯一可以从外部直接访问的实体

One of the clearest examples of an Aggregate might be a User object. While specific use cases might differ, we can easily picture an object model in which we have a core User object (with fields such as FirstName, LastName, DateOfBirth, NationalId, etc). We might attach collections of contact information, such as Address, Phone, or Email to that User object.

聚合的最清楚的例子之一可能是User对象。 尽管特定的用例可能有所不同,但我们可以轻松地描绘一个对象模型,其中有一个核心的User对象(具有诸如FirstNameLastNameDateOfBirthNationalId等字段)。 我们可能会将联系信息的集合(例如AddressPhoneEmail附加到该User对象。

Image for post
Any other entities can access a User Aggregate only through the Aggregate Root: the User entity
任何其他实体只能通过聚合根访问用户聚合:用户实体

The User object itself would clearly by the User Aggregate’s root. No other entity outside of the Aggregate should be able to access the User Aggregate via any of the other entities in the Aggregate.

User对象本身将清楚地由User Aggregate的根目录构成。 聚合之外的任何其他实体都不能通过聚合中的任何其他实体访问用户聚合。

There are a myriad of benefits in adhering to this pattern that I outlined in my previous article; those reasons include:

坚持我在上一篇文章中概述的这种模式有很多好处; 这些原因包括:

  • A clear blueprint for splitting apart our monolithic data model

    拆分整体数据模型的清晰蓝图
  • Allowing us to shard our databases as we scale

    在扩展时允许我们分片数据库
  • Providing clearly-defined messages that our services can produce for other services to consume

    提供我们的服务可以产生供其他服务使用的明确定义的消息
  • Making safe retries of unsuccessfully-consumed messages possible

    可以安全地重试未用完的消息
  • Dramatically simplified caching

    大大简化了缓存

聚合和微服务 (Aggregates and Microservices)

How do Aggregates affect the design of our microservices? Primarily, they guide us in the design of our Data Services; that is, the microservices that serve as gateways to our organizations data.

聚合如何影响我们的微服务设计? 首先,它们指导我们设计数据服务 ; 也就是说,微服务充当了我们组织数据的网关。

Put another way, as we design our Aggregates, these Aggregates naturally drive the design of:

换句话说,在设计聚合时,这些聚合自然会驱动以下各项的设计:

  • the schemas of the databases that store the Aggregates

    存储聚合的数据库的模式
  • the API of the microservices that provide direct access to those databases

    提供直接访问这些数据库的微服务的API

Looking at our User example from above, then, we would likely wind up with a microservice and database pair that looks something like the following:

然后,从上面看我们的User示例,我们可能会得到一个类似于以下内容的微服务和数据库对:

Image for post

Note that we’ll depict ReST APIs in our examples, for illustrative purposes. But the same principles apply to Thrift APIs, gRPC APIs, etc. The database might be an RDBMS such as MySQL, but could equally easily be a document datastore such as MongoDB.

请注意,出于说明目的,我们将在示例中描述ReST API。 但是,相同的原则也适用于Thrift API,gRPC API等。数据库可能是RDBMS(例如MySQL),但也很容易成为文档数据存储区(例如MongoDB)。

Within our architecture, we would have other, higher-level services that would make use of these data services. For example, we might have an Orchestration Service that combines various Aggregates. The example below depicts a service within an eCommerce website that puts together an overview of an online order, via the following steps:

在我们的体系结构中,我们将拥有其他更高级的服务,这些服务将利用这些数据服务。 例如,我们可能有一个Orchestration Service ,它结合了各种聚合。 下面的示例描述了电子商务网站中的一项服务,该服务通过以下步骤汇总了在线订单:

  • exposes a GET API that takes an order GUID

    公开需要订单GUID的GET API

  • retrieves the Order Aggregate represented by that GUID

    检索该GUID表示的订单汇总
  • extracts the GUIDs of the buyer and seller (which are both Users)

    提取买主和卖主(均为用户)的GUID
  • retrieves the User Aggregates representing the buyer and the seller

    检索代表买方和卖方的用户集合
  • packages all of those Aggregates into high-level Order Details model and returns it to the caller

    将所有这些集合打包到高级“订单详细信息”模型中,并将其返回给调用方
Image for post
An orchestration service stitches together calls to our various Data Services
编排服务将对各种数据服务的调用整合在一起

听起来不错。 但是如何查询我们的聚合呢? (Sounds great. But what about querying our Aggregates?)

One of the import requirements of Aggregates, which bears repeating, is that all access into the Aggregate must go through the root entity. So any request URL will need to start with something like/aggregate/{aggregate-identifier}Thus, if we provide read APIs to our Users Data Service, they will look something like this:

聚合的导入要求之一(需要重复)是,所有对聚合的访问都必须通过根实体。 因此,任何请求URL都必须以/aggregate/{aggregate-identifier}类的内容开头。因此,如果我们向Users Data Service提供读取的API,则它们将看起来像这样:

GET  /users/{guid}
GET /users/{guid}/phones
GET /users/{guid}/phones/{phoneId}

That means that we cannot provide APIs like the following:

这意味着我们无法提供以下API:

GET /users/phones/{phoneId}

Astute readers might have realized that we also cannot provide “search” APIs like the following:

精明的读者可能已经意识到,我们也无法提供如下所示的“搜索” API:

GET /users/?email=me@mydomain.com

On the surface, this looks like a big problem.

从表面上看,这似乎是一个大问题。

What if we do need to be able to look up users — not by their GUID — but by their email addresses?

如果我们 是否需要能够(不是通过GUID)而是通过电子邮件地址查找用户?

Or if we varied-up our Order example from above; what if our Orchestration Service instead accepted a User GUID, and then needed to search through Orders to find those with matching buyer or seller GUIDs?

或者,如果我们从上面修改了Order示例; 如果我们的协调服务改为接受用户GUID ,然后需要搜索订单以查找具有匹配的买方或卖方GUID的 订单怎么办?

More broadly, what if we need to locate any Aggregate by something other than its GUID? This is hardly an unusual need or an unreasonable ask. But does an aggregate-oriented architecture preclude this?

更广泛地说,如果我们需要通过GUID以外的其他内容来定位任何聚合,该怎么办? 这绝非罕见的需求,也不是不合理的要求。 但是面向聚集的体系结构是否可以排除这种情况?

解决方案:实施单独的索引服务 (Solution: Implement a separate indexing service)

The solution to this problem is deceptively simple. We will implement a separate service, which is optimized for indexing search terms. All “searches” — that is, all “GET” requests that query for anything other than an instance of our aggregate specified by its ID — will be performed against this Indexing Service.

这个问题的解决方案看似简单。 我们将实施一项单独的服务,该服务针对索引搜索词进行了优化。 将针对此索引服务执行所有“搜索”,即查询除其ID指定的聚合实例以外的任何其他内容的所有“ GET”请求。

Our Data Service would still be the originating source, or the system of record, for the Aggregate. All writes to our Aggregates (inserts, updates, deletes, etc) will happen through this service. The Indexing Service, on the other hand, will never be directly updated. Instead, it will be notified of these writes — typically through an asynchronous mechanism such as Kafka — and be kept eventually consistent.

我们的数据服务仍将是汇总的原始来源或记录系统。 通过此服务将对我们的聚合进行所有写入(插入,更新,删除等)。 另一方面,索引服务将永远不会直接更新。 取而代之的是,通常通过诸如Kafka之类的异步机制将这些写入通知给它们,并最终保持一致。

Image for post
All updates — and get-by-GUID operations — would go through the Data Service. Freeform searches would be handled by our Indexing Service. Note that in reality, we might split the “Indexing Service” into two separate services: one that consumes Kafka messages and updates the index, and one that facilitates searches.
所有更新以及GUID获取操作都将通过数据服务进行。 自由格式搜索将由我们的索引服务处理。 请注意,实际上,我们可能会将“索引服务”分为两个单独的服务:一个使用Kafka消息并更新索引,而另一个则方便搜索。

Let’s revisit the find-orders-by-user dilemma we’d presented earlier. Our orchestration service needs to be able to search for Orders in which the buyer’s GUID or the seller’s GUID matches the given User GUID.

让我们回顾一下前面介绍的按用户查找顺序的难题。 我们的编排服务需要能够搜索与买方的GUID或卖方的GUID匹配给定用户GUID的订单。

To support this, we will introduce an Order Indexing Service. This service will be notified by the Order Data Service of any additions (or updates, if we allow updates to existing orders). It will then maintain an index of buyer GUIDs and seller GUIDs, which map back to their associated Orders. The Orchestration Service, then, will query the Order Indexing Service.

为此,我们将引入订单索引服务。 订单数据服务将向该服务通知任何添加(或更新,如果我们允许更新现有订单)。 然后,它将维护买方GUID和卖方GUID的索引,这些索引映射回它们的关联订单。 然后,业务流程服务将查询订单索引服务。

What will the Order Indexing Service return, assuming it finds matching Orders? We have a couple of options.

假设找到匹配的订单,订单索引服务将返回什么? 我们有两种选择。

  1. Return the GUIDs of the matching Orders. Our Indexing Service might not return Order objects themselves, but simply the GUIDs of the matching Orders. The caller (in this case, the Order Details Orchestration Service) would be responsible for calling the Order Data Service to retrieve the details of the matching orders.

    返回匹配订单的GUID。 我们的索引服务可能不会返回Order对象本身,而只会返回匹配的Orders的GUID。 调用者(在这种情况下,是“订单详细信息编排服务”)将负责调用“订单数据服务”以检索匹配订单的详细信息。

    The main benefit of this approach is that the orchestration service will always be retrieving the current representation of the Orders. Recall that by design, the indexing service will be eventually-consistent, so the details that it returns may not be up-to-date.

    这种方法的主要好处是编排服务将始终在检索订单的当前表示形式。 回想一下,按设计,索引服务将最终保持一致,因此它返回的详细信息可能不是最新的。

    An additional benefit is that the amount of data pushed between the Indexing Service and the Orchestration Service is dramatically reduced.

    另一个好处是,索引服务和业务流程服务之间推送的数据量大大减少了。

    The downside is obvious: additional calls that the orchestration service must make. Depending on the number of matching Orders, this could result in a large number of calls to the Order Details service.

    缺点很明显:编排服务必须进行的其他调用。 根据匹配的订单数量,这可能导致对“订单详细信息”服务的大量调用。

  2. Return rich representations of the matching Orders. Our Indexing Service might instead return the entire Order objects.

    返回匹配的订单的丰富表示。 我们的索引服务可能会返回整个Order对象。

    The pros and cons are, of course, the inverse of the previous approach. Here, more data will be returned by the Indexing Service, but the Orchestration Service will not need to make additional calls to the Data Service. However, the data returned by the Orchestration Service might be out-of-date.

    优缺点当然是先前方法的反面。 在这里,索引服务将返回更多数据,但是编排服务将不需要对数据服务进行其他调用。 但是,业务流程服务返回的数据可能已过期。

Which approach to favor depends on our specific design. For example, if our data is append-only — that is, not updated once created — then we need not worry about out-of-sync data. In that case, we might favor option 2.

哪种支持方法取决于我们的特定设计。 例如,如果我们的数据是仅追加的(即创建后就不会更新),那么我们就不必担心数据不同步。 在这种情况下,我们可能赞成选项2。

Moreover, if this is the case, then we may decide to take our design even further, by requiring all read operations to go through our Indexing Service. In this case, we have an even more clear separation between the Data Service and the Indexing Service.

而且,如果是这种情况,那么我们可能会决定通过要求所有读取操作都通过我们的索引服务来进一步扩展我们的设计。 在这种情况下,我们在数据服务和索引服务之间有着更加清晰的分离。

摘要 (Summary)

Earlier, we presented the visual representation of Aggregates and microservices depicted on the left, below. When searching becomes necessary, our mental model should be adjusted to the representation on the right.

之前,我们在下面的左侧展示了聚合和微服务的可视化表示。 当搜索成为必要时,我们的思维模式应调整为右侧的表示。

Image for post

That’s a general overview, of course. Our actual details can differ. For example, we may create separate Indexing and Search Services. Or we might use a different mechanism than Kafka to keep the systems in sync.

当然,这是一个总体概述。 我们的实际细节可能有所不同。 例如,我们可能创建单独的索引和搜索服务。 或者,我们可以使用与Kafka不同的机制来保持系统同步。

But the overall idea remains the same. To support an Aggregate-oriented architecture along with search capabilities, we should bundle our Data Services with companion Indexing Services to support searching.

但是总体思路保持不变。 为了支持面向聚合的体系结构以及搜索功能,我们应该将数据服务与配套的索引服务捆绑在一起以支持搜索。

Gain Access to Expert View — Subscribe to DDI Intel

获得访问专家视图的权限- 订阅DDI Intel

翻译自: https://medium.com/datadriveninvestor/are-aggregate-oriented-microservices-a-dead-end-6651db494f0e

面向对象中关键聚合组合

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值