vovaspace/brandi

Accept token factories as arguments to `injected`

smithki opened this issue · 2 comments

I'm interested in using a pattern like this for injected services:

import { injected, token } from 'brandi';

class MyService {
  static token = token<MyService>('MyService');

  // ...
}

injected(MyService, /* ... */);

so that token mappings are co-located with their relevant service implementations. But, there could be an issue if I introduce circular dependencies... for example:

// ./name.service.ts
import { injected, token } from 'brandi';
import { Logger } from './logger.service';

class NameService {
  static token = token<NameService>('NameService');

  constructor(private readonly log: Logger) {}

  async getName() {
    this.log.debug('called NameService.getName()');
    return 'Sally';
  }
}

injected(NameService, Logger.token);

// ./logger.service.ts
import { injected, token } from 'brandi';
import { NameService } from './name.service';

class Logger {
  static token = token<Logger>('Logger');

  constructor(private readonly nameSvc: NameService) {}

  async sayHello() {
    const name = await this.nameSvc.getName();
    console.log(`Hello, my name is ${name}!`);
  }

  debug(message: string) {
    console.log('[debug]', message);
  }
}

injected(Logger, NameService.token);

I think this could be resolved by allowing tokens to be provided as functions, like:

injected(MyService, () => MyOtherService.token, ...);

Then the token could be fully resolved when container.get() is called.

Hi!

It’s clear what the problem is but it seems that the problem can be solved in a different way. You can move the token to a separate file like tokens.ts or MyService.token.ts.

Brandi is also design to be used on the frontend, and the way you suggested provokes extra code entering the bundle when you import MyService for the token, but do not actually use the class implementation.

Oh wait… I didn't see that you want to use circular dependencies.

Thus it is more complex problem. Moving tokens doesn’t resolve that. And I guess your way won't resolve that too.

Something like Inversify's lazy identifiers is needed here. But it would be better to just avoid circular dependencies.