We do this occasionally. We use config files (XML formatted, but could be JSON or properties too) instead of a DB table.
The assumption is that each service that would be invoked has the same signature – same inputs and outputs. You could define a Specification object to help guide that.
First thing you likely want: a helper service to use com.wm.app.b2b.server.Service.doInvoke(). You may already have one. If not, here is one you may find helpful:
EDIT: I forgot about pub.flow:invokeService. Old habits I guess. I don’t recall when that became available but we’ve used our own for so long, I forgot that the built-in service exists. 
public static final void invokeServiceEx(IData pipeline) throws ServiceException {
IDataCursor idc = pipeline.getCursor();
String folder = IDataUtil.getString(idc, "folder");
String service = IDataUtil.getString(idc, "service");
boolean thread = IDataUtil.getBoolean(idc, "thread");
com.wm.lang.ns.NSName svc;
if( ((folder==null) || (folder.length() == 0)) && (service.length() > 0) )
svc = com.wm.lang.ns.NSName.create(service);
else
svc = com.wm.lang.ns.NSName.create(folder,service);
try
{
//Invoke the service
if(thread)
{
java.util.Date dt = new java.util.Date();
java.util.Random z = new java.util.Random();
String name = String.valueOf(z.nextInt());
z.setSeed(dt.getTime());
Service.doThreadInvoke(svc, new com.wm.app.b2b.server.Session(name), IDataUtil.clone(pipeline) );
}
else
{
Service.doInvoke(svc, pipeline);
}
}
catch(Exception ex)
{
throw new ServiceException(ex);
}
finally
{
idc.destroy();
}
}
With the above, then we create a helper service that exposes the same inputs/outputs as the dynamically called services. The job of this service is to accept the name of the service to invoke along with inputs, call the service using the invokeServiceEx above, then return the results. We usually use scoping to limit the pipeline.
For example, say you have several “map” services that translate a document to another. You want to call any of them. Say they take an input of “sourceDocument” and return an output of “targetDocument”. The helper would be:
Service: invokeMapService
Inputs: sourceDocument, service
Outputs: targetDocument
Body:
…MAP – map the 2 inputs to a temp document named “__invokeScope”
…call inovokeServiceEx – set Scope to __invokeScope; this limits the pipeline for the invoked map service
…MAP – map the output from __invokeScope to the output var of the helper service
Then in your main service where you’re determining which service to invoke based upon the DB look up, call this “invokeMapService”. It will get the outputs just as if it called the service directly.
Other side notes – be cautious if you decide to use global vars. Without a naming scheme and management plan, it can get out of control. I would also caution about using caching. IME, it has never been necessary/helpful and only introduces complexity. If performance measurements indicate more speed is needed, and using cache shows it provides the necessary speed increase, then by all means use it. But do not make assumptions about it being faster – my experience has been it has zero meaningful impact. But definitely a YMMV aspect.
HTH.
#webMethods#Integration-Server-and-ESB#webMethods-io-Integration#webMethods-cloud#Flow-and-Java-services