-
-
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.