-
Notifications
You must be signed in to change notification settings - Fork 25
Toevoeging async request-response patroon #631
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Open
terborg
wants to merge
18
commits into
developer-overheid-nl:main
Choose a base branch
from
terborg:kennisbank-updates
base: main
Could not load branches
Branch not found: {{ refName }}
Loading
Could not load tags
Nothing to show
Loading
Are you sure you want to change the base?
Some commits from the old base branch may be removed from the timeline,
and old review comments may become outdated.
Open
Changes from all commits
Commits
Show all changes
18 commits
Select commit
Hold shift + click to select a range
c706376
async request-reply stuk toegevoegd
terborg 7d489dd
openapi voorbeeld toegevoegd
terborg d28ca7e
fix
terborg df2ec38
fix2
terborg 7fc714d
Extra lijn toegevoegd, use cases naar voren gehaald
terborg 370e7c4
Feedback van Copilot verwerkt
terborg dca9501
Retry-After header toegevoegd als toelichting op de polling variant v…
terborg ab29b7d
retry-after ook aan openapi voorbeeld toegevoegd
terborg 2cf9c67
Zin toegevoegd over statusendpoint aan sectie asynchroon verwerken
terborg f90f7e4
Use cases verbreed, diagram gegeneraliseerd en terminologie naar cons…
terborg 7284903
Taalkundige en terminologische verfijningen
terborg eb43653
Nadelensectie toegevoegd
terborg 6ee862e
Transactionele outbox aangehaald in werking punt 2
terborg a9fcb77
Tekstuele verbeteringen
terborg b95dac2
Tekstuele verbeteringen
terborg d32030e
Verduidelijking voordeel domeinkennis
terborg eb8ec95
Minimale edit
terborg 63b9b06
Punt 1 aangescherpt, niet altijd transactionele outbox nodig
terborg File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
202 changes: 202 additions & 0 deletions
202
docs/api-ontwikkeling/architectuur/async-request-reply.md
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,202 @@ | ||
| --- | ||
| content_type: architectuur | ||
| tags: | ||
| - api | ||
| - rest | ||
| --- | ||
|
|
||
| # Asynchronous Request-Reply Pattern | ||
|
|
||
| Het Asynchronous Request-Reply pattern is ideaal voor operaties die lang duren, | ||
| onvoorspelbaar zijn qua tijdsduur of verloop, óf waarbij de provider een | ||
| vervolgactie teruglegt bij de consumer. Denk aan het genereren van rapportages, | ||
| batch-updates, of processen waarbij de provider direct een verwijzing teruggeeft | ||
| — zoals een URL waarnaar de consumer asynchroon een bestand moet uploaden. | ||
|
|
||
| ## Problemen bij synchrone verwerking | ||
|
|
||
| Wanneer een consumer een langdurige of onvoorspelbare operatie start (zoals | ||
| documentverwerking of batch-updates) en synchroon wacht op het resultaat, kunnen | ||
| timeouts optreden. Het verhogen van de timeout-limiet is hierbij een _bad | ||
| practice_, omdat dit resources aan de providerkant onnodig lang bezet houdt. | ||
| Bovendien zijn timeouts vaak afhankelijk van de netwerkverbinding en | ||
| tussenliggende infrastructuur, wat de limiet arbitrair maakt en geen | ||
| succesgarantie biedt. De consumer blijft dan in onzekerheid over de status, wat | ||
| leidt tot onbetrouwbaarheid en potentieel dubbele verwerking bij retries. Dit | ||
| resulteert in een slechte gebruikerservaring en mogelijke data-inconsistentie. | ||
|
|
||
| ## Asynchroon verwerken | ||
|
|
||
| Het Asynchronous Request-Reply pattern lost dit op door het request los te | ||
| koppelen van de verwerking. De provider accepteert de operatie en geeft | ||
| onmiddellijk een bevestiging, waarna de verdere afhandeling asynchroon | ||
| plaatsvindt. De consumer wordt via een statusendpoint op de hoogte gehouden van | ||
| de voortgang. | ||
|
|
||
| ### Werking | ||
|
|
||
| Het verloop is als volgt: | ||
|
|
||
| 1. **Request**: De consumer stuurt een `POST`-request om een operatie te | ||
| starten. Zie ook | ||
| [Veilige retries met volledige idempotency](./retries-met-volledige-idempotency.md) | ||
| voor veilige retries. Leg de geaccepteerde operatie duurzaam vast voordat je | ||
| `202 Accepted` retourneert, zodat deze intern niet verloren kan gaan. | ||
| 2. **Acceptatie**: De provider valideert de aanvraag en stuurt direct een | ||
| [`202 Accepted`](https://www.rfc-editor.org/rfc/rfc9110#name-202-accepted) | ||
| response met een `Location` header naar het statusendpoint. De response body | ||
| kan aanvullende informatie bevatten, zoals een upload-URL of referenties naar | ||
| gerelateerde (vervolg)acties voor de consumer. | ||
| 3. **Status opvragen**: De consumer pollt het statusendpoint met `GET`-requests. | ||
| De provider kan een advies meegeven over het volgende pollmoment, | ||
| bijvoorbeeld met een | ||
| [`Retry-After`](https://www.rfc-editor.org/rfc/rfc9110#name-retry-after) | ||
| header. Voor directe updates zonder polling zijn Server-Sent Events (SSE) of | ||
| Webhooks geschikter (zie [Event-Driven Architecture](./eda.md)). | ||
| 4. **Statusupdate**: Het statusendpoint geeft de huidige status (bijv. | ||
| "Processing"), eventueel met een voortgangspercentage of schatting van de | ||
| resterende tijd. | ||
| 5. **Voltooiing**: Bij voltooiing meldt het endpoint "Succeeded" met een link | ||
| naar of inhoud van het resultaat, of "Failed" met bijvoorbeeld | ||
| [Problem Details](./problem-details.md). Als alternatief kan het | ||
| statusendpoint bij succesvolle voltooiing antwoorden met een `303 See Other` | ||
| redirect naar de URL van het uiteindelijke resultaat. | ||
|
|
||
| Hieronder het sequentiediagram voor de polling-variant van "Status opvragen". | ||
|
|
||
| ```mermaid | ||
| sequenceDiagram | ||
| participant Consumer | ||
| participant Provider | ||
| participant Uitvoerder | ||
|
|
||
| Consumer->>Provider: POST /operaties | ||
| note right of Provider: Aanmaken (status: Pending) | ||
| Provider->>Uitvoerder: Starten | ||
| Provider->>Consumer: 202 Accepted (Location: /status/123) | ||
|
|
||
| activate Uitvoerder | ||
| Uitvoerder-->>Uitvoerder: Verwerken... | ||
|
|
||
| loop Poll-loop | ||
| Consumer->>Provider: GET /status/123 | ||
| alt Nog bezig | ||
| Provider->>Consumer: 200 OK (status: Processing, Retry-After: 5) | ||
| else Geslaagd | ||
| Uitvoerder->>Provider: Voltooid (succeeded) | ||
| Provider->>Consumer: 200 OK (status: Succeeded) | ||
| else Mislukt | ||
| Uitvoerder->>Provider: Voltooid (failed) | ||
| Provider->>Consumer: 200 OK (status: Failed) | ||
| end | ||
| end | ||
| deactivate Uitvoerder | ||
| ``` | ||
|
|
||
| ## Voorbeeld in OpenAPI | ||
|
|
||
| Hieronder een deel van een voorbeeld van hoe je dit patroon in een OpenAPI | ||
| specificatie kunt vastleggen, met de start van de operatie en een apart | ||
| statusendpoint. | ||
|
|
||
| ```yaml | ||
| paths: | ||
| /operaties: | ||
| post: | ||
| summary: Start een asynchrone operatie | ||
| parameters: | ||
| - name: Idempotency-Key | ||
| in: header | ||
| # ... | ||
| requestBody: | ||
| required: true | ||
| content: | ||
| application/json: | ||
| schema: | ||
| $ref: "#/components/schemas/MijnAanvraag" | ||
| responses: | ||
| "202": | ||
| description: Aanvraag geaccepteerd en asynchroon in verwerking genomen | ||
| headers: | ||
| Location: | ||
| description: URL van het statusendpoint voor deze operatie. | ||
| schema: | ||
| type: string | ||
| format: uri | ||
| content: | ||
| application/json: | ||
| schema: | ||
| $ref: "#/components/schemas/OperatieStatus" | ||
| /status/{operatieId}: | ||
| get: | ||
| summary: Vraag de status van een operatie op | ||
| parameters: | ||
| - name: operatieId | ||
| in: path | ||
| required: true | ||
| schema: | ||
| type: string | ||
| format: uuid | ||
| responses: | ||
| "200": | ||
| description: Huidige status van de operatie | ||
| headers: | ||
| Retry-After: | ||
| description: | ||
| Aanbevolen wachttijd in seconden voor de volgende poll, indien | ||
| de operatie nog niet voltooid is. | ||
| schema: | ||
| type: integer | ||
| content: | ||
| application/json: | ||
| schema: | ||
| $ref: "#/components/schemas/OperatieStatus" | ||
| "404": | ||
| description: Operatie niet gevonden | ||
|
|
||
| components: | ||
| schemas: | ||
| OperatieStatus: | ||
| type: object | ||
| required: | ||
| - status | ||
| properties: | ||
| status: | ||
| type: string | ||
| enum: | ||
| - Pending | ||
| - Processing | ||
| - Succeeded | ||
| - Failed | ||
| resultaatUrl: | ||
| type: string | ||
| format: uri | ||
| description: | ||
| URL van het uiteindelijke resultaat zodra de operatie is voltooid. | ||
| ``` | ||
|
|
||
| ### Voordelen | ||
|
|
||
| - **Verbeterde gebruikerservaring**: De consumer krijgt direct feedback en hoeft | ||
| niet te wachten op voltooiing. | ||
| - **Betrouwbaarheid**: Timeouts aan de consumerkant worden voorkomen; de status | ||
| is altijd opvraagbaar. | ||
| - **Minder domeinkennis bij de consumer**: Vervolgstappen komen vanuit de | ||
| provider; de consumer hoeft de volgorde van stappen niet vooraf te kennen. | ||
| Denk aan domein-specifieke afhankelijkheden waarbij de ene resource eerst | ||
| aangemaakt moet zijn vóórdat een andere aangemaakt kan worden — en waarbij die | ||
| volgorde per type object kan verschillen. Zonder dit patroon moet de consumer | ||
| die volgorde per situatie kennen en zelf de juiste resources en acties in de | ||
| juiste volgorde aanroepen. Met dit patroon geeft de provider na acceptatie de | ||
| benodigde vervolgacties terug — bijvoorbeeld als links in de status response | ||
| body — zodat de consumer het proces stap voor stap kan doorlopen zonder die | ||
| domeinkennis zelf te bevatten. | ||
|
|
||
| ### Nadelen | ||
|
|
||
| - **Meer technische complexiteit bij de consumer**: In plaats van één | ||
| request-response-cyclus moet de consumer de afhandeling van asynchrone | ||
| statussen implementeren — via polling, SSE of webhooks — inclusief | ||
| retry-afhandeling en statusinterpretatie. | ||
| - **Persistente toestand bij de provider**: De provider moet operatiestatus | ||
| opslaan en na verloop van tijd opschonen. | ||
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.