0:00 So in order to actually begin the workflow, well, the first step, 0:04 as we will call it, is to charge the user. 0:06 We want to try to charge their card because, well, if their card does 0:10 not successfully get billed, we don't want to proceed. 0:13 We don't want to take off the inventory from our store, 0:17 and of course, we don't want to actually deliver the 0:21 order. So the very first step as part of the 0:25 workflow is going to be 0:27 charge. So I'm going to create a const charge, 0:30 and now we 0:31 await context.step. So 0:35 step, as we can see here, is going to run the function we provided 0:39 and journal its result into our Postgres store. 0:42 Critically, replays of the same step which already exist in the 0:46 journal will simply return its return value without actually 0:50 rerunning the s- step. 0:53 And this is a critical part, is that after the step is successfully run, 0:57 we are guaranteed to never run it again. 0:59 Instead, we just replay the re-- original result of it. 1:03 So into this step, we'll first pass in the name of the 1:06 step, which we'll identify it in the database as 1:09 the journal, 1:10 and we'll call this charge payment. 1:13 Make sure we also mark our run method as async. 1:17 And so this parameter into step is going to be the app callback 1:20 function, which is what we're going to do the work, 1:24 and we're going to 1:24 store that result into the journal. Sure that it gets repeated. 1:28 If it's ever run again, instead of actually re-executing, 1:31 we just return 1:32 the saved result from the database. 1:35 And so in this callback function, we're going to use the payment 1:38 service 1:40 and call our charge method to charge the user. 1:43 Now, this callback function also takes in a 1:47 step context object where we get some details about the 1:51 step itself. 1:53 Now, on here, we also get an idempotency key that 1:57 NestJS workflow automatically gives us. 2:00 And as we can see here, this is composed of the workflow 2:04 ID:step name. And so this is going to be 2:08 identical for every attempt and every worker that 2:11 runs this step. It's creating this unique idempotency key 2:16 for the workflow instance and this particular step, 2:20 such that we can now provide it to the payment service, which we know uses the 2:24 idempotency key 2:26 to never duplicate a payment. And this is automatically going to ensure 2:30 that if this ever does get retried, 2:34 because if we don't succeed at the first time, we will keep retrying. 2:38 Well, we want to make sure that the payment didn't actually already 2:41 succeed, and 2:42 so we can now pass in the order itself. 2:46 That'll rename from the input. 2:48 So that's the order that we are charging, and then the idempotency key that it 2:52 expects. 2:54 And so now we are invoking the payment service and 2:58 ensuring this logic only ever occurs one time, thanks to the 3:01 idempotency key. Now, as we mentioned before, a big benefit of 3:05 NestJS workflow is that it will automatically retry any 3:09 failures by default. And so if payment service fails for whatever 3:13 reason, maybe the external provider is down, it's automatically going to get 3:17 retried. And we can configure and fine-tune these 3:20 retry settings further into the next parameter, 3:25 which is an options object where we can configure things 3:29 like retries. So here we have a retry object where we 3:33 can change the number of retries we have. 3:37 Default is three. We can say we want to override it to five, 3:41 and 3:41 we can also provide a backoff to wait between attempts. 3:46 So we can configure a backoff 3:49 object and set a delay to two seconds, for 3:52 example, which is going to wait two seconds between 3:55 each of our five 3:56 retries for the payment service. And this is going to be really nice 4:00 because if the payment service is-- we now are waiting for it to come back up 4:04 and automatically retrying the payment without failing the 4:08 workflow itself. Now, another key benefit to the workflow 4:12 steps, as, as we've talked about 4:15 in setting up our services, is we have the ability 4:19 to undo them. And that is what this compensate 4:22 parameter is for. And so what this is, as we 4:26 can see in the description, is it essentially will just undo this 4:30 step. 4:30 If the workflow fails or is canceled, compensations run 4:34 in reverse order of the context.step calls, 4:38 each one journaled and retried like a step. 4:42 And so this is really nice. It allows us to undo 4:45 whatever we did inside of the workflow step. 4:50 If the workflow has failed and we want to roll back these operations 4:54 to 4:54 keep everything fully atomic, which is very common in our business 4:58 workflow. In this case, if the delivery, get delivered user 5:02 in time or something else goes wrong, returned or timed out, 5:06 we want to undo the payment, refund the user, 5:10 and release the inventory. So that's exactly what we can do inside 5:14 of compensate. Now, critically, whatever gets returned from the 5:18 original 5:20 step function, in this case, payment service.charge, will be 5:24 available as input parameter for 5:27 compensate. So in this case, we know that charge returns us 5:31 back this charge interface with the charge 5:35 ID and the amount. And so we can destructure 5:39 that from the first parameter here inside of compensate charge 5:43 ID. 5:44 And then the second parameter is our workflow step options, 5:48 the same step options from the invocation of the 5:52 step method. And so here we can go ahead 5:55 and destructure the 5:56 idempotency key. 5:58 This is because- 6:00 We are going to pass this into payment service dot 6:04 refund. So we pass through the charge ID, the 6:08 idempotency key. 6:10 And so this is going to refund the original payment that charge 6:14 actually has charged in the event that this workflow fails all 6:18 automatically. And just like we saw here in the 6:20 description, if for whatever 6:22 reason compensate itself fails, refund service is 6:25 down, then 6:28 it'll get retried just the same as the step function itself. 6:32 And that's why we're passing the idempotency key again to make sure this 6:35 happened more than once, because we have this fault tolerance built 6:39 into the workflow. And now at this point, our first step 6:43 method is fully configured and complete working end to end. 6:47 After we execute this, we will invoke the payment service dot 6:51 charge, restore the result of that inside of our Postgres 6:55 journal so that we can resume this workflow later without reduplicating 6:59 this work. 7:01 And we have retry functionality built in out of the box and the 7:04 ability to even undo it. This is excellent. 7:08 So at this point, we've charged the user's card, 7:11 and the next time that we enter 7:12 this workflow instance, 7:15 we will call the same step. We can see that this 7:19 result was already stored in the database so it doesn't rerun. 7:22 This is a key understanding to these workflows, is that they do get called 7:27 more than once. The instance itself will resume 7:30 and call run 7:31 again multiple times, and we'll see when that happens later 7:35 on. And so this is the key aspect, is that we don't reduplicate work 7:38 because we're storing the result of these steps inside the 7:42 database. And so now what we can also do 7:45 is use the workflow 7:46 context object and use dot set status, 7:50 which lets us set the instance's custom status, which 7:54 is just any sort of value that we want to provide 7:58 that can be used later on when we want to get the same workflow 8:01 instance and check to see what state it is currently in 8:05 for monitoring or debugging purposes. 8:08 So I'm just going to set this status to paid because at this point we have paid. 8:12 And now when we query back this workflow, which we'll see how we can do later on, 8:17 we can check to see if there are any workflows at or beyond this 8:20 particular status point. So this is completely optional, 8:23 but just helps us to 8:24 visualize and debug the workflow a bit more. 8:28 Now at this point, we've charged the... 8:30 The next step is to reserve our stock using our 8:33 inventory service. That is going to be the next workflow step. 8:38 So let's do this next.