Implicit services
Simple services, which don't require any special options and whose dependencies can be resolved automatically by the compiler, need only to be discoverable by the compiler as exports from one of the resource files:
// this will register the ServiceOne class as a service and it will also
// automatically include any interfaces ServiceOne implements, as well as
// its ancestors and their interfaces, as aliases:
export class ServiceOne {
// ...
}
// this will register a dynamic service of the type 'ServiceTwo'; any other
// interfaces which ServiceTwo extends will be registered as its aliases:
export interface ServiceTwo {
// ...
}
// aside from classes and interfaces you can also export factory functions:
export function createServiceThree(): ServiceThree {
// ...
}
Services registered this way will not have a public service ID. They will have
autogenerated string identifiers beginning with a #
character, but you're
strongly discouraged from using these, because they're a product of the
compilation process and can change at any time, even between compilations in
some cases. Instead, you should rely on injection to get instances of these
services.
There is a special case for classes which don't have any public constructors,
but which have a static create()
method. If such a class is found, the create()
method will be used as its factory:
export class ServiceFour {
static create(): ServiceFour {
// ...
}
private constructor() {
// ...
}
}
Service factories, whether they're functions or static create()
methods, can
be async
:
export async function loadConfig(): Promise<ApplicationConfig> {
return JSON.parse(await readFile('config.json', 'utf-8'));
}
Factories can also return undefined
if a service cannot be instantiated at
runtime, allowing services to be optional:
interface LogWriter {
write(message: string): void;
}
export function fileLogWriter(): LogWriter {
return process.env.LOG_FILE ? new FileLogWriter(process.env.LOG_FILE) : undefined;
}
export function elasticLogWriter(): LogWriter {
return process.env.ELASTIC_DSN ? new ElasticLogWriter(process.env.ELASTIC_DSN) : undefined;
}
A service can also be a list or an iterable:
export function * logWriterFactory(): Iterable<LogWriter> {
if (process.env.LOG_FILE) {
yield new FileLogWriter(process.env.LOG_FILE);
}
if (process.env.ELASTIC_DSN) {
yield new ElasticLogWriter(process.env.ELASTIC_DSN);
}
}
When you need to do some more complex logic to create a service instance, or when you want to provide other service configuration such as a scope or one or more service hooks, you can export an explicit service definition from a resource file.