Skip to Content

InMemory Adapter

The InMemory adapter connects the Jumentix persistence contract to InMemory. Use cases keep talking to repository ports; database selection stays in composition.

Integrated technology

Process-local memory store

  • Data model: Keyed objects
  • Jumentix driver: InMemory
  • Runtime selection: JUMENTIX_DATABASE_DRIVER=InMemory

When to use

Use it for: Unit tests, demos, and browser-free contract examples.

When to avoid

Avoid for durable data or multi-process services.

How to validate locally

Use the real monorepo smoke test. It validates the adapter lifecycle and prevents shipping configuration that does not connect.

JUMENTIX_DATABASE_DRIVER=InMemory bun run smoke:db:inmemory

Complete example: Task and Category with a database port

type Category = {
  id: string;
  name: string;
};

type Task = {
  id: string;
  title: string;
  categoryId: string;
  completed: boolean;
};

type Repository<T extends { id: string }> = {
  create(record: T): Promise<T>;
  getById(id: string): Promise<T | undefined>;
  list(): Promise<T[]>;
};

function createRepository<T extends { id: string }>(): Repository<T> {
  const records = new Map<string, T>();

  return {
    async create(record) {
      records.set(record.id, record);
      return record;
    },
    async getById(id) {
      return records.get(id);
    },
    async list() {
      return [...records.values()];
    }
  };
}

const adapterProfile = {
  driver: 'InMemory',
  dataModel: 'Keyed objects',
  smokeTest: 'bun run smoke:db:inmemory'
} as const;

const categories = createRepository<Category>();
const tasks = createRepository<Task>();

export async function seedTaskCatalog() {
  const operations = await categories.create({ id: crypto.randomUUID(), name: 'Operations' });
  const finance = await categories.create({ id: crypto.randomUUID(), name: 'Finance' });

  await tasks.create({
    id: crypto.randomUUID(),
    title: 'Review adapter smoke test',
    categoryId: operations.id,
    completed: false
  });

  await tasks.create({
    id: crypto.randomUUID(),
    title: 'Close billing reconciliation',
    categoryId: finance.id,
    completed: true
  });

  return { adapterProfile, categories: await categories.list(), tasks: await tasks.list() };
}

export async function listTasksForCategory(categoryId: string): Promise<Task[]> {
  const category = await categories.getById(categoryId);

  if (!category) {
    throw new Error('Category not found');
  }

  return (await tasks.list()).filter((task) => task.categoryId === category.id);
}

What changes in production

The example above shows the full contract with an in-memory implementation so it can be read end to end. In production, composition injects the real client selected by JUMENTIX_DATABASE_DRIVER=InMemory; the domain stays the same.