DEV Community

Tu Bui
Tu Bui

Posted on • Edited on

1 1 1 1 1

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

AWS Security LIVE!

Tune in for AWS Security LIVE!

Join AWS Security LIVE! for expert insights and actionable tips to protect your organization and keep security teams prepared.

Learn More

Top comments (0)

AWS Security LIVE!

Tune in for AWS Security LIVE!

Join AWS Security LIVE! for expert insights and actionable tips to protect your organization and keep security teams prepared.

Learn More

👋 Kindness is contagious

Explore a sea of insights with this enlightening post, highly esteemed within the nurturing DEV Community. Coders of all stripes are invited to participate and contribute to our shared knowledge.

Expressing gratitude with a simple "thank you" can make a big impact. Leave your thanks in the comments!

On DEV, exchanging ideas smooths our way and strengthens our community bonds. Found this useful? A quick note of thanks to the author can mean a lot.

Okay