-
-
Save fjeldstad/a233c25aae510af2265144605ceda4bc to your computer and use it in GitHub Desktop.
| const service, { sendCommand, query, publishEvent } = require('microservice'); | |
| const accountSuspensionService = service('account-suspension'); | |
| accountSuspensionService.onCommand('suspend-account', async function (command) { | |
| const { id, reason } = command.payload; | |
| const account = await query('get-account-by-id', { id }); | |
| if (account.status !== 'active') { | |
| throw new Error(`Suspending an account requires status 'active', was instead '${account.status}'.`); | |
| } | |
| account.status = 'suspended'; | |
| account.suspendReason = reason; | |
| await sendCommand('update-account', { account, expectedEtag: account._metadata.etag }); | |
| return publishEvent('account-suspended', { id, reason }); | |
| }); | |
| // Automatically suspend the account if it's Stripe subscription is cancelled. | |
| accountSuspensionService.onEvent('stripe-subscription-cancelled', async function (event) { | |
| await sendCommand('suspend-account', { | |
| id: event.payload.metadata.accountId, | |
| reason: 'Stripe subscription cancelled.' | |
| }); | |
| }); | |
| // Also, let an admin suspend accounts manually. | |
| // [below: somewhere in the admin GUI code] | |
| // ... | |
| $('#admin-suspend-account-button').on('click', (event) => { | |
| sendCommand('suspend-account', { id: event.target.value, reason: 'Suspended by administrator.' }); | |
| }); | |
| // Silly example though; normally client code would not have access to `sendCommand` et al. |
Terminologi:
command - en beskrivning av hur man vill ändra modellen/systemet. Kan misslyckas/ignoreras, t.ex. om kommandot är ogiltigt givet aktuellt state (som ovan). Har per definition sidoeffekter.
event - en beskrivning av något som faktiskt har hänt. Kan normalt inte misslyckas (utöver infrastrukturfel förstås).
query - en beskrivning av något man vill läsa ur systemet. Alltid sidoeffektfritt.
Endast en tjänst får hantera en viss kommandotyp eller querytyp. Om man skulle definiera flera tjänster som hanterar samma kommando/query bör systemet kasta ett fel under initfasen.
Däremot får obegränsat antal tjänster prenumerera på samma typ av event. Commands och queries är request/response, events är broadcast.
Infrastrukturmässigt skulle command- och query handlers kunna implementeras med ett enkelt HTTP-API medan event handlers skulle prenumerera på meddelanden från ett pub/sub-system.
I exemplet ovan antas att det finns en annan service som hookat upp
onCommand('update-account', ...)samtonQuery('get-account-by-id', ...), typ enaccount-crud-service. Kan förstås vara två separata lika gärna.