nats-io / nats-architecture-and-design

Architecture and Design Docs
Apache License 2.0
177 stars 20 forks source link

Update Service API ADR with multi-endpoints details #186

Closed piotrpio closed 1 year ago

aricart commented 1 year ago

Possibly the compromise is config for the handler can take a {root_subject: string, endpoints?: NamedEndpoints[]} or a {subject: string, handler: ()=>{}} If you use the first, on some clients now it becomes possible to do addEndpoint(), if the second config is used, addEndpoint() is an illegal operation - the only problem is developer will have to refactor logic and subjects to support multiple endpoints if they started with the single endpoint.

aricart commented 1 year ago

The easiest solution may be that the root_subject is an optional field. This allows for the simple case and complex to work without much change. This also enables a "fat" service that listens on foo and bar. One thing to consider from talking to @Jarema is that if we wanted to group subjects - so the child subjects have a shared root, it becomes possible to do, and we may even want to have the ability to add grandchildren - ie the config for the endpoint is actually a parent for other configs.

aricart commented 1 year ago

To summarize the changes:

Also should be obvious, but groups can contain * but no >

scottf commented 1 year ago

What about just getting rid of root subject? Your example already allows an endpoint without a group. What do you think about "path" instead of "group"?

const srv = addService(name:"name")...
srv.addEndpoint("blah", blahHandler);
srv.addPath("control").addEndpoint("stop", stopHandler);
srv.addPath("v1").addPath("accounts").addEndpoint("list", listHandler);
srv.addPath("v1").addPath("accounts").addEndpoint("delete", deleteHandler):

info subjects are then

"subjects":["blah", "control.stop", "v1.accounts.list" "v1.accounts.delete"]

stats are like this. Something to consider is how stats data is provided. Since we are now tracking stats per endpoint, each Endpoint Stats has/had it's own "data". Or should we move data up, out of the endpoint {} and into the stats {}

{
    "type": "..."
    "name": "name"
    "id": id"
    "version": 1.2.3"
    "started": "..."
    "data": ?
    "endpoints": {
        "blah": {...}
        "control.stop": {...}
        "v1.accounts.list": {...}
        "v1.accounts.delete": {...}
    }
}
aricart commented 1 year ago

@scottf I think group is better term than path here. Also the thing that you are pointing out is that if you have group, the need for root subject is a moot point. Because since a group can have a name or not, it will act accordingly.

The other benefit to not having that configuration is that the config could now have an endpoint just like it did before and behave correctly - albeit it now becomes optional.

scottf commented 1 year ago
  1. Maybe all endpoints need a unique name - I just learned that we are expected to allow multiple endpoints of the same subject, so we need something unique. It could be an id I suppose that we assign and the dev can see.

  2. Do we need the whole group thing? Why can't we just

const srv = addService(name:"name")...
srv.addEndpoint("blahName", "blahSubject", blahHandler, <optional> blahStatsDataHandler);
srv.addEndpoint("stop", "control.stop", stopHandler, <optional> stopStatsDataHandler);
srv.addEndpoint("list account", "v1.accounts.list", listHandler);
srv.addEndpoint("delete account", "v1.accounts.delete", deleteHandler);
aricart commented 1 year ago
  1. Maybe all endpoints need a unique name - I just learned that we are expected to allow multiple endpoints of the same subject, so we need something unique. It could be an id I suppose that we assign and the dev can see.
  2. Do we need the whole group thing? Why can't we just
const srv = addService(name:"name")...
srv.addEndpoint("blahName", "blahSubject", blahHandler, <optional> blahStatsDataHandler);
srv.addEndpoint("stop", "control.stop", stopHandler, <optional> stopStatsDataHandler);
srv.addEndpoint("list account", "v1.accounts.list", listHandler);
srv.addEndpoint("delete account", "v1.accounts.delete", deleteHandler);

not sure that we need it to be honest - the stats will report the name they gave and the subject calculated - so if these differ, they will be alongside.

Now to the point of a stats handler, if you do multiple addEndpoint() with the same name or subject, the stats handler won't be able to differentiate between them, but possibly it doesn't need to. If you want to know how many requests went to hello world and you have that sort of topology, then providing the aggregate doesn't seem wrong.

Scroll up to see what they look like.