# Your application structure

> Keep the generated app independent. Add product code and tests in your app; import shared services from the framework package.

- Package: `@db3.ai/app`
- Canonical page: [https://db3.ai/docs/project-structure](https://db3.ai/docs/project-structure)
- Markdown: [https://db3.ai/docs/project-structure.md](https://db3.ai/docs/project-structure.md)
- Framework source of truth: `docs/framework-conventions.md`

## Workflow
1. **Start with your app** — The creator gives you server, UI, database and test files.
2. **Add a feature** — Keep its model, route, UI and behaviour test together in the app.
3. **Use framework services** — Import supported APIs from @db3.ai/app subpaths.

<a id="standalone"></a>

## The generated starter is an ordinary app

You do not need the framework monorepo or a packages directory. The creator gives you the structure below. `server/config.ts` reads settings, `server/app.ts` creates the framework App, `server/http/createServer.ts` owns routes, and `server/index.ts` owns the process.

Add your model under `server/models`, register it in `server/database/models.ts` and generate a migration. Change the UI under `src` and add behaviour tests under `tests`. The starter guide runs that complete first change.

- [Create and extend the starter](https://db3.ai/docs/starter-app.md)
- [A smaller HTTP-only layout](https://db3.ai/docs/create-app.md#layout)

### Your generated app

```text
my-app/
	package.json
	.env.example
	docker-compose.yml
	src/
		App.vue
		api.ts
	server/
		config.ts
		app.ts
		index.ts
		http/createServer.ts
		models/Note.ts
		database/models.ts
	database/
		migrations/
		schema.snapshot.json
	tests/
		app.test.ts
		aiAllowance.test.ts
```

<a id="apps-own-products"></a>

## Apps own product behaviour

Keep routes, product models, screens, prompts, application jobs, and business orchestration inside the consuming app. Framework packages should provide reusable mechanics and stable contracts without absorbing one product’s policy.

This boundary keeps apps easy to understand and prevents the framework from becoming a second application hidden behind generic names.

For your next feature, continue with the starter, model or API guides below. The remaining sections are for people contributing reusable code to the framework, not folders you need to create in your app.

- [Extend the starter](https://db3.ai/docs/starter-app.md)
- [Model your data](https://db3.ai/docs/guide-model-data.md)
- [Build an API](https://db3.ai/docs/guide-api.md)

<a id="service-owned-modules"></a>

## For contributors: service-owned modules

Each reusable framework service is a package-shaped service module inside its current package. The service owns implementation, public contracts, drivers, README guidance, production-shaped examples, behaviour tests, fixtures, and test support.

The package root owns application composition and public subpath exports. Every framework test lives with an owning service; cross-service integration tests belong to the service whose public behaviour they primarily exercise.

### Service-owned module layout

```txt
packages/app/src/queue/
	index.ts
	README.md
	contracts/
	drivers/
	examples/
	tests/
		drivers/
		examples/
		support/
```

<a id="queue-reference-module"></a>

## For contributors: Queue as a reference

Queue established this convention, and every framework service suite now follows the same packages/app/src/{service}/tests ownership rule. Queue examples compile independently, and the documentation website imports those example files rather than maintaining copied snippets.

The job below remains application-shaped even though Queue owns the example. It demonstrates the public API a product uses without moving product-specific behaviour into the framework.

### GenerateReportJob.ts

```typescript
import { app } from '@db3.ai/app/server';
import { QueueableJob } from '@db3.ai/app/queue';

/**
 * Durable data required to generate one application report.
 */
export interface GenerateReportJobData extends Record<string, unknown> {
	/** Stable application-owned report identifier. */
	reportId: string;
}

/**
 * Example application job restored from its JSON-safe queue payload.
 */
export class GenerateReportJob extends QueueableJob<GenerateReportJobData> {
	/**
	 * Creates a report job after validating its durable payload.
	 *
	 * @param data - Report identity persisted with the queued job.
	 */
	constructor(data: GenerateReportJobData) {
		if (typeof data.reportId !== 'string' || data.reportId.trim() === '') {
			throw new Error('GenerateReportJob requires a report id.');
		}

		super(data);
	}

	/**
	 * Generates the report through services resolved from the active application.
	 */
	async handle(): Promise<void> {
		app().log.info({
			reportId: this.data.reportId,
		}, 'Generating report');
	}
}
```

<a id="build-test-doc-boundaries"></a>

## For contributors: build and documentation boundaries

Production builds exclude service tests and examples. Dedicated checks still type-check examples, the test runner discovers colocated suites, and documentation generation verifies every source path.

Documentation tests compare generated snippets byte-for-byte with their owning files. A stale copy therefore fails the maintenance gate instead of quietly drifting away from executable framework behaviour.

<a id="future-extraction"></a>

## For contributors: when to extract a package

Co-location makes dependencies and consumers visible, but it does not require a package split. Extract a service only when independent reuse, dependency weight, ownership, versioning, or release cadence justifies the additional package.

Migrate small leaf services first, data-backed services second, orchestration services third, and foundational database and server boundaries last. Move one service at a time so the convention remains easy to verify.

## Behavioural verification
Executes the imported Queue examples through the real Queue service with a deterministic service-owned driver.
- Behaviour test: `packages/app/src/queue/tests/examples/createAndProcessReportJob.test.ts`
- Repository test command (framework checkout only): `npm test --workspace packages/app -- src/queue/tests/examples/createAndProcessReportJob.test.ts`
- In an installed application, use the walkthrough commands instead of this repository test.
- Expected outcome: 3 tests pass · dispatch, chain, batch, retry policy, and replay are exercised
- Environment: Node.js · deterministic in-memory queue driver

## Related documentation
- [Build your first app](https://db3.ai/docs/starter-app.md): Create an account, save a private note and summarise it with AI. Start with working application code you can change.
- [Configure the application runtime](https://db3.ai/docs/app-config.md): Keep application settings explicit. Boot one App, give each service its options and leave HTTP and worker startup to the host.
- [Take a note from input to stored data](https://db3.ai/docs/guide-model-data.md): Choose fields, protect ownership, persist the model and return its public shape.
- [Build an owned-note JSON API](https://db3.ai/docs/guide-api.md): Keep HTTP validation, field conversion and authorization at their own boundaries. Use the starter’s real note routes as the example.
- [Queue](https://db3.ai/docs/queue-overview.md): Create durable background jobs, run named workers, understand every attempt, compose chains and batches, and recover failures through one complete service guide.
- [Build and operate queued work](https://db3.ai/docs/example-queue.md): Follow the complete imported Queue example set: define durable data, register and process a job, compose chains and batches, configure retry policy, defer backpressure, and replay terminal failures.

## Framework-owned source: `docs/framework-conventions.md`

This is the exact source document captured by the documentation build. Use it for detailed API and workflow guidance, subject to the public package exports and behavioural evidence identified above.

````markdown
# Framework Conventions

This document defines repeatable conventions for framework code in `packages/*`. The goal is to make framework APIs easy to find, easy to import, and easy to document without turning simple implementation details into architecture.

## Service-Owned Modules

Each reusable framework service should be a self-contained, package-shaped module inside its current package. Source, public contracts, drivers, documentation, examples, tests, fixtures, and test support belong to the service that owns the behaviour.

Queue is the reference layout:

```text
packages/app/src/queue/
	index.ts
	README.md
	Queue.ts
	QueueableJob.ts
	contracts/
	drivers/
	examples/
	tests/
		drivers/
		examples/
		support/
```

This keeps `packages/app` simple to install today while making each service boundary visible enough to extract into a standalone package later. A future extraction should primarily move one service directory, declare its existing dependencies, and update the root application package to compose or re-export it.

Follow these ownership rules:

- Keep service documentation in the service `README.md`.
- Keep production-shaped, copyable examples in `examples/`.
- Keep behaviour, integration, fixtures, and support code in `tests/`.
- Import a sibling service through its public barrel instead of reaching into private implementation files.
- Export every supported application API through the owning service `index.ts`.
- Keep app-specific models, jobs, prompts, routes, and orchestration in the consuming app.
- Assign cross-service integration tests to the service whose public outcome they assert; do not create a package-root `tests/` directory.

Examples and tests have different consumers. Examples should contain complete application-shaped usage without assertions or test-runner APIs. Tests should import and execute those examples with deterministic framework components and controlled external providers. Documentation should render the example source instead of maintaining a copied code string.

Production builds must exclude colocated `tests/` and `examples/` directories. Dedicated test and example checks should still type-check them, and documentation verification should fail when a rendered example no longer matches its service-owned source. Run the complete package suite with `npm test --workspace packages/app` or one owning service with `npm run test:service --workspace packages/app -- {service}`.

Co-location does not mean a service is already independent. Record current sibling dependencies in its README and preserve them as explicit boundaries. Queue currently depends on database and logging services and is composed by the application root; those dependencies can become package dependencies if Queue is extracted later.

## Contract Files

Use a `contracts/` directory inside a framework module when a type or interface represents a public service boundary, driver boundary, lifecycle API, payload envelope, options object, or result shape.

Good examples:

```text
packages/app/src/queue/
	contracts/
		QueueDriver.ts
		QueuePayload.ts
		QueueService.ts
		QueueWorkerLifecycle.ts
		QueueableJob.ts
		index.ts
	Queue.ts
	QueueWorker.ts
	QueueableJob.ts
```

Prefer `contracts/` over `interfaces/`. Contracts are not only TypeScript `interface` declarations; they can include public type aliases, payload envelopes, options, and result shapes that form the API.

Name contracts by purpose, not by implementation syntax. Use `QueueService`, `QueueDriver`, and `QueueWorkerLifecycle`, not `IQueueService` or `IQueueWorker`.

Keep one main concept per file. Related payload fields can live together when splitting them would make the API harder to understand, for example `QueuePayload.ts` can own `JobEnvelope`, `QueueJob`, `DispatchOptions`, and `QueueProcessResult`.

## Imports

Implementation files should usually import contracts as a namespace:

```ts
import type * as queue from './contracts';
```

Then refer to contract types through that namespace:

```ts
export class Queue implements queue.QueueService {
	private readonly driver: queue.QueueDriver;

	async dispatch(
		job: string,
		data: Record<string, unknown>,
		options: queue.DispatchOptions = {},
	): Promise<queue.QueueJobId> {
		// ...
	}
}
```

Keep runtime imports separate from contract imports:

```ts
import { QueueWorker } from './QueueWorker';
import type * as queue from './contracts';
```

Avoid long named type import lists from shared type buckets. If a file needs many public queue contracts, that is a signal to use the module namespace.

## Exports

The module barrel should export the public contracts:

```ts
export * from './contracts';
```

Concrete implementation files may re-export their closely related contracts when that keeps existing import paths intuitive:

```ts
export type { QueueService, QueueOptions } from './contracts';
```

Do not keep duplicate public type buckets such as `types.ts` beside a formal `contracts/` directory. One public source of truth is easier to maintain and document.

## Local Types

Keep private helper shapes near the implementation when they are not part of the framework API:

```ts
interface ParsedInternalRow {
	id: number;
	payload: unknown;
}
```

Promote a local type into `contracts/` when app code, another framework module, a driver, a worker, a public method, or documentation needs to rely on it.

## Comments

Public framework APIs need production-standard JSDoc block comments. This includes exported classes, functions, type aliases, interfaces, options objects, result objects, and non-obvious fields.

Use comments to explain ownership and boundaries, not just repeat names. A useful contract comment says who creates the shape, who consumes it, and what a caller can rely on.

Good field comments:

```ts
/**
 * Per-dispatch overrides for queueing one job.
 */
export interface DispatchOptions {
	/**
	 * Named queue/channel the job should be pushed onto.
	 *
	 * Workers process one named queue at a time, so this lets callers route
	 * different classes of work to different worker pools. When omitted, the
	 * queue service uses its configured default queue name.
	 */
	queue?: string;
}
```

Add `@param` and `@returns` for functions and methods that form part of an API. Add `@example` when a caller would otherwise need to inspect tests or implementation to understand intended usage.

Private functions should also have docblocks when their purpose, boundary, or failure behavior is not obvious. Do not add empty narration to trivial code.

## Review Checklist

Before finishing a framework contract change:

1. Check whether the public shape belongs in `contracts/` or should stay local.
2. Use namespace imports for contract-heavy implementation files.
3. Keep runtime imports and type contracts visually separate.
4. Remove duplicate type buckets after moving the contract.
5. Add JSDoc to exported contracts and non-obvious fields.
6. Run focused tests and type checks for both the framework package and any app that consumes it.
7. Update README or architecture docs when the public API, convention, or usage changed.
8. Add or update a service-owned example when a common application workflow changed.
9. Keep service tests, fixtures, and example verification inside the owning service directory.
````

## Guidance for AI tools
Use the documented public import `@db3.ai/app` and its exported types. Prefer the source-backed examples and behavioural outcomes above over invented APIs or source-relative internal imports.
