Search code examples
typescriptinversifyjs

Inversify correct way to inject dependencies on many domain classes


I'm having troubles figuring out how to handle dependencies and injections on a rest web service made with typescript. I'm trying to avoid depending on inversify on my domain classes following dependency inversion principle. This is the project structure so far:

core/ (domain classes)
expressjs/ (web service context)
inversify/ (the injection magic for my domain classes should happen here)
other-modules/ (concrete interface implementations on 3rd party techs)

This is an example on how my classes looks like:

interface DomainInterface {
    foo(): void;
}

interface DomainService {
    bar();
}

class ConcreteClass implements DomainInterface {
    constructor(colaborator: DomainService) { }

    foo() {
        this.colaborator.bar();
        ...
    }
}

Now i want to inject all dependencies through inversify, but i don't want to modify all my domain classes to make them injectables through @injectable decorator.

One thing i though was doing a class that contains the @injectable dependency on inversify module which inherits for each domain class i need to be injected. For example:

@injectable()
class InverisfyConcreteClass extends ConcreteClass {
    constructor(@inject(DomainService) colaborator: DomainService) {
        super(colaborator);
    }
}

But this leads me to the problem that i have a lot of domain classes, and would be insane to create that many classes.

The other approach was to create a 'Context' class which contains the reference to all classes, binds them to a container and retrieve them when needed:

class InversifyInjectionContext {
    container: Container;

    bind() {
        // bind all needed instances somehow (??)
    }

    concreteClass() {
        return container.get<ConcreteClass>();
    }

    concreteDomainService() {
        return container.get<AnyConcreteDomainService>();
    }
}

The problem now is that i can't figure out how could i create instances and register them correctly in inversify container, so i could retrieve them after at the application.

What would be the best approach to solve this?


Solution

  • I finally solved it by decorating each class in runtime:

    InversifyContext {
        container: Container;
    
        bindConcreteClass() {
            decorate(injectable(), InverisfyConcreteClass);
            decorate(inject("ColaboratorDomainService"), InverisfyConcreteClass, 0);
            this.container.bind("InverisfyConcreteClass").to(DomainInterface);
        }
    
        bindColaboratorDomainService() {
            decorate(injectable(), ColaboratorDomainService);
            this.container.bind("ColaboratorDomainService").to(DomainService);
        }
    }
    

    This way i avoided to depend on a specific injection tech for any domain class leaving them clean.