Almost one year ago, I talked about AsyncAPI 2.6
and
how confusing its publish
and subscribe
operations can be in my Understanding
AsyncAPI’s publish & subscribe semantics with an
example post.
Since then, a new 3.0
version of AsyncAPI has been released with breaking changes
and a totally new send
and receive
operations.
In this blog post, I want to revisit the example from last year and show how to
rewrite it for AsyncAPI 3.0
with the new send
and receive
operations.
AsyncAPI 3.0
AsyncAPI 3.0
was released in December 2023. Since it’s a major version, it
has some breaking changes. These two pages does a good job explaining the
changes and the rationale behind them:
I won’t go through all the changes. For me, the biggest change is the separation
of operations and channels, and changing publish
and subscribe
operations to
send
and receive
.
Recap: Publish and subscribe operations in AsyncAPI 2.6
As a recap, AsyncAPI 2.6
has the following publish
and subscribe
operations with these semantics:
AsyncAPI Term | From Server Perspective | From User Perspective |
---|---|---|
Publish | Server receives the message | User publishes messages to the server |
Subscribe | Server sends the message | User subscribes for messages from the server |
In 2.6
, publish
and subscribe
operations are from user’s perspective.
New: Send and receive operations in AsyncAPI 3.0
In 3.0
, the publish
and subscribe
operations are replaced with
send
and receive
operations. In Migrating to
v3 page, the rationale is
given as follows:
In v2, the publish and subscribe operations consistently caused confusion, even among those familiar with the intricacies.
When you specified publish, it implied that others could publish to this channel since your application subscribed to it. Conversely, subscribe meant that others could subscribe because your application was the one publishing.
In v3, these operations have been entirely replaced with an action property that clearly indicates what your application does. That eliminates ambiguities related to other parties or differing perspectives.
While I agree that publish
and subscribe
were confusing, I’m not sure if
send
and receive
are less confusing. You still need to talk about whose
perspective when defining these operations. AsyncAPI docs talk about
application but that’s not clear either. Does application refer to the code
sending the message (user) or the code receiving the message (server)?
In Async 3.0
, send
and receive
operations are from server’s
perspective. An example will clarify.
Account and Email Services
Let’s revisit our example from last year. You have two microservices:
Account Service
emits an userSignedUp
event and Email Service
receives that event:
How do you define such an architecture in AsyncAPI 2.6
vs 3.0
?
Account Service
For Account Service
, in 2.6
, you had to define a channel with subscribe
operation because the user has to subscribe to receive messages:
asyncapi: 2.6.0
channels:
user/signedup:
subscribe:
operationId: publishUserSignedUp
message:
$ref: '#/components/messages/userSignedUp'
In 3.0
, the channel does not have an operation anymore. Instead, there’s a new
publishUserSignedUp
operation with send
action that refers to the channel.
This is because the server sends the message:
asyncapi: 3.0.0
channels:
user/signedup:
address: user/signedup
messages:
publishUserSignedUp.message:
$ref: '#/components/messages/userSignedUp'
operations:
publishUserSignedUp:
action: send
channel:
$ref: '#/channels/user~1signedup'
messages:
- $ref: '#/channels/user~1signedup/messages/publishUserSignedUp.message'
Email Service
Similarly, in Email Service
, in 2.6
, you had to define a publish
operation
because the user had to publish a message to the server:
asyncapi: 2.6.0
channels:
user/signedup:
publish:
operationId: receiveUserSignedUp
message:
$ref : '#/components/messages/userSignedUp'
However, in 3.0
, the server receives a message, so the operation has
receive
action:
asyncapi: 3.0.0
channels:
user/signedup:
address: user/signedup
messages:
receiveUserSignedUp.message:
$ref: '#/components/messages/userSignedUp'
operations:
receiveUserSignedUp:
action: receive
channel:
$ref: '#/channels/user~1signedup'
messages:
- $ref: '#/channels/user~1signedup/messages/receiveUserSignedUp.message'
In this blog post, I explored a small part of AsyncAPI and explained the
differences between 2.6
operations publish
and subscribe
and 3.0
operations send
and receive
. In a nutshell, in 2.6
, you need to think from
user’s perspective and in 3.0
in server’s perspective when you define these
operations.
If you’re interested in learning more, I have a talk on CloudEvents and AsyncAPI and a repo with some samples: