Skip to content

Instantly share code, notes, and snippets.

@resistdesign
Last active June 13, 2017 18:36
Show Gist options
  • Select an option

  • Save resistdesign/683c75bd9f9c79973aac2c6d66b137ee to your computer and use it in GitHub Desktop.

Select an option

Save resistdesign/683c75bd9f9c79973aac2c6d66b137ee to your computer and use it in GitHub Desktop.
Incarnate Server Example
// Stub to stop Babel REPL from bitching ;)
class Incarnate {
async resolvePath(){}
}
// Map dependencies. Use nested modules by returning sub-maps from functions.
// The process fully supports async but keeps everything in order of dependencies when resolving.
// Sub-dependencies (args) are aquired in parallel for the quickest result.
// Modules and "connecting everything via string names" are both decoupling techniques that will aid in smooth parallel development.
const depMap = {
'handlers': () => {
return {
'services': () => {
return {
'config': () => {
return {
'db': {
args: [],
factory: () => {
return {
url: 'wha',
user: 'tev',
password: 'er'
};
}
}
};
},
'userContext': {
args: [
// Depend on something from the current request or global context.
ctx => ctx.token
],
factory: (token) => {
// Use the token if it's supplied on the Incarnate context (ctx) object, or...
// Return that fake context thing we always need :P
// Although, I'm sure we can figure out a better way to get this info once we're using dependency management.
const request = {get: fake};
injectServices(request, null, fake);
return request.context;
}
},
'bindle': {
args: [],
factory: async () => {
return await callOutToTheBindle(); // Or some other service or whatever we want for cross-micro-service access.
}
},
'contact': {
args: [
'userContext',
'config.db',
'bindle'
],
// `factory` functions (and everything else) support async, if you need it.
factory: async (userContext, dbConfig, someBindleShitYo) => {
// Instantiate the service with the user context so that credentials can be utilized.
return new ContactService(userContext, dbConfig, someBindleShitYo.programs);
}
}
};
},
'contact': {
args: [
'services.contact' // ,
// ctx => ctx.body // Optionally, get the conact from request body if the request is used as the context.
],
factory: (contactService /*, contact*/) => {
// The contact could optionally be resolved from the request context ^
// It depends on how caching is being used and how we want to pass data. See getHandler below.
return async (contact) => {
return await contactService.create(contact);
};
}
}
};
}
};
const inc = new Incarnate(depMap);
// Dynamically get a handler from a resource name that could come from a route path. (i.e. Using middleware or something.)
async function getHandler(resourceName, requestData = undefined){
// Some requestData could be passed as the CURRENT execution context data. This would ignore cached values.
const contactHandler = await inc.resolvePath(`handlers.${resourceName}`, requestData);
// -- Optionally, requestData could be set as the global Incarnate instance context and caching will be used.
// inc.context = requestData; // Or create a completely new instance of Incarnate as needed.
// const contactHandler = await inc.resolvePath(`handlers.${resourceName}`);
// -- Or no request data, and the values can just be passed to the resolved handler function.
// const contactHandler = await inc.resolvePath(`handlers.${resourceName}`);
return contactHandler;
}
// There is more, like invalidating a cached value and listening for invalidation.
// Invalidation features are to support the life cycles of long lived applications.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment