Coding Complex App Logic, Visually
David Khourshid Β· @davidkpiano
Γredev Developer Conference 2022
<form onSubmit={event => {
submitData(event);
}}>
{/* ... */}
</form>
const [isLoading, setIsLoading] = useState(false);
<form onSubmit={event => {
if (isLoading) { return; }
submitData(event);
setIsLoading(true);
}}>
{/* ... */}
</form>
const [isLoading, setIsLoading] = useState(false);
<form onSubmit={event => {
if (isLoading) { return; }
submitData(event)
.then(() => { setIsLoading(false) })
setIsLoading(true);
}}>
{/* ... */}
</form>
const [isLoading, setIsLoading] = useState(false);
const [error, setError] = useState(null);
<form onSubmit={event => {
if (isLoading) { return; }
submitData(event)
.then(() => { setIsLoading(false) })
.catch(err => {
setIsLoading(false);
setError(err);
});
setIsLoading(true);
}}>
{/* ... */}
</form>
const [isLoading, setIsLoading] = useState(false);
const [isCanceled, setIsCanceled] = useState(false);
const [error, setError] = useState(null);
<form onSubmit={event => {
if (isLoading) { return; }
submitData(event)
.then(() => {
if (isCanceled) { return; }
setIsLoading(false)
})
.catch(err => {
if (isCanceled) { return; }
setIsLoading(false);
setError(err);
});
setIsLoading(true);
}}>
{/* ... */}
<button
type="button"
onClick={() => { setIsCanceled(true) }}
>
Cancel
</button>
{/* ... */}
</form>
+ Features
- Bugs
+/- Devs
+ Changes
+ Shiny new things
Time
Complexity
The path to
legacy code
Left to right
Top to bottom
File by file
Code is linear,
programs are not
Low-code
No thanks?
Graphics design
Games & CAD
Websites & Apps
π
User stories
πΌ
Mockups
π
User flows
π»
Code
π§ͺ
Tests
π©βπ«
Tutorials
π₯
π
User stories
πΌ
Mockups
π
User flows
π»
Code
π§ͺ
Tests
π©βπ«
Tutorials
Designers
Stakeholders
Developers
Logged out
Logged in
LOG IN
[correct credentials]
- Make API call to Supabase
- Initiate OAuth flow
- Ensure token is valid
- Persist token as cookie
- Redirect to logged in view
Given a user is logged out,
When the user logs in with correct credentials
Then the user should be logged in
All programs are
directed graphs
cart
shipping
contact
payment
confirmation
CHECKOUT
NEXT
NEXT
ORDER
PAYPAL
- Initial state
- Finite set of states
- Finite set of events
- Transitions
- Final states
State machines prevent
impossible states.
isLoading isSuccess hasErrors
type Status = Β | 'loading' Β | 'error' Β | 'success'
function transition(state, event) {
switch (state.value) {
case 'cart':
if (event.type === 'CHECKOUT') {
return { value: 'shipping' };
}
return state;
case 'shipping':
// ...
default:
return state;
}
}
State machines
with switch statements
State
Event
const machine = {
initial: 'cart',
states: {
cart: {
on: {
CHECKOUT: 'shipping'
}
},
shipping: {
on: {
NEXT: 'contact'
}
},
contact: {
// ...
},
// ...
}
}
function transition(state, event) {
const nextState = machine
.states[state]
.on?.[event.type]
?? state;
}
transition('cart', { type: 'CHECKOUT' });
// => 'shipping'
transition('cart', { type: 'UNKNOWN' });
// => 'cart'
State machines
with objects
import { createMachine } from 'xstate';
const machine = createMachine({
initial: 'cart',
states: {
cart: {
on: {
CHECKOUT: 'shipping'
}
},
shipping: {
on: {
NEXT: 'contact'
}
},
contact: {
// ...
},
// ...
}
});
import { interpret } from 'xstate';
const actor = interpret(machine).start();
actor.send({ type: 'CHECKOUT' });
// => { value: 'shipping' }
actor.send({ type: 'NEXT' });
// => { value: 'contact' }
npm install xstate
xstate.js.org/docs
Β
cart
shipping
contact
payment
confirmation
CHECKOUT
NEXT
NEXT
ORDER
PAYPAL
BACK
BACK
CANCEL
Identify
logical flaws
cart
shipping
contact
payment
confirmation
CHECKOUT
NEXT
NEXT
ORDER
PAYPAL
As a user, when I'm in the cartΒ and I click the checkoutΒ button, I should be on the shippingΒ page.
cart
shipping
contact
payment
confirmation
CHECKOUT
NEXT
NEXT
ORDER
PAYPAL
As a user, when I'm in the cartΒ and I checkout via PayPal, I should be taken directly to the paymentΒ screen.
cart
shipping
contact
payment
confirmation
CHECKOUT
NEXT
NEXT
ORDER
PAYPAL
Shortest path
confirmationΒ state
cart
shipping
contact
payment
confirmation
CHECKOUT
NEXT
NEXT
ORDER
PAYPAL
Shortest path
confirmationΒ state where
shipping address is provided
Demo
Statecharts
Extended state machines
idle
loading
LOAD / fetchData
success
RELOAD / fetchData
RESOLVE / assign
idle
loading
Β
LOAD
success
RELOAD
RESOLVE / assign
entry / fetchData
exit / logTelemetry
- β‘οΈ Transition actions
- π₯ Entry actions
- π€ Exit actions
Compound states
loading
ready
playing
paused
loaded
next
βΈ
βΆοΈ
Parallel states
music
player
volume
playing
paused
muted
unmuted
Other features
π Guarded transitions β¬οΈ Raised events β± Delayed (after) transitions βΆοΈ Eventless (always) transitions β³ History states ...
Demo
Callbacks
Event handlers
Ad-hoc logic
Ad-hoc logic
Reducer
EVENT
EVENT
EVENT
EVENT
State machine
EVENT
EVENT
EVENT
Statechart
Actors
What is an actor?
Actor π©βπ»
Actor π§
βοΈ
βοΈ
someActor.send({
type: 'greet',
value: 'Hej!',
from: self
});
// ...
switch (message.type) {
case 'greet':
message.from.send({
type: 'greet',
value: '...',
from: self
});
// ...
}
Send
Receive
Rules of actors
π€ All computation is performed within an actor
π© Actors can communicate only through messages
π₯ In response to a message, an actor can:
π¬ Send messages to other actors
π¨βπ§βπ¦ Create a finite number of child actors
π (π₯)
Behavior
State
π Change its state/behavior
Rules of actors
A
B
βοΈ
βοΈ
C
βοΈ
π€ All computation is performed within an actor
π© Actors can communicate only through messages
π₯ In response to a message, an actor can:
π¬ Send messages to other actors
π¨βπ§βπ¦ Create a finite number of child actors
π Change its state/behavior
Rules of actors
A
βοΈ
π€ All computation is performed within an actor
π© Actors can communicate only through messages
π₯ In response to a message, an actor can:
π¬ Send messages to other actors
π¨βπ§βπ¦ Create a finite number of child actors
π Change its state/behavior
A β A'
Rules of actors
A
βοΈ
π€ All computation is performed within an actor
π© Actors can communicate only through messages
π₯ In response to a message, an actor can:
π¬ Send messages to other actors
π¨βπ§βπ¦ Create a finite number of child actors
π Change its state/behavior
B
βοΈ
Rules of actors
A
βοΈ
π€ All computation is performed within an actor
π© Actors can communicate only through messages
π₯ In response to a message, an actor can:
π¬ Send messages to other actors
π¨βπ§βπ¦ Create a finite number of child actors
π Change its state/behavior
π
π
π©βπ»
π§
I would like a coffee...
What would you like?
Β ActorΒ
I would like a coffee, please.
Β ActorΒ
π
π¬
π¬
Here you go. βοΈ
Thanks!
π©βπ»
π§
π©βπ³
βοΈβ
π
πΆβ
πΆ
πβ
βοΈ
βοΈ
Actor system architecture
Sequence diagrams
Map requirements to code
1
Code independently of frameworks
2
Make interfaces simple (read, send)
3
Literally profit
4
The future of development is visual β¨
Flowcharts
State machines
Statecharts
Sequence diagrams
Thank you Γredev!
Coding Complex App Logic Visually
By David Khourshid
Coding Complex App Logic Visually
- 1,066