> ## Documentation Index
> Fetch the complete documentation index at: https://docs.bijection.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Organizing by Domain

> Give each part of your app its own tables, so only its code can write them

As an app grows, many parts of the code end up changing the same tables. A
domain fixes this: a folder owns its tables, only that folder's functions can
write them, and everything else calls those functions. Reads stay shared, so
views and joins keep working across the whole app.

A small app doesn't need any of this. Add a domain when a second area of your
app appears.

## Declare a domain

Put the tables in a file inside the folder:

```ts bijection/orders/tables.ts theme={"theme":{"light":"github-light-default","dark":"github-dark-default"}}
import { defineTable } from "bijection/server";
import { v } from "bijection/values";

export const orders = defineTable({
  customerId: v.id("customers"),
  status: v.string(),
}).index("by_customerId", ["customerId"]);

export const orderLines = defineTable({
  orderId: v.id("orders"),
  sku: v.string(),
});
```

Then add one line to your schema:

```ts bijection/schema.ts theme={"theme":{"light":"github-light-default","dark":"github-dark-default"}}
import { defineSchema, defineTable, owned } from "bijection/server";
import { v } from "bijection/values";
import * as orders from "./orders/tables";

export default defineSchema({
  customers: defineTable({ name: v.string() }), // shared: any mutation can write
  ...owned("orders", orders),
});
```

There is still one schema, so a view or `.reference()` can use tables from any
domain.

## Write inside the folder

Import builders from the folder's generated server file. They can write that
folder's tables and read everything else:

```ts bijection/orders/payments.ts theme={"theme":{"light":"github-light-default","dark":"github-dark-default"}}
import { internalMutation } from "../_generated/domains/orders/server";
import { v } from "bijection/values";

export const markPaid = internalMutation({
  args: { orderId: v.id("orders") },
  handler: async (ctx, { orderId }) => {
    await ctx.db.patch("orders", orderId, { status: "paid" });
  },
});
```

Inside a folder, share code with plain helper functions, as you would anywhere
else.

## Call across folders

Code in another folder changes those tables by calling one of the folder's
functions. The call runs in the same transaction, and if it throws, only its own
writes roll back:

```ts bijection/billing/pay.ts theme={"theme":{"light":"github-light-default","dark":"github-dark-default"}}
import { internal } from "../_generated/api";
import { mutation } from "../_generated/domains/billing/server";
import { v } from "bijection/values";

export const run = mutation({
  args: { orderId: v.id("orders"), amount: v.int64() },
  handler: async (ctx, { orderId, amount }) => {
    await ctx.db.insert("invoices", { orderId, amount });
    await ctx.runMutation(internal.orders.payments.markPaid, { orderId });
  },
});
```

Writing to `orders` directly from `billing` is a type error, and it also fails
at runtime with `TableDomainRequired`, because the check runs on the function
that is actually running. Each cross-folder call has a small cost, so keep them
to real changes of ownership rather than every line of shared logic.

## Narrow a table further

`.writers()` still works on a table in a domain, and it only ever narrows. Use it
to say that only one function may change a field:

```ts theme={"theme":{"light":"github-light-default","dark":"github-dark-default"}}
export const orders = defineTable({ status: v.string(), notes: v.string() })
  .writers<InternalMutationPath>(["orders/payments:markPaid", "orders/notes:edit"], {
    fields: { status: ["orders/payments:markPaid"] },
  });
```


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.