Skip to content

Commit

Permalink
Finally the Risk page makes sense :)
Browse files Browse the repository at this point in the history
  • Loading branch information
alexewerlof committed Sep 11, 2024
1 parent 6c7d3d9 commit 08e8c3d
Show file tree
Hide file tree
Showing 8 changed files with 228 additions and 41 deletions.
46 changes: 46 additions & 0 deletions config.js
Original file line number Diff line number Diff line change
Expand Up @@ -373,4 +373,50 @@ export const config = fz({
step: 0.01,
default: 10,
},
impactLevel: fz({
default: 2,
possibleValues: fz([
{
title: 'Low',
value: 1,
},
{
title: 'Medium',
value: 2,
},
{
title: 'High',
value: 3,
},
{
title: 'Critical',
value: 4,
},
]),
}),
likelihood: fz({
default: 3,
possibleValues: fz([
{
title: 'Rare',
value: 1,
},
{
title: 'Unlikely',
value: 2,
},
{
title: 'Possible',
value: 3,
},
{
title: 'Likely',
value: 4,
},
{
title: 'Certain',
value: 5,
},
]),
}),
})
18 changes: 15 additions & 3 deletions models/assessment.js
Original file line number Diff line number Diff line change
@@ -1,9 +1,7 @@
import { System } from '../models/system.js'
import { Consumer } from '../models/consumer.js'
import { isInstance } from '../lib/validation.js'
import { Service } from './service.js'
import { Consumption } from './consumption.js'
import { Dependency } from './dependency.js'
import { config } from '../config.js'

export class Assessment {
constructor() {
Expand All @@ -27,6 +25,20 @@ export class Assessment {
return this.allDependencies.flatMap(dependency => dependency.failures)
}

get allRisks() {
return this.allFailures.flatMap(failure => failure.risk)
}

getRisks(likelihood, impactLevel) {
if (!config.likelihood.possibleValues.map(({ value }) => value).includes(likelihood)) {
throw new RangeError(`Expected likelihood to be one of ${config.likelihood.possibleValues}. Got ${likelihood}`)
}
if (!config.impactLevel.possibleValues.map(({ value }) => value).includes(impactLevel)) {
throw new RangeError(`Expected impactLevel to be one of ${config.impactLevel.possibleValues}. Got ${impactLevel}`)
}
return this.allRisks.filter(risk => risk.likelihood === likelihood && risk.impactLevel === impactLevel)
}

addSystem(system) {
if (!isInstance(system, System)) {
throw new Error(`Expected an instance of System. Got ${system}`)
Expand Down
3 changes: 2 additions & 1 deletion models/failure.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import { isInstance } from '../lib/validation.js'
import { Dependency } from './dependency.js'
import { Risk } from './risk.js'

// If a certain service fails, what activities will it impact and how?
export class Failure {
Expand All @@ -10,7 +11,7 @@ export class Failure {
this.dependency = dependency
this.symptom = symptom
this.consequence = consequence
this.businessImpact = businessImpact
this.risk = new Risk(this, businessImpact)
}

toString() {
Expand Down
19 changes: 19 additions & 0 deletions models/risk.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
import { config } from '../config.js'

export class Risk {
constructor(
failure,
businessImpact,
likelihood = config.likelihood.default,
impactLevel = config.impactLevel.default
) {
this.failure = failure
this.businessImpact = businessImpact
this.likelihood = likelihood
this.impactLevel = impactLevel
}

toString() {
return `${this.failure.consequence} - ${this.businessImpact}`
}
}
78 changes: 78 additions & 0 deletions views/risk-view.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
<div>
<div class="annotated">
<div class="annotated__controls">
<h2>Business Impact</h2>
<input type="text" v-model="risk.businessImpact">
</div>
<div class="annotated__help">
<p>
What is the business impacted when
{{ risk.failure.consequence }}
because
{{ risk.failure.symptom }} (symptom)
which is caused by
{{ risk.failure.dependency.service.title }}
(provided by {{ risk.failure.dependency.service.system.title }})
when
{{ risk.failure.dependency.consumption.consumer.title }}
is trying to
{{ risk.failure.dependency.consumption.title }}
</p>
<code>
<span title="Consequence" class="exprs">
{{ risk.failure.consequence }}
</span>
<br>
&nbsp;<span class="punct"></span>
Because
<span title="Symptom" class="exprs">
{{ risk.failure.symptom }}
</span>
<br>
&nbsp;&nbsp;<span class="punct"></span>
When
<span title="Consumer" class="exprs">
{{ risk.failure.dependency.consumption.consumer.title }}
</span>
is trying to
<span title="Consumption" class="exprs">
{{ risk.failure.dependency.consumption.title }}
</span>
<br>
&nbsp;&nbsp;&nbsp;<span class="punct"></span>
Using
<span title="Service" class="exprs">
{{ risk.failure.dependency.service.title }}
</span>
provided by
<span title="System" class="exprs">
{{ risk.failure.dependency.service.system.title }}
</span>
</code>
</div>
</div>

<div class="annotated">
<div class="annotated__controls">
<h2>Impact Level</h2>
<select v-model="risk.impactLevel">
<option v-for="impactLevel of config.impactLevel.possibleValues"
:value="impactLevel.value">
{{ impactLevel.title }}
</option>
</select>
<h2>Likelihood</h2>
<select v-model="risk.likelihood">
<option v-for="likelihood of config.likelihood.possibleValues"
:value="likelihood.value">
{{ likelihood.title }}
</option>
</select>
</div>
<div class="annotated__help">
<p>
YY
</p>
</div>
</div>
</div>
27 changes: 27 additions & 0 deletions views/risk-view.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
import { loadComponent } from '../lib/fetch-template.js'
import { isInstance } from '../lib/validation.js'
import { config } from '../config.js'
import { percL10n } from '../lib/fmt.js'
import ExtLink from '../components/ext-link.js'
import { Risk } from '../models/risk.js'

export default {
template: await loadComponent(import.meta.url, true),
computed: {
config() {
return config
}
},
props: {
risk: {
type: Object,
validator: v => isInstance(v, Risk),
},
},
methods: {
percL10n,
},
components: {
ExtLink,
},
}
8 changes: 6 additions & 2 deletions workshop/app.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,11 @@ import ExtLink from '../components/ext-link.js'
import SystemView from '../views/system-view.js'
import ConsumerView from '../views/consumer-view.js'
import DependencyView from '../views/dependency-view.js'
import RiskView from '../views/risk-view.js'
import { System } from '../models/system.js'
import { Consumer } from '../models/consumer.js'
import { Assessment } from '../models/assessment.js'
import { config } from '../config.js'

export const app = createApp({
data() {
Expand All @@ -32,7 +34,7 @@ export const app = createApp({

const dep1 = webClientConsumer.consumptions[0].addDependency(apiServerSystem.services[0])
dep1.addNewFailure(
'Web page response is slow',
'Web page response is too slow',
'User may leave',
'Loss of potential customer',
)
Expand All @@ -55,15 +57,17 @@ export const app = createApp({

const tabNames = ['Start', 'Provider', 'Consumers', 'Failures', 'Risks', 'Service Levels']
return {
selectedTab: tabNames[0],
selectedTab: tabNames[4],
tabNames,
assessment,
config,
}
},
components: {
DependencyView,
SystemView,
ConsumerView,
RiskView,
TabsComponent,
ShowHideComponent,
ExtLink,
Expand Down
70 changes: 35 additions & 35 deletions workshop/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -228,41 +228,41 @@ <h2>Mental Model</h2>
</div>

<div v-if="selectedTab === 'Risks'">
<section>
<h1>Risks</h1>
<p>What are the risks associated with the failures? Sort them based on the impact on the business.</p>
<table>
<thead>
<tr>
<th colspan="2">Provicer</th>
<th colspan="2">Consumer</th>
<th colspan="2">Business</th>
</tr>
<tr>
<th>Service</th>
<th>Symptom</th>
<th>Consumption</th>
<th>Consequence</th>
<th>Business Impact</th>
<th>Priority</th>
</tr>
</thead>
<tbody>
<tr v-for="failure in assessment.allFailures">
<td>{{ failure.dependency.service || '⚠️TBD' }}</td>
<td>{{ failure.symptom || '⚠️TBD' }}</td>
<td>{{ failure.dependency.consumption || '⚠️TBD' }}</td>
<td>{{ failure.consequence || '⚠️TBD' }}</td>
<td>
<input type="text" v-model="failure.businessImpact">
</td>
<td>
<button @click="failureUp(failure)">🔼</button>
<button @click="failureDown(failure)">🔽</button>
</td>
</tr>
</table>
</section>
<div class="annotated">
<div class="annotated__controls">
<table>
<thead>
<tr>
<th>Likelihood</th>
<th v-for="impactLevel of config.impactLevel.possibleValues">
{{ impactLevel.title }} Impact Level
</th>
</tr>
</thead>
<tbody>
<tr v-for="likelihood of config.likelihood.possibleValues">
<th>{{ likelihood.title }}</th>
<td v-for="impactLevel of config.impactLevel.possibleValues">
{{ assessment.getRisks(likelihood.value, impactLevel.value).length }}
</td>
</tr>
</tbody>
</table>
</div>
<div class="annotated__help">
<p>
Risks are tied to the failures.
The idea with this page is to think about the business impact of
the failures in order to prioritize which symptoms should
be prioritized for the service level first.
</p>
</div>
</div>
<show-hide-component
v-for="risk of assessment.allRisks"
title="Risk"
:name="risk">
<risk-view :risk="risk"></risk-view>
</div>
</main>
<script type="module" src="index.js" defer async></script>
Expand Down

0 comments on commit 08e8c3d

Please sign in to comment.