moleculerjs / moleculer

:rocket: Progressive microservices framework for Node.js
https://moleculer.services/
MIT License
6.11k stars 580 forks source link

how to approach mutually exclusive dependencies #1026

Closed OutdatedVersion closed 1 year ago

OutdatedVersion commented 2 years ago

I'm having trouble finding a supported way to do mutually exclusive dependencies with Moleculer's built-in service dependency API.

For example, the snippet below has two implicit, unasserted, dependencies (v2.service and v1.service) -- it would be nice to have more confidence that at least one of those is available at runtime.

const things = await ctx.call(
 'v2.service.getThings',
 { active: true },
 {
    fallbackResponse: () => {
       return ctx.call('v1.service.getThings', { active: true });
    },
  }
);

I, without looking at the code/examples, tried a dependencies of ['service'] and [{ name: 'service' }] thinking Moleculer may do some sort of resolution to match the name... with no luck. From looking around, I believe support would require a Moleculer change.

Something like:

{
   dependencies: [
     { or: ['v1.service', 'v2.service']  }
   ],
}

could facilitate some special logic in the block below for "one or the other" searches https://github.com/moleculerjs/moleculer/blob/b71bd71dc2375a72d32c25e2718b4b36a8b57f60/src/service-broker.js#L1022-L1027

It seems best for members of the or special case to support the full set of version keys (shorthand with/without version, object, etc.). Though, I believe that would require enriching the Registry#hasService API to do partial matches. Not really sure on that one.

Some questions:

icebob commented 2 years ago

You can't do that currently with dependencies. But the idea is good, but I suggest other solution than or: [] because I think there is no real use-case that somebody wants to wait for different services with or. My suggestion is that version can be an array, like:

{
   dependencies: [
     { name: "myService", version: [1, 2] }
   ],
}
icebob commented 2 years ago

Current workaround if you don't use the dependencies in the schema, instead use the waitForServices in started. E.g.

{
    name: "otherService",

    started() {
        while (true) {
            try {
                await this.waitForServices("v1.myService", 1000);
                break;
            } catch(err) {
                try {
                    await this.waitForServices("v2.myService", 1000);
                    break;
                } catch(err) {
                    // Wait again
                }
            }
        }
    }
}