Creating Factories before Container
If in some reason you need to define factories separatelly from the container declaration, you can just declare factories object of type IFactories<IContainer>
.
Example 1. Declaring Factories Object
./factories.ts
import { IFactories } from 'true-di';
import { IContainer } from './interfaces';
import Logger from './Logger';
import DataSourceService from './DataSourceService';
import ECommerceService from './ECommerceService';
const factories: IFactories<IContainer> = {
logger: () =>
new Logger(),
dataSourceService: ({ logger }) =>
new DataSourceService(logger),
ecommerceService: ({ logger, dataSourceService }) =>
new ECommerceService(logger, dataSourceService),
};
export default factories;
then we can use this factories object to create a container:
./index.js
import express from 'express';
import diContainer from 'true-di';
import factories from './factories';
import { getOrders } from './controller';
const app = express();
app.use((req, res, next) => {
req.container = diContainer(factories);
next();
});
app.get('/orders', getOrders);
app.listen(8080);
Example 2. Partial Factories Object
You can define several factories objects those could be marged on the container creation stage.
./factories.ts
import { IFactories } from 'true-di';
import { IContainer } from './interfaces';
import Logger from './Logger';
import DataSourceService from './DataSourceService';
import ECommerceService from './ECommerceService';
type FactoryFor<Names extends keyof IContainer> = Pick<IFactories<IContainer>, Names>;
export const loggerFactory: FactoryFor<'logger'> = {
logger: () => new Logger(),
};
export const dasFactory: FactoryFor<'dataSourceService'> = {
dataSourceService: ({ logger }) => new DataSourceService(logger),
};
export const ecommerceServiceFactory: FactoryFor<'ecommerceService'> = {
ecommerceService: ({ logger, dataSourceService }) =>
new ECommerceService(logger, dataSourceService),
};
and use these factories to create a container:
./index.js
import express from 'express';
import diContainer from 'true-di';
import { loggerFactory, dasFactory, ecommerceServiceFactory } from './factories';
import { getOrders } from './controller';
const app = express();
app.use((req, res, next) => {
req.container = diContainer({
...oggerFactory,
...dasFactory,
...ecommerceServiceFactory,
});
next();
});
app.get('/orders', getOrders);
app.listen(8080);
If some of partially declared factories contains a non-enumerable property, then use shallowMerge
function instead of spread-operator.
import express from 'express';
import diContainer from 'true-di';
import { shallowMerge } from 'true-di/utils';
import { loggerFactory, dasFactory, ecommerceServiceFactory } from './factories';
import { getOrders } from './controller';
const app = express();
app.use((req, res, next) => {
req.container = diContainer(shallowMerge(
oggerFactory,
dasFactory,
ecommerceServiceFactory,
));
next();
});
app.get('/orders', getOrders);
app.listen(8080);
Example 3. Direct Factory Declaration
Sometimes it could be convenient to declare just a factory for an item.
import { IFactory } from 'true-di';
import { IContainer } from './interfaces';
import Logger from './Logger';
import DataSourceService from './DataSourceService';
import ECommerceService from './ECommerceService';
type FactoryFor<Name extends keyof IContainer> = IFactory<IContainer, Name>;
export const logger: FactoryFor<'logger'> = () => new Logger();
export const dataSourceService: FactoryFor<'dataSourceService'> =
({ logger }) => new DataSourceService(logger);
export const ecommerceService: FactoryFor<'ecommerceService'> =
({ logger, dataSourceService }) => new ECommerceService(logger, dataSourceService);
or
import { IContainer } from './interfaces';
import Logger from './Logger';
import DataSourceService from './DataSourceService';
import ECommerceService from './ECommerceService';
export const logger = () =>
new Logger();
export const dataSourceService = ({ logger }: IContainer) =>
new DataSourceService(logger);
export const ecommerceService = ({ logger, dataSourceService }: IContainer) =>
new ECommerceService(logger, dataSourceService);
or even
import { ILogger, IDataSourceService } from './interfaces';
import Logger from './Logger';
import DataSourceService from './DataSourceService';
import ECommerceService from './ECommerceService';
type DASDeps = {
logger: ILogger;
}
type EComDeps = {
logger: ILogger;
dataSourceService: IDataSourceService;
}
export const logger = () => new Logger();
export const dataSourceService = ({ logger }: DASDeps) =>
new DataSourceService(logger);
export const ecommerceService = ({ logger, dataSourceService }: EComDeps) =>
new ECommerceService(logger, dataSourceService);
and then use these factories to create container:
import express from 'express';
import diContainer from 'true-di';
import { logger, dataSourceService, ecommerceService } from './factories';
import { getOrders } from './controller';
const app = express();
app.use((req, res, next) => {
req.container = diContainer({ logger, dataSourceService, ecommerceService });
next();
});
Caution
Despite pointed bellow way to declare factory seems concise:
export const logger = () => new Logger();
but the consumers of the container became to be dependent on the interface of specific logger implementation, that could be wider then ILogger
.
So, it is better to declare such factories in this way:
export const logger = (): ILogger => new Logger();
Last updated
Was this helpful?