Configuration Reference
Overview
Every Bounda project requires a bounda.config.ts file at the project root. This file defines which aggregates exist, how their event stores and read models are wired, and what runtime behavior to apply for policies and process managers.
import type { Config } from "@bounda-dev/config";
export default {
domain: { /* ... */ },
read: { /* ... */ },
} satisfies Config;
The Config type is the top-level interface. Bounda merges your configuration with sensible defaults at boot time.
Top-Level Options
| Option | Type | Default | Description |
|---|---|---|---|
domain | Record<string, DomainConfig> | {} | One entry per aggregate, keyed by aggregate name |
read | Record<string, ReadConfig> | {} | One entry per read model, keyed by read model name |
rootDir | string | "app" | Root directory containing your domain and read model files |
outputDir | string | ".bounda" | Directory where generated types are written |
commandPattern | string | "**/commands/{*.ts,*/index.ts}" | Glob pattern for discovering command files |
eventPattern | string | "**/domain/*/*.ts" | Glob pattern for discovering event files |
policyPattern | string | "**/policies/{*.ts,*/index.ts}" | Glob pattern for discovering policy files |
projectionPattern | string | "**/read/*/projections/{*.ts,*/index.ts}" | Glob pattern for discovering projection files |
queryPattern | string | "**/read/*/queries/{*.ts,*/index.ts}" | Glob pattern for discovering query files |
processManagerPattern | string | "**/processes/{*.ts,*/index.ts}" | Glob pattern for discovering process manager files |
Domain Configuration
Each key in domain corresponds to an aggregate directory name. The value is a DomainConfig object:
interface DomainConfig {
readonly eventStore: EventStoreConfig;
readonly scheduling?: SchedulingConfig;
readonly commands?: Record<string, CommandConfig>;
readonly policies?: PoliciesConfig;
readonly processManagers?: ProcessManagersConfig;
}
Event Store
Every aggregate requires an event store adapter. See the Adapters section for available types.
eventStore: { type: "in-memory" }
eventStore: { type: "sqlite", path: "./db/events.db" }
eventStore: { type: "postgresql", url: process.env.DATABASE_URL, schema: "bounda" }
Scheduling
Scheduling enables delayed and scheduled commands for an aggregate.
scheduling: {
type: "sqlite",
path: "./db/scheduler.db",
pollingIntervalMs: 100,
}
| Option | Type | Default | Description |
|---|---|---|---|
type | "sqlite" or "postgresql" | — | Storage adapter for the scheduler |
path | string | — | File path (SQLite only) |
url | string | — | Connection URL (PostgreSQL only) |
schema | string | — | Database schema (PostgreSQL only) |
pollingIntervalMs | number | — | How often to poll for due commands, in milliseconds |
startWorker | boolean | — | Whether to start the scheduling worker automatically |
Commands
The commands map lets you inject collaborator bindings into specific command handlers.
commands: {
sendConfirmation: {
notificationSender: { use: "console" },
},
sendWelcomeEmail: {
emailSender: {
use: process.env.SEND_WELCOME_EMAIL_SENDER ?? "resend",
apiKey: process.env.RESEND_API_KEY,
},
},
},
Each command entry accepts a map of collaborator names to bindings. A binding is either a plain string or an object with use plus additional properties passed to the collaborator factory.
Policies
interface PoliciesConfig {
readonly retry?: PolicyRetryConfig;
readonly dlq?: PolicyStorageConfig;
readonly maxChainDepth?: number;
readonly executionsTTL?: number;
}
| Option | Type | Default | Description |
|---|---|---|---|
retry.retryStrategy | "none", "exponential", "linear", "fixed" | "exponential" | Retry strategy for failed policy executions |
retry.maxRetries | number | 3 | Maximum number of retry attempts |
retry.timeout | number | 30000 | Timeout per execution in milliseconds |
maxChainDepth | number | 25 | Maximum depth of chained policy reactions before halting |
executionsTTL | number | 604800000 (7 days) | Time-to-live for execution records in milliseconds |
dlq.type | "sqlite" or "postgresql" | — | Storage adapter for the dead letter queue |
Process Managers
interface ProcessManagersConfig {
readonly timeout?: string;
readonly retry?: ProcessManagerRetryConfig;
readonly dlq?: ProcessManagerStorageConfig;
}
| Option | Type | Default | Description |
|---|---|---|---|
timeout | string | "7d" | Maximum lifetime of a process manager instance (e.g., "7d", "24h") |
retry.retryStrategy | "none", "exponential", "linear", "fixed" | "exponential" | Retry strategy for failed handler executions |
retry.maxRetries | number | 3 | Maximum number of retry attempts |
retry.timeout | number | 30000 | Timeout per execution in milliseconds |
dlq.type | "sqlite" or "postgresql" | — | Storage adapter for the dead letter queue |
Read Configuration
Each key in read corresponds to a read model directory name. The value specifies the storage adapter:
read: {
"order-summary": { type: "sqlite", path: "./db/order-summary.db" },
"users-directory": { type: "mongodb-memory", path: "./db/users-directory-mongo", dbName: "bounda" },
"my-profile": { type: "postgresql", url: process.env.DATABASE_URL, schema: "bounda" },
}
See Adapters for details on each read model adapter type.
Complete Examples
Storefront (SQLite + In-Memory)
import type { Config } from "@bounda-dev/config";
export default {
rootDir: "src",
domain: {
order: {
eventStore: { type: "in-memory" },
scheduling: {
type: "sqlite",
path: "./db/order-scheduler.db",
pollingIntervalMs: 100,
},
commands: {
sendConfirmation: {
notificationSender: { use: "console" },
},
},
policies: {
maxChainDepth: 5,
retry: { retryStrategy: "none" },
},
},
},
read: {
"order-summary": { type: "sqlite", path: "./db/order-summary.db" },
"order-catalog": { type: "sqlite", path: "./db/order-catalog.db" },
"my-orders": { type: "sqlite", path: "./db/my-orders.db" },
},
} satisfies Config;
This configuration uses an in-memory event store for fast iteration during development, SQLite for scheduling and read models, and disables policy retries while limiting chain depth to 5.
Onboarding (PostgreSQL)
import type { Config } from "@bounda-dev/config";
const schema = process.env.BOUNDA_SCHEMA ?? "bounda";
export default {
domain: {
user: {
eventStore: {
type: "postgresql",
url: process.env.DATABASE_URL,
schema,
},
scheduling: {
type: "postgresql",
url: process.env.DATABASE_URL,
schema,
},
commands: {
sendWelcomeEmail: {
emailSender: {
use: process.env.SEND_WELCOME_EMAIL_SENDER ?? "resend",
apiKey: process.env.RESEND_API_KEY,
},
},
},
processManagers: {
dlq: {
type: "postgresql",
url: process.env.DATABASE_URL,
schema,
},
},
},
},
read: {
"users-directory": {
type: "mongodb-memory",
path: "./db/users-directory-mongo",
dbName: "bounda",
},
"user-details": {
type: "sqlite",
path: "./db/user-details-read.db",
},
"my-profile": {
type: "postgresql",
url: process.env.DATABASE_URL,
schema,
},
},
} satisfies Config;
This production-oriented configuration uses PostgreSQL for the event store, scheduling, and process manager DLQ. Read models mix adapters based on query needs: MongoDB-style queries for the users directory, SQLite for user details, and PostgreSQL for the profile view.
Related
- Adapters — Available storage adapters and their configuration
- CLI Reference — Command-line tools for code generation and operations