📦 Dependency injection / inversion of control framework.
FoxStorm Container is a lightweight Inversion of Control container for TypeScript applications. It uses a mechanism to register dependencies which can be retriever later as instance.
- Environment
- Config
- Services
- ApplicationContainer
Create Environment
instances by passing a string as the name and a boolean as isRelease.
const staging = Environment.new('staging', false)
To easily create development, testing and a production environments the following methods can be used:
const development = Environment.development() // has isRelease false
const testing = Environment.testing() // has isRelease false
const production = Environment.production() // has isRelease true
Config
chooses the proper service when multiple services has been registered for the same interface. Assuming this services are registered on the same interface:
const services = new Services()
services.registerWithInterface(PrintLogger, 'Logger')
services.registerWithInterface(FileLogger, 'Logger')
we need to choose which service should be used:
const config = new Config()
switch (environment.name) {
case 'development': {
const preference: ConfigPreference = { prefer: PrintLogger, for: 'Logger' }
config.usePreference(preference)
},
case 'production': {
const preference: ConfigPreference = { prefer: FileLogger, for: 'Logger' }
config.usePreference(preference)
}
}
New services can be registered, configured or created. There are a couple of ways how you can register a service:
const services = new Services()
services.registerInterfaceWithFactory(PrintLogger, () => {
return new PrintLogger()
})
services.registerInterfaceWithFactory(PrintLogger, 'Logger', () => {
return new PrintLogger()
})
services.registerServiceWithInterfacesAndFactory(PrintLogger, [ 'Logger', 'ErrorLogger' ], () => {
return new PrintLogger()
})
try {
services.registerProvider(new PrintLoggerProvider())
} catch { /* throw some error */ }
Services can be registered easier if they implement a static makeService
method.
interface Logger {
info (message: string): void
}
// PrintLogger.ts
class PrintLogger implements Logger {
static makeService (): this {
return new this()
}
info (message: string): void {
console.log(message)
}
}
const services = new Services()
services.registerInstance(new PrintLogger())
services.registerInstanceWithInterface(new PrintLogger(), 'Logger')
services.registerInstanceWithInterfaces(new PrintLogger(), [ 'Logger', 'ErrorLogger' ])
services.registerService(PrintLogger)
The ApplicationContainer
is initialised with the Config
, Environment
and the Services
and provides a way to retrieve services based on the provided parameters:
const environment = Environment.development()
const config = customConfig || new Config()
const services = new Services()
services.registerWithInterface(PrintLogger, 'Logger')
const application = new ApplicationContainer(config, environment, services)
const printLogger = application.retrieveServiceFor(PrintLogger)
// Logger.ts
interface Logger {
info (message: string): void
}
// PrintLogger.ts
class PrintLogger implements Logger {
static makeService (): this {
return new this()
}
info (message: string): void {
console.log(message)
}
}
// FileLogger.ts
import * as fs from 'fs'
class FileLogger implements Logger{
static makeService (): this {
return new this()
}
info (message: string): void {
fs.writeFile('foo.txt', message)
}
}
// Application.ts
import { Config, Environment, Services, ApplicationContainer } from 'foxstorm-container'
const environment = Environment.development()
const config = new Config()
const services = new Services()
services.registerWithInterface(PrintLogger, 'Logger')
services.registerWithInterface(FileLogger, 'Logger')
switch (environment.name) {
case 'development': {
const preference: ConfigPreference = { prefer: PrintLogger, for: 'Logger' }
config.usePreference(preference)
},
case 'production': {
const preference: ConfigPreference = { prefer: FileLogger, for: 'Logger' }
config.usePreference(preference)
}
}
const application = new ApplicationContainer(config, environment, services)
const printLogger = application.retrieveServiceFor(PrintLogger)
const fileLogger = application.retrieveServiceFor(FileLogger)
const logger = application.retrieveServiceFor('Logger') // resolved by custom preference
logger.info(`I'm a logger`)