> For the complete documentation index, see [llms.txt](https://docs.soapjs.com/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://docs.soapjs.com/components/repository.md).

# Repository

A **repository** is the domain-facing data-access boundary. Use cases and handlers depend on a tech-free **port**; an **adapter** implements it for a specific database. Swap databases by writing a new adapter — the domain never changes.

### The pattern, by hand

SoapJS is convention-driven (no scaffolding CLI) — you write these pieces directly. The [demo](https://github.com/soapjs/soap-node-demo) is the full reference; here's the shape.

#### 1. Port — the contract + DI token

```typescript
import { Result } from '@soapjs/soap';
import { Character } from '../../domain/character.entity';

export abstract class CharacterRepository {
  static readonly Token = 'CharacterRepository';
  abstract add(...characters: Character[]): Promise<Result<Character[]>>;
  abstract findById(id: string): Promise<Result<Character | null>>;
  abstract findAll(filters?: CharacterFilters): Promise<Result<Character[]>>;
}
```

#### 2. Adapter — the implementation

Extend `ReadWriteRepository` and implement the port. Build queries with `Where` — **no database syntax leaks into the adapter**:

```typescript
import { ReadWriteRepository, FindParams, Where } from '@soapjs/soap';

export class CharacterRepositoryMongo
  extends ReadWriteRepository<Character, CharacterDocument>
  implements CharacterRepository {

  async findById(id: string): Promise<Result<Character | null>> {
    const r = await this.find(FindParams.create({ where: new Where().valueOf('_id').isEq(id), limit: 1 }));
    return r.isFailure() ? Result.withFailure(r.failure) : Result.withSuccess(r.content[0] ?? null);
  }

  async findAll(filters: CharacterFilters = {}): Promise<Result<Character[]>> {
    const where = new Where();
    if (filters.universe) where.valueOf('universe').isEq(filters.universe);
    return this.find(FindParams.create({ where, sort: { name: 1 } }));
  }
}
```

`ReadWriteRepository` provides `add`, `find`, `count`, `update`, `remove`. Compose queries with `Where` + `FindParams` / `UpdateParams` / `RemoveParams`.

#### 3. Wire it in the composition root

```typescript
const repo = new CharacterRepositoryMongo(
  new DatabaseContext(new MongoSource(soapMongo, 'characters'), new CharacterMapper(), soapMongo.sessions),
);
container.bindValue(CharacterRepository.Token, repo);
```

Handlers inject the **port**, never the adapter:

```typescript
@Inject(CharacterRepository.Token) private readonly repo: CharacterRepository;
```

### Querying

* **Where conditions** — typed repo methods (*Using Where Conditions*)
* **RepositoryQuery** — named, reusable query objects (*Using QueryBuilder*)
* **Native queries** — the `Source.native()` escape hatch (*Using Native Queries*)

### CQRS read/write split

For CQRS, expose **two** ports — a read repository and a write repository — backed by separate connections (replica vs primary). See the [demo](https://github.com/soapjs/soap-node-demo).
