Project Structure
Convention-Based Directory Layout
Bounda uses a file-based convention system to discover and wire your domain. Place files in the expected directories and Bounda handles registration, type generation, and runtime orchestration automatically.
Root Directory
The default root directory is app/. You can change this with the rootDir option in bounda.config.ts:
import type { Config } from "@bounda-dev/config";
export default {
rootDir: "src",
domain: { /* ... */ },
read: {},
} satisfies Config;
Directory Overview
app/
domain/
order/
order-placed.ts
order-cancelled.ts
commands/
place-order.ts
cancel-order.ts
send-confirmation/
index.ts
notification-sender.console.ts
notification-sender.email.ts
policies/
send-confirmation-on-order-placed.ts
processes/
order-lifecycle/
index.ts
on-order-placed.ts
on-order-confirmed.ts
on-order-fulfilled.ts
read/
order-summary/
view.ts
projections/
order-placed.ts
order-cancelled.ts
queries/
get-order-summary.ts
order-catalog/
view.ts
projections/
order-placed.ts
queries/
get-orders.ts
.bounda/
Domain Directory
domain/<aggregate-name>/
Each aggregate gets its own directory under domain/. Event files live directly inside the aggregate directory. Subdirectories organize commands, policies, and process managers.
Events — domain/<aggregate-name>/<event-name>.ts
Event files define a Zod payload schema and an apply function that updates the aggregate state. They are discovered by the pattern **/domain/*/*.ts.
Commands — domain/<aggregate-name>/commands/<command-name>.ts
Command handlers validate input and emit events. For simple commands, use a single file. For commands with collaborators (external services, adapters), use a directory with an index.ts entry point.
Pattern: **/commands/{*.ts,*/index.ts}
Policies — domain/<aggregate-name>/policies/<description>.ts
Policies react to events and dispatch follow-up commands. Name them descriptively to reflect the reaction, for example send-confirmation-on-order-placed.ts.
Pattern: **/policies/{*.ts,*/index.ts}
Process Managers — domain/<aggregate-name>/processes/<process-name>/
Process managers coordinate long-running workflows across multiple events. Each process has an index.ts configuration file and on-<event-name>.ts handler files.
Pattern: **/processes/{*.ts,*/index.ts}
Read Directory
read/<view-name>/
Each view (read model) gets its own directory under read/.
Views — read/<view-name>/view.ts
The view.ts file declares the field schema for the read model table.
Projections — read/<view-name>/projections/<event-name>.ts
Projection handlers receive events and write to the read model using the projector API. Each projection file corresponds to one event type.
Pattern: **/read/*/projections/{*.ts,*/index.ts}
Queries — read/<view-name>/queries/<query-name>.ts
Query handlers define a payload schema, a repository function for data access, and a handler that shapes the response.
Pattern: **/read/*/queries/{*.ts,*/index.ts}
Generated Types
.bounda/
The output directory for all generated files. Created and updated by running npx bounda generate. Configured via the outputDir option (defaults to .bounda).
+types/
Generated type files appear in +types/ directories co-located with your domain files. Each concept (event, command, query, projection) gets its own type file. These files are auto-generated and should not be edited manually.
For example, defining domain/order/order-placed.ts produces domain/order/+types/order-placed.ts containing the Event.PayloadFunction, Event.ApplierArgs, and other typed interfaces used in your event file.
File Naming Conventions
| Concept | File Name | Example |
|---|---|---|
| Events | <event-name>.ts | order-placed.ts |
| Commands | <command-name>.ts or <command-name>/index.ts | place-order.ts |
| Policies | <event-reaction-description>.ts | send-confirmation-on-order-placed.ts |
| Process managers | <process-name>/index.ts (config), on-<event-name>.ts (handlers) | order-lifecycle/index.ts |
| Views | view.ts | view.ts |
| Projections | <event-name>.ts | order-placed.ts |
| Queries | <query-name>.ts | get-order-summary.ts |
All file names use kebab-case.
Default File Patterns
These are the glob patterns Bounda uses to discover files. They can be overridden in bounda.config.ts:
| Concept | Pattern |
|---|---|
| Commands | **/commands/{*.ts,*/index.ts} |
| Events | **/domain/*/*.ts |
| Policies | **/policies/{*.ts,*/index.ts} |
| Projections | **/read/*/projections/{*.ts,*/index.ts} |
| Queries | **/read/*/queries/{*.ts,*/index.ts} |
| Process managers | **/processes/{*.ts,*/index.ts} |
Note: Test files (
**/*.test.ts,**/*.spec.ts),node_modules,.bounda, and+typesdirectories are excluded by default.