Skip to main content

Conditional Bindings

Brandi allows you to use tags and targets to control which implementation of the abstraction will be injected to a target.

Creating tokens:

tokens.ts
import { token } from 'brandi';

import type { Cacher } from './Cacher';
import type { UserService } from './UserService';
import type { SettingsService } from './SettingsService';
import type { AdminService } from './AdminService';

export const TOKENS = {
cacher: token<Cacher>('cacher'),
userService: token<UserService>('userService'),
settingsService: token<SettingsService>('settingsService'),
adminService: token<AdminService>('adminService'),
};

Creating tags:

tags.ts
import { tag } from 'brandi';

export const TAGS = {
offline: tag('offline'),
};

Creating two types of cacher with a common interface:

Cacher.ts
export interface Cacher {
/* ... */
}

export class OnlineCacher implements Cacher {
/* ... */
}

export class LocalCacher implements Cacher {
/* ... */
}

Creating some services with a dependency on the Cacher:

UserService.ts
import { injected } from 'brandi';

import { TOKENS } from './tokens';
import { Cacher } from './Cacher';

export class UserService {
constructor(private cacher: Cacher) {}

/* ... */
}

injected(UserService, TOKENS.cacher);
SettingsService.ts
import { injected } from 'brandi';

import { TOKENS } from './tokens';
import { Cacher } from './Cacher';

export class SettingsService {
constructor(private cacher: Cacher) {}

/* ... */
}

injected(SettingsService, TOKENS.cacher);
AdminService.ts
import { injected } from 'brandi';

import { TOKENS } from './tokens';
import { Cacher } from './Cacher';

export class AdminService {
constructor(private cacher: Cacher) {}

/* ... */
}

injected(AdminService, TOKENS.cacher);

Configuring the container:

container.ts
import { Container, tagged } from 'brandi';

import { TOKENS } from './tokens';
import { TAGS } from './tags';
import { OnlineCacher, LocalCacher } from './Cacher';
import { UserService } from './UserService';
import { SettingsService } from './SettingsService';
import { AdminService } from './AdminService';

tagged(SettingsService, TAGS.offline); /* ← Tags `SettingsService`. */

export const container = new Container();

container
.bind(TOKENS.cacher) /* ← Binds to `OnlineCacher` in common cases. */
.toInstance(OnlineCacher)
.inTransientScope();

container
.when(TAGS.offline) /* ← Binds to `LocalCacher` when target tagget by `offline` tag. */
.bind(TOKENS.cacher)
.toInstance(LocalCacher)
.inTransientScope();

container
.when(AdminService) /* ← Binds to `LocalCacher` when target is `AdminService`. */
.bind(TOKENS.cacher)
.toInstance(LocalCacher)
.inTransientScope();

container.bind(TOKENS.userService).toInstance(UserService).inTransientScope();
container.bind(TOKENS.settingsService).toInstance(SettingsService).inTransientScope();
container.bind(TOKENS.adminService).toInstance(AdminService).inTransientScope();

Dependencies are injected into services based on the tag:

index.ts
import { TOKENS } from './tokens';
import { OnlineCacher, LocalCacher } from './Cacher';
import { container } from './container';

const userService = container.get(TOKENS.userService);
const settingsService = container.get(TOKENS.settingsService);
const adminService = container.get(TOKENS.adminService);

expect(userService.cacher).toBeInstanceOf(OnlineCacher);
expect(settingsService.cacher).toBeInstanceOf(LocalCacher);
expect(adminService.cacher).toBeInstanceOf(LocalCacher);