nicojs / typed-inject

Type safe dependency injection for TypeScript
Apache License 2.0
456 stars 23 forks source link

Can I use DI in a Factory Pattern? #70

Open besserwisser opened 1 month ago

besserwisser commented 1 month ago

Hey :) Thanks for the awesome library! I enjoy it.

Maybe it is my lack of knowledge, but I am trying to figure something out.

I am using a factory pattern (simplified the code)

export class RestaurantRepositoryFactory {
  createRestaurantRepository(restaurant: Restaurant): RestaurantRepository {
    switch (restaurant.name) {
      case 'The French Laundry':
        return new FrenchLaundryRepository(); // Error: Missing the logger
      case 'Noma':
        return new NomaRepository(); // Error: Missing the logger
      case 'Eleven Madison Park':
        return new ElevenMadisonParkRepository(); // Error: Missing the logger
      default:
        throw new Error('Unknown restaurant');
    }
  }
}

Some example Repository here:

export class FrenchLaundryRepository {
  public static inject = [DI_TYPES.Logger] as const
  constructor(public logger: Logger) {}
  name = 'The French Laundry';
}

export class NomaRepository {
  public static inject = [DI_TYPES.Logger] as const
  constructor(public logger: Logger) {}
  name = 'Noma';
}

export class ElevenMadisonParkRepository {
  public static inject = [DI_TYPES.Logger] as const
  constructor(public logger: Logger) {}
  name = 'Eleven Madison Park';
}

Now, this doesn't work obviously, because I am creating instances without passing the logger constructor argument. But also I don't want to pass the logger as argument! I want it to be provided by the DI Container. I guess something I could do is to register all three repository implementations with the injector. And retrieve the correct one in the repository instead of instanciating in the factory. But than it is not really a factory anymore :D

Or is there a another way to do this without having to register the implementations in the container?

Thank you :)

besserwisser commented 1 month ago

For know I will register all 3 repositories with the container and resolve them in the factory:

export class RestaurantRepositoryFactory {
  createRestaurantRepository(restaurant: Restaurant): RestaurantRepository {
    switch (restaurant.name) {
      case 'The French Laundry':
        return rootInjector.resolve(DI_TYPES.FrenchLaundryRepository);
      case 'Noma':
        return rootInjector.resolve(DI_TYPES.NormaRepository);
      case 'Eleven Madison Park':
        return rootInjector.resolve(DI_TYPES.ElevenMadisonParkRepository);
      default:
        throw new Error('Unknown restaurant');
    }
  }
}

I am very curious, if you have other suggestions.