These changes affect breaking change in the protocol as well.
Summary
Currently, there is 2 payload fields in the communication packets, params / data and meta. The first mentioned stored user-defined data or user-input from one service to another service. The meta stored business logic data and transferred it to all services and vice-versa in a request chain.
However, sometimes the middlewares want to add new transferrable fields to the protocol (which reaches only the targetted service (in other words the lifetime is only the current request), but it's not available. It means, if we want to improve the e.g. tracing, timeout, retry middlewares, our hands are tied because every new field adding causes breaking change in the protocol and in the core module.
Why not add the new field to the ctx.params?
We can't because the params is processed by a user-created service, and not by the middlewares. I know, the middleware can wrap the localAction, get certain properties and remove them from params, but it's hacking.... not very beautiful.
Why not add the new field to the ctx.meta?
As I mentioned, the meta is transferred to all services in the chain and with the merged changes it comes back to the caller service. IF you want to add an extra tracing ID to the request, it won't be good, because other services will receive it, not just the target service of the request.
I suggest a new headers field in the communication in the REQ, EVENT and in the Context class. The functionality is the same as the HTTP request/response headers. It can contain predefined or custom key-value pairs.
This new field is able to improve the flexibility of the communication protocol. In the future, we can extend it without protocol breaking change.
Detailed design
The headers field is similar to meta it contains business logic data and not user-input data. So you can use it to bypass authentication/authorization, disable cache finding,
The headers will be available inside the services via ctx.headers. It means the developer can use it to transfer extra meta information to the target service. Therefore I suggest the (generally known) $ (dollar sign) to sign the internal header keys. So it won't collide with the user-defined header keys.
Protocol changes
We should move some existing middleware-used fields that are already defined in separated fields in the packets (e.g. timeout, stream, requestID, tracing information) into the headers.
There is some use-case where the parent tracing span comes from an external service (e.g. load balancer or browser). In this case, the parentSpanID exists and should be used. Currently, it's not easy, because the parent handling is inside the Context class. Therefore, it would be better to detach the Tracing middleware fields from the Context class and move the tracing fields in the REQ and EVENT into the new headers field. After that, anybody can write a custom tracing module and can transfer additional data via transporters (without any breaking change). It means
Move timeout fields into headers
The Timeout middleware uses the timeout field in the protocol. We should move it into the headers.
Add stream file upload extra fields to the headers
Currently it's a problem that we can't add extra information when sending streaming data to a service. With the headers it can be solved (hopefully).
Unresolved questions
What about caching keys?
Should we take into account the headers key-value pairs in the caching keys? If we allow the developer to use the headers in the services, they can use to send data which affects the results of the request. In this case, the cache key generator should able to read keys from the headers as well.
E.g.:
module.exports = {
actions: {
list: {
cache: {
keys: [
// From `ctx.params`
"limit",
"offset",
// From `ctx.meta`
"#tenantID",
// From `ctx.headers`
"@withPopulates"
]
}
}
}
}
These changes affect breaking change in the protocol as well.
Summary
Currently, there is 2 payload fields in the communication packets,
params
/data
andmeta
. The first mentioned stored user-defined data or user-input from one service to another service. Themeta
stored business logic data and transferred it to all services and vice-versa in a request chain. However, sometimes the middlewares want to add new transferrable fields to the protocol (which reaches only the targetted service (in other words the lifetime is only the current request), but it's not available. It means, if we want to improve the e.g. tracing, timeout, retry middlewares, our hands are tied because every new field adding causes breaking change in the protocol and in the core module.Why not add the new field to the
ctx.params
? We can't because theparams
is processed by a user-created service, and not by the middlewares. I know, the middleware can wrap thelocalAction
, get certain properties and remove them fromparams
, but it's hacking.... not very beautiful.Why not add the new field to the
ctx.meta
? As I mentioned, the meta is transferred to all services in the chain and with the merged changes it comes back to the caller service. IF you want to add an extra tracing ID to the request, it won't be good, because other services will receive it, not just the target service of the request.I suggest a new
headers
field in the communication in theREQ
,EVENT
and in theContext
class. The functionality is the same as the HTTP request/response headers. It can contain predefined or custom key-value pairs.This new field is able to improve the flexibility of the communication protocol. In the future, we can extend it without protocol breaking change.
Detailed design
The
headers
field is similar tometa
it contains business logic data and not user-input data. So you can use it to bypass authentication/authorization, disable cache finding,The
headers
will be available inside the services viactx.headers
. It means the developer can use it to transfer extra meta information to the target service. Therefore I suggest the (generally known)$
(dollar sign) to sign the internal header keys. So it won't collide with the user-defined header keys.Protocol changes
We should move some existing middleware-used fields that are already defined in separated fields in the packets (e.g. timeout, stream, requestID, tracing information) into the
headers
.Current REQ packet
New REQ packet
Current EVENT packet
New EVENT packet
Usage in service code as consumer
Usage in service code as producer
Usage in middleware
Further changes
Separated tracing information from context IDs
There is some use-case where the parent tracing span comes from an external service (e.g. load balancer or browser). In this case, the
parentSpanID
exists and should be used. Currently, it's not easy, because the parent handling is inside theContext
class. Therefore, it would be better to detach the Tracing middleware fields from theContext
class and move the tracing fields in theREQ
andEVENT
into the newheaders
field. After that, anybody can write a custom tracing module and can transfer additional data via transporters (without any breaking change). It meansMove timeout fields into
headers
The
Timeout
middleware uses thetimeout
field in the protocol. We should move it into theheaders
.Add stream file upload extra fields to the
headers
Currently it's a problem that we can't add extra information when sending streaming data to a service. With the
headers
it can be solved (hopefully).Unresolved questions
What about caching keys?
Should we take into account the headers key-value pairs in the caching keys? If we allow the developer to use the
headers
in the services, they can use to send data which affects the results of the request. In this case, the cache key generator should able to read keys from theheaders
as well.E.g.: