Build one MessagingBackend that spans every enabled provider.
Conversation and message ids leaving the composite are namespaced as
"<providerId>:<innerId>"; ids arriving from callers are split on the first
: to find the provider. Pagination cursors for conversations() are
namespaced the same way and are only produced when exactly one provider is
involved; with several providers the merged page carries no cursor, because
no single provider can resume it.
A send that names a conversation goes to that conversation's provider. A send
that names only recipients goes to the default; when that provider answers
unavailable:recipient-not-ready the next enabled one is tried, in order, and
the first refusal is reported when none can reach the person. Before a
one-to-one send leaves, the composite looks up which provider the
correspondent last wrote through; when it is not the planned one,
options.chooseReply decides, and a message moved to their provider is sent
to the person, not into the conversation that was named. A send that names a
provider is never second-guessed.
close() is a no-op: the registry that enabled the backends owns their
lifetime and closes them through MessagingRegistry.close(). Unsubscribing
a composite subscription skips providers that were disabled in the meantime,
since disabling already closed their backend.
Build one
MessagingBackendthat spans every enabled provider.Conversation and message ids leaving the composite are namespaced as
"<providerId>:<innerId>"; ids arriving from callers are split on the first:to find the provider. Pagination cursors forconversations()are namespaced the same way and are only produced when exactly one provider is involved; with several providers the merged page carries no cursor, because no single provider can resume it.A send that names a conversation goes to that conversation's provider. A send that names only recipients goes to the default; when that provider answers
unavailable:recipient-not-readythe next enabled one is tried, in order, and the first refusal is reported when none can reach the person. Before a one-to-one send leaves, the composite looks up which provider the correspondent last wrote through; when it is not the planned one,options.chooseReplydecides, and a message moved to their provider is sent to the person, not into the conversation that was named. A send that names aprovideris never second-guessed.close()is a no-op: the registry that enabled the backends owns their lifetime and closes them throughMessagingRegistry.close(). Unsubscribing a composite subscription skips providers that were disabled in the meantime, since disabling already closed their backend.