---
name: mutation-command
description: Add a write/submit operation — infrastructure adapter method plus an application command factory returning Result, keeping the reducer pure. Use for any POST/PUT/DELETE a form or action triggers.
---

# Mutation command

Reducer = "what the new state is"; command = "go do it, then say what happened".
The UI holds an application command, never the network client (lint-enforced).

## Skeleton

**Adapter method** (`<context>/infrastructure/<name>.adapter.ts`) — takes the
machine's `Valid` type, returns the server's answer, no parse (server is authority):

```ts
@Injectable({ providedIn: 'root' })
export class ChangeRequestAdapter {
  private client = inject(ApiClient);
  async changeRequest(data: Valid): Promise<string> {
    /* → server reference */
  }
}
```

**Command factory** (`<context>/application/submit-<name>.ts`) — binds the adapter in
an injection context; `runSubmit` is the one try/catch + ProblemDetails mapping:

```ts
export function createSubmitChangeRequest() {
  const adapter = inject(ChangeRequestAdapter);
  return (data: Valid): Promise<Result<string, string>> =>
    runSubmit(() => adapter.changeRequest(data), SUBMIT_FAILED);
}
```

**Caller** (organism, field initializer — same shape as `createStore`): on the
machine's `Submitting` state, run the command, then dispatch
`SubmitConfirmed` / `SubmitFailed { error }`. Never throw into the template.

## Optimistic updates on shared state

When a root store's view must reflect the write before the server confirms:
`begin*` (flip pending) → `confirm*` (clear + `resource.reload()`) / `rollback*`
(undo). See `src/app/registratie/application/big-profile.store.ts`.

## Worked examples

- `src/app/registratie/infrastructure/change-request.adapter.ts`
- `src/app/registratie/application/submit-change-request.ts`
- `src/app/registratie/application/draft-sync.ts` — `createDraftSync(...)` bundles
  draft persistence + submit for wizard flows; prefer it when the form has a draft.
- `src/app/shared/application/submit.ts` — `runSubmit`, `SUBMIT_FAILED`.

## Verify

```bash
npm test && npm run lint && npm run build
# exercise the failure path in the browser: ?scenario=error on the submitting page
```
