Safely restructure your codebase with Dependency Graphs
Tu Bui
Posted on July 29, 2024
Using "inversify" is a key to create Deoencency-Graph-Binding
import { Container } from "inversify";
var container = new Container();
export { container };
import { interfaces, namedConstraint, taggedConstraint, traverseAncerstors, typeConstraint } from "inversify";
class BindingHelper
{
targetNamed(request: interfaces.Request, name: string | number | symbol): boolean
{
return request != null && request.target.matchesNamedTag(name.toString());
}
targetTagged(request: interfaces.Request, key: string | number | symbol, value: unknown): boolean
{
return request != null && request.target.matchesTag(key)(value);
}
targetIsDefault(request: interfaces.Request): boolean
{
return request != null && request.target != null && !request.target.isNamed() && !request.target.isTagged();
}
injectInto(request: interfaces.Request, parent: (NewableFunction | string)): boolean
{
return request != null && typeConstraint(parent)(request.parentRequest);
}
parentNamed(request: interfaces.Request, name: string | number | symbol): boolean
{
return request != null && namedConstraint(parent)(request.parentRequest);
}
parentTagged(request: interfaces.Request, tag: string | number | symbol, value: unknown): boolean
{
return request != null && taggedConstraint(tag)(value)(request.parentRequest);
}
anyAncestorIs(request: interfaces.Request, ancestor: (NewableFunction | string)): boolean
{
return traverseAncerstors(request, typeConstraint(ancestor));
}
noAncestorIs(request: interfaces.Request, ancestor: (NewableFunction | string)): boolean
{
return !traverseAncerstors(request, typeConstraint(ancestor));
}
anyAncestorTagged(request: interfaces.Request, tag: string | number | symbol, value: unknown): boolean
{
return traverseAncerstors(request, taggedConstraint(tag)(value));
}
noAncestorTagged(request: interfaces.Request, tag: string | number | symbol, value: unknown): boolean
{
return !traverseAncerstors(request, taggedConstraint(tag)(value));
}
anyAncestorNamed(request: interfaces.Request, name: string | number | symbol): boolean
{
return traverseAncerstors(request, namedConstraint(name));
}
noAncestorNamed(request: interfaces.Request, name: string | number | symbol): boolean
{
return !traverseAncerstors(request, namedConstraint(name));
}
}
var when = new BindingHelper();
export { when };
import { Container, interfaces } from "inversify";
import { when } from "./context-binding-helper";
interface DependencyBranch
{
from(type: any): DependencyGraphHelper;
}
class DependencyGraphHelper implements DependencyBranch
{
private container: Container;
private graphs: any[] = [];
private isBranching: boolean;
private branchNode: any;
bind(identifier: any, type: any, isConstant: boolean = false): DependencyGraphHelper
{
if (this.isBranching)
{
console.error('Please specify which class to be branched from. Parameters: ', identifier, ' ', type);
}
var parent = this.graphs[this.graphs.length - 1];
var node = { identifier: identifier, type: type, parent: parent, isConstant: isConstant };
this.graphs.push(node);
return this;
}
branch(identifier: any, type: any, isConstant: boolean = false): DependencyBranch
{
if (this.graphs.length == 0)
{
console.error('Dependency graph cannot start with a "branch". It must start with a "bind" function.Parameters: ', identifier, ' ', type);
}
if (this.isBranching)
{
console.error('Please specify which class to be branched from. Parameters: ', identifier, ' ', type);
}
this.isBranching = true;
this.branchNode = { identifier: identifier, type: type, isConstant: isConstant };
return this;
}
from(type: any): DependencyGraphHelper
{
if (this.isBranching == false)
{
console.error('A "from" function must be called after a "branch" function. Parameters: ', type);
}
this.isBranching = false;
var parent = this.graphs.filter(i => i.type == type).pop();
this.branchNode.parent = parent;
this.graphs.push(this.branchNode);
return this;
}
registerTo(container: Container): void
{
if (this.isBranching)
{
console.error('Please specify which class to be branched from before call "register" function.');
}
this.container = container;
for (let index = 0; index < this.graphs.length; index++)
{
const graph = this.graphs[index];
this.registerGraph(graph);
}
this.graphs = [];
this.container = null;
}
private registerGraph(graph: any): void
{
if (graph.isConstant)
{
this.bindToConstant(graph);
}
else
{
this.bindTo(graph);
}
}
private bindTo(graph: any): void
{
this.container.bind(graph.identifier).to(graph.type).when(request =>
{
return this.recursiveCheckBindingCondition(request, graph);
});
}
private bindToConstant(graph: any): void
{
this.container.bind(graph.identifier).toConstantValue(graph.type).when(request =>
{
return this.recursiveCheckBindingCondition(request, graph);
});
}
private recursiveCheckBindingCondition(request: interfaces.Request, graph: any): boolean
{
if (graph.parent == null)
{
return true;
}
var injected = when.injectInto(request, graph.parent.type) && this.recursiveCheckBindingCondition(request.parentRequest, graph.parent);
return injected;
}
}
var dependencyGraph = new DependencyGraphHelper();
export { dependencyGraph };
import { dependencyGraph } from "../../common/ioc-helper/dependency-graph-helper";
import { container } from "../../ioc/ioc-container";
import { MonkeyClimb } from "../monkey/monkey-climb";
import { MonkeyControl } from "../monkey/monkey-control";
import { MonkeyEat } from "../monkey/monkey-eat";
import { MonkeyFindFood } from "../monkey/monkey-find-food";
import { MonkeySwingThroughTheTree } from "../monkey/monkey-swing-through-the-tree";
import { MONKEY } from "./animal-ioc-config";
export default class DependenceGraphBinding
{
public register(): void
{
this.registerMonkey();
}
private registerMonkey(): void
{
dependencyGraph
.bind(MONKEY.CONTROL,MonkeyControl)
.branch(MONKEY.EAT,MonkeyEat).from(MonkeyControl)
.branch(MONKEY.FIND_FOOD,MonkeyFindFood).from(MonkeyControl)
.branch(MONKEY.FIND_FOOD,MonkeyClimb).from(MonkeyFindFood)
.branch(MONKEY.FIND_FOOD,MonkeySwingThroughTheTree).from(MonkeyFindFood)
.registerTo(container);
}
}
💖 💪 🙅 🚩
Tu Bui
Posted on July 29, 2024
Join Our Newsletter. No Spam, Only the good stuff.
Sign up to receive the latest update from our blog.