简体   繁体   中英

TSyringe and TypeORM importing Database dependencies BEFORE other dependencies

I'm facing one problem, in which I have lots of Repositories(which they are TS Classes that implements one interfaces, they are typeorm repositories class) and I use TSyringe to handle all my dependencies in my API

Here is an example of one repository

import { getRepository, Repository } from 'typeorm';
import SafeUsd from '@modules/currencies/infra/typeorm/entities/SafeUsd';
import ICreateSafeUsdDTO from '@modules/currencies/dtos/ICreateSafeUsdDTO';
import ISafeUsdRepository from '@modules/currencies/repositories/ISafeUsdRepository';
import { classToClass } from 'class-transformer';
import IUpdateSafeUsdDTO from '@modules/currencies/dtos/IUpdateSafeUsdDTO';

export default class SafeUsdRepository implements ISafeUsdRepository {
  private ormRepository: Repository<SafeUsd>;

  constructor() {
    this.ormRepository = getRepository(SafeUsd);
  }

  async create(data: ICreateSafeUsdDTO): Promise<SafeUsd> {
    const safeUsd = this.ormRepository.create(data);

    await this.ormRepository.save(safeUsd);

    return classToClass(safeUsd);
  }

  async update({
    safeUsd,
    value,
    acceptable_variance,
    margin,
  }: IUpdateSafeUsdDTO): Promise<SafeUsd> {
    if (margin !== undefined) {
      safeUsd.margin = margin;
    }

    if (value !== undefined) {
      safeUsd.value = value;
    }

    if (acceptable_variance !== undefined) {
      safeUsd.acceptable_variance = acceptable_variance;
    }

    await this.ormRepository.save(safeUsd);

    return classToClass(safeUsd);
  }

  async findOne(): Promise<SafeUsd | undefined> {
    const safeUsd = await this.ormRepository.findOne();

    return classToClass(safeUsd);
  }
}

After that, I register all my repositories in one file, inside "shared/container/repositories/index.ts"

import { container } from 'tsyringe';

import ICurrenciesRepository from '@modules/currencies/repositories/ICurrenciesRepository';
import CurrenciesRepository from '@modules/currencies/infra/typeorm/repositories/CurrenciesRepository';

import ICurrenciesUrlRepository from '@modules/currencies/repositories/ICurrenciesUrlRepository';
import CurrenciesUrlRepository from '@modules/currencies/infra/typeorm/repositories/CurrenciesUrlRepository';
import ISafeUsdRepository from '@modules/currencies/repositories/ISafeUsdRepository';
import SafeUsdRepository from '@modules/currencies/infra/typeorm/repositories/SafeUsdRepository';

container.registerSingleton<ICurrenciesRepository>(
  'CurrenciesRepository',
  CurrenciesRepository,
);

container.registerSingleton<ICurrenciesUrlRepository>(
  'CurrenciesUrlRepository',
  CurrenciesUrlRepository,
);

container.registerSingleton<ISafeUsdRepository>(
  'SafeUsdRepository',
  SafeUsdRepository,
);

In my app I also have some providers(which are basically implementations of third-partner APIs, such as AWS, GoogleFirebase, and so on...)

What I want to do is to inject some of my Repositories inside my providers, then I want to register their instances resolved by the TSyringe container.

The problem start when I try to do what I've just wrote above. When registering both my Repositories and Providers with the TSyring container, I do:

import './repositories';
import './providers';

Which are both a lot of TS files that does the TSyringe container.registerSingleton, .resolve or .registerInstance methods.

I always get this error:

Error: Cannot inject the dependency at position #2 of "BitfinexBrokerProvider" constructor.
Reason:No repository for "Currency" was found. Looks like this entity is not registered in current "default" connection?

This error, means that my Repositories haven't been up yet, so when TSyringe tries to inject them at my Providers, they heven't stablished connection with the database yet, so I get this error. If I don't inject those repositories inside my providers, everything works.

Any ideas on how to solve that? What I would like to do would be something like:

  1. First resolve and register ALL repositories singletons, so they will connect with the DB.
  2. Only after that, register all providers.

Any ideas on how to accomplish that? I hope I've made myself clear through my problem, Ask me if you guys have any doubt, I will edit the post trying to be more clear.

Thanks in advance.

UPDATE: I've tried to add all TSyringe .registerSingleton() || .registerInstance() || .resolve(), inside one "setTimeout(() => {}, 3000)" and it worked! As I said in my problem, I just needed to wait a little so the DB connection is stablished on my repositories, before injecting on the providers.

here you can see what I did

import { container } from 'tsyringe';

import BitfinexBrokerProvider from './implementations/BitfinexBrokerProvider';
import BrokerHandlerProvider from './implementations/BrokerHandlerProvider';
import FlowBTCBrokerProvider from './implementations/FlowBTCBrokerProvider';
import BitfinexChannelHandler from './implementations/websocket/bitfinex/ChannelHandler';

import IBrokerHandlerProvider from './models/IBrokerHandlerProvider';
import IBrokerProvider from './models/IBrokerProvider';
import IChannelHandler from './models/IChannelHandler';

setTimeout(() => {
  container.registerSingleton<IBrokerProvider>(
    'FlowBTCBrokerProvider',
    FlowBTCBrokerProvider,
  );

  container.registerSingleton<IChannelHandler>(
    'BitfinexChannelHandler',
    BitfinexChannelHandler,
  );

  const bitfinexBrokerProvider = container.resolve(BitfinexBrokerProvider);

  container.registerInstance<IBrokerProvider>(
    'BitfinexBrokerProvider',
    bitfinexBrokerProvider,
  );

  const brokerHandlerProvider = container.resolve(BrokerHandlerProvider);

  container.registerInstance<IBrokerHandlerProvider>(
    'BrokerProvider',
    brokerHandlerProvider,
  );
}, 3000);

Although it worked, I found this solution very unpleasant and not elegant. Is there a better way to achieve a better solution?

You can use "delay" helper func inside of TSyringe. More details here

Example:

@injectable()
export class Foo {
  constructor(@inject(delay(() => Bar)) public bar: Bar) {}
}

@injectable()
export class Bar {
  constructor(@inject(delay(() => Foo)) public foo: Foo) {}
}

// construction of foo is possible
const foo = container.resolve(Foo);

// property bar will hold a proxy that looks and acts as a real Bar instance.
foo.bar instanceof Bar; // true

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM