Skip to content
Bunshi
GitHub

moleculeInterface

Sometimes you don’t care about the implementation details. Your molecules need a dependency, but it doesn’t matter where it comes from or how it’s implemented. Bunshi supports interfaces as a tool to fix this problem.

An interface defines a dependency, but not the implementation for the dependency or how it gets created.

API

In other programming languages interfaces can be referenced at build time or runtime, but Javascript doesn’t have interfaces and Typescript interfaces don’t exist at runtime.

Bunshi has moleculeInterface to create molecule interfaces that can be referenced and used at runtime.

import { moleculeInterface } from "bunshi";

export interface SendsEmail {
  sendEmail(recipient: string);
}

export const SendsEmailMolecule = moleculeInterface<SendsEmail>();

Molecules can depend on interfaces, not just other molecules.

import { molecule } from "bunshi";
import { SendsEmailMolecule } from "./molecules";
import { FormMolecule } from "./forms";

export const RegistrationFormMolecule = molecule((mol) => {
  const emailSender = mol(SendsEmailMolecule);
  const form = mol(FormMolecule);

  const onSubmit = () => {
    emailSender.sendEmail(form.getData().email);
  };

  return {
    form,
    onSubmit,
  };
});

Usage with Injectors

Interfaces are resolved through injectors with interface bindings. Child injectors can inherit bindings from parent injectors.

Basic Binding

const injector = createInjector({
  bindings: [[ApiClientInterface, FetchApiClientMolecule]]
});

const apiClient = injector.get(ApiClientInterface);

Parent-Child Hierarchy

const parentInjector = createInjector({
  bindings: [[ApiClientInterface, ProductionApiClientMolecule]]
});

const childInjector = createInjector({
  parent: parentInjector,
  bindings: [[CacheInterface, MemoryCacheMolecule]]
});

// Child can access both ApiClientInterface (from parent) and CacheInterface (local)

Testing & Environment Configuration

// Override parent bindings for testing
const testInjector = createInjector({
  parent: productionInjector,
  bindings: [[ApiClientInterface, MockApiClientMolecule]]
});

// Environment-specific injectors
const injector = isDev ? devInjector : prodInjector;

Tips

  • Interfaces don’t specify if they are scoped or not. Write your molecules accordingly.
  • Bindings to interfaces have to be done in injectors using the bindings parameter.
  • Use parent injectors to create hierarchical configurations where child injectors inherit base bindings.
  • Child injectors can override parent bindings by providing their own implementation for the same interface.
  • Interfaces can be provided in React applications using MoleculeProvider.
  • If you have a default implementation for an interface, then just use a molecule instead.
  • Scopes can act as a replacement for interfaces if you have a large number of implementations