Deciding where a decision belongs
What you want to do
The shop will keep gaining rules, and for each new one you want to know where it belongs: in a step, in a rescue step, in the workflow or in a policy.
The code
Every decision the shop has made so far has its place. The workflow's declaration, as chapter 23 shows it, names the steps, their policies, the rescue step and the retry budget; the ID generator, the providers and the input adapter are the workflow's methods, shown in chapter 11, chapter 12, chapter 13, chapter 21, chapter 22 and chapter 35:
#[workflow(name = "orders", reporters = [Log, Desk], policies = [Notify])]
#[with_step(Lookup, name = "lookup", policies = [AlreadyCompleted])]
#[with_step(Price, name = "price")]
#[with_step(Charge, name = "charge", rescue = RenewCard, policies = [Audit, Outage])]
#[with_step(Ship, name = "ship", retry_budget = 1, policies = [FailedShipping])]
struct Orders {
channel: &'static str,
}The rest is as earlier chapters show it: lookup and already_completed in chapter 22, price in chapter 20, charge in chapter 33, charge_rescue in chapter 35, outage and audit in chapter 34, ship in chapter 17, failed_shipping in chapter 23, notify in chapter 19, the log in chapter 33, chapter 35 and chapter 36, and the desk in chapter 11.
| Decision | Where it lives | Why there |
|---|---|---|
| Whether the order was already completed | the lookup step |
reading the order book is the business's work |
| Ending the journey of an order already completed | the already_completed policy |
it decides what happens after the lookup's outcome |
| The amount, less any discount | the price step |
it is a rule of the business |
| Whether the card can be charged | the charge step |
it is what the business does with the order |
| Renewing an expired card | the rescue step, charge_rescue |
it repairs why the charge failed, so the charge can run once more |
| Giving up a charge that ended in the bank's error | the outage policy |
it decides what happens after an outcome |
| Writing every charge down | the audit policy |
it reacts to an outcome, and changes nothing in the charge |
| A gift card has nothing to ship | the ship step, which skips |
only the step knows its domain well enough |
| Shipping tried twice, with another carrier the second time | the workflow: a retry budget, and a provider for every attempt | how often to try, and with what, is the plan, not the rule |
| Refunding a shipping given up | the failed_shipping policy, through a role |
it reacts to a step given up, and acts on the outside world |
| Telling the customer | the notify policy, through a role |
it reacts to how the journey ended |
| The weight out of the warehouse's parcel | the workflow's input adapter | it changes the form of a step's data, and decides nothing |
How it works
Ask these questions about a new decision, in this order. The first answer that fits says where it belongs.
flowchart TD
accTitle: Where a decision belongs
accDescr: A decision that repairs why a step failed goes in a rescue step. Otherwise, a decision about what the business does goes in a step. Otherwise, how often a step is tried, and which services it gets, goes in the workflow. Anything else, deciding what happens after an outcome or acting on the outside world after one, goes in a policy's hook.
Q1{Does it repair why a step failed?} -->|yes| R[A rescue step]
Q1 -->|no| Q2{Is it what the business does?}
Q2 -->|yes| S[A step]
Q2 -->|no| Q3{Is it how often to try, or with what?}
Q3 -->|yes| W[The workflow]
Q3 -->|no| P[A policy's hook]The diagram asks three questions in turn, with four answers:
- A decision that repairs why a step failed, so that the step can run once more, belongs in a rescue step.
- Otherwise, a decision about what the business does, such as pricing, charging or shipping, belongs in a step.
- Otherwise, how often a step is tried, and which services it gets, belongs in the workflow's declaration.
- Otherwise, the decision says what happens after an outcome, or acts on the outside world after one, and belongs in a policy's hook.
Every decision in the table answers one of these questions; the adapter, which decides nothing, only changes the form of a step's data. Each place holds one kind of decision, and the journey records each one where it was taken: a step's outcome, a rescue step's repair, a retry the workflow allows, and the hook a policy ran.
But a rule put in the wrong place often still runs, and nothing in the shop's orders says so.
Outcomes
We have shown how to place a new decision of the shop.
| The usual way | With Itinera |
|---|---|
| A business function often repairs, retries and notifies inside itself, because that is where the problem shows up. | A step does the business's work and says how it went; repairing, retrying and reacting each have a place of their own. |