Setting Up the Project
Overview
This tutorial walks through building a task management system with Bounda. By the end, you will have a working application that creates, assigns, and completes tasks using event sourcing and CQRS.
In this first step, you will scaffold the project, install dependencies, and generate the initial type definitions.
Create the Project
Start by creating a new directory and initializing it as an npm project:
mkdir task-manager
cd task-manager
npm init -y
Install Dependencies
Bounda is split into a small set of focused packages. Install the runtime packages and the dev tooling:
npm i @bounda-dev/core @bounda-dev/config
npm i -D @bounda-dev/codegen @bounda-dev/ops typescript
| Package | Purpose |
|---|---|
@bounda-dev/core | Runtime: boots the app, dispatches commands, runs queries |
@bounda-dev/config | Typed configuration schema |
@bounda-dev/codegen | CLI tooling and code generation |
@bounda-dev/ops | Runtime operations tools (DLQ management, process manager inspection) |
typescript | TypeScript compiler |
Configure TypeScript
Create a tsconfig.json at the project root:
{
"compilerOptions": {
"target": "ES2022",
"module": "NodeNext",
"moduleResolution": "NodeNext",
"strict": true,
"outDir": "dist",
"rootDir": ".",
"esModuleInterop": true,
"skipLibCheck": true
},
"include": ["src", ".bounda"]
}
Note: The
includearray contains.boundabecause the code generator writes type definitions there. Without it, TypeScript will not find the generated types.
Create the Bounda Configuration
Create bounda.config.ts at the project root. This file tells Bounda where your source code lives, which aggregates exist, and how data is stored:
import type { Config } from "@bounda-dev/config";
export default {
rootDir: "src",
domain: {
task: {
eventStore: { type: "in-memory" },
},
},
read: {
"task-board": {
type: "sqlite",
path: "./db/task-board.db",
},
},
} satisfies Config;
The configuration declares:
rootDir— the base directory for all source files.domain.task— an aggregate calledtaskthat stores events in memory.read.task-board— a read model backed by a local SQLite database.
Create the Directory Structure
Bounda uses a file-system convention to discover events, commands, policies, projections, and queries. Create the expected directories:
mkdir -p src/domain/task/commands
mkdir -p src/domain/task/policies
mkdir -p src/read/task-board/projections
mkdir -p src/read/task-board/queries
The resulting layout looks like this:
task-manager/
bounda.config.ts
tsconfig.json
src/
domain/
task/
commands/
policies/
read/
task-board/
projections/
queries/
Run the Code Generator
Bounda generates type-safe interfaces from your file structure and configuration. Run it now to bootstrap the initial types:
npx bounda generate
This creates a .bounda directory at the project root containing generated type definitions. You will re-run this command whenever you add new events, commands, or queries.
What We Built
- Initialized an npm project with Bounda’s core packages.
- Configured TypeScript with
NodeNextmodule resolution. - Declared a
taskaggregate and atask-boardread model inbounda.config.ts. - Created the directory structure that Bounda’s file-system conventions expect.
- Ran the code generator to produce initial type definitions.
The project is scaffolded and ready. Next, you will define the first aggregate by writing events that describe what can happen to a task.
Next step: Defining Your First Aggregate