Is it possible to have a global singleton?


#1

I have a base class as follows where HttpClient and BusyService should be singletons global across the scope of the application:

export class BaseFetchService {
  constructor(private readonly baseApi: string, private readonly httpClient: HttpClient, private readonly busyService: BusyService) { }

  protected async _load<T extends IEntity>(id: string, action: string = "") {
    const url = action && action.length > 0 ? `${this.baseApi}/${action}/${id}` : `${this.baseApi}/${id}`;
    this.busyService.on();
    const data = await (await this.httpClient.fetch(url)).json();
    this.busyService.off();
    return data as T;
  }
etc...
}

I then inherit from the BaseFetchService in client-service.ts

@autoinject
export class ClientService extends BaseFetchService {
  constructor(private readonly clientService: ClientService, httpClient: HttpClient, busyService: BusyService) {
    super("client", httpClient, busyService);
  }
}

Is there some way to configure httpClient and busyService so that I can change the BaseFetchService to obtain the singleton instance of these classes so that I do not have to inject them into the constructor?

Something like …

export class BaseFetchService {
private httpClient: HttpClient;
private busyService: BusyService;

   constructor(private readonly baseApi: string){
    this.httpClient = get_instance_of_HttpClient;
    this.busyService = get_instance_of_BusyService;
   }
}

allowing me now to inherit ClientService as

export class ClientService extends BaseFetchService {
  constructor(private readonly clientService: ClientService) {
    super("client");
  }
}

Many thanks


#2

you could register and get them manually from the aurelia Container. not sure if this is what you’re after.


#3

Yes - I read that, but must confess that I didn’t understand it!

All the examples at the very bottom of the article are passing the singleton classes as parameters into the constructor, which is what I was hoping to avoid.

I was hoping that I could “new them up” in the body of the constructor.


#4

You can assume that Container.instance (the global / root Container) is setup properly, and you can do in the constructor:

this.thatService = Container.instance.get(ThatServiceClass)

Though I’d argue against it, it’s quite against maintainability as you will lose track of dependency flow over time (i.e why would ThatServiceClass be instantiated differently compared to the rest, in a framework obtrusive way).

Also, for me, as long reading the code doesn’t involve guessing & interpreting, the code is probably gonna last.


#5

That’s great - that’s exactly what I was looking for.

The reason I could justify it to myself, is that all of the services inherit from the one BaseFetchService.


#6

In the age of dependency injection I’m not sure it’s good practice to rely on user code instantiated from the Aurelia top level container since Global Variables/Functions can be so easily misused not by the original writer but others maintaining the code at a later date. If something goes wrong it can be difficult to track down something that can be accessed/changed from anywhere in the code.


#7

You’re absolutely correct. After trying it for about 30 minutes, I scrapped the idea and went back to correctly implementing dependency injection.

All I was trying to do was save a miniscule amount of typing - hardly compensates for the amount of debugging I had to do!