DEV Community

Tu Bui
Tu Bui

Posted on • Edited on

Safely restructure your codebase with Dependency Graphs

Using "inversify" is a key to create Deoencency-Graph-Binding

import { Container } from "inversify"; var container = new Container(); export { container }; 
Enter fullscreen mode Exit fullscreen mode
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 }; 
Enter fullscreen mode Exit fullscreen mode
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 }; 
Enter fullscreen mode Exit fullscreen mode
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); } } 
Enter fullscreen mode Exit fullscreen mode

Top comments (0)