← Contents · Runnable Specifications by Eric Normand · Work in progress · Comments
103
103
Chapter 4
Composition Lens Part 1
The operations play a vital role in the domain model, and until
now, weve considered them in isolation. But we rarely do one
operation by itself. Instead, we perform sequences of operations
all operating on the same data. These operations relate to each
other. We can extract quite a lot of information from those rela-
tionships to make better design decisions.
Chapter objectives
Learn the two major approaches to identifying important do-
main concepts.
Discover how the closure property can expand the expres-
sivity of your model.
Understand that most operations are called in sequence with
other operations.
State
UI events
mutations
current state
HTML
present
translate
Super Mega Galactic
by the end of this
chapter, you’ll understand
these three diagrams
Important terms
composition
organic and synthetic
approaches
closure property
algebraic properties
total property
partial property
you’ll learn these
terms in this chapter
104 Chapter 4
The most interesting interactions involve multiple operations
Do you remember the touchscreen interface the barista used to take an order? Let’s look at a typical
interaction the barista will have with a customer.
Here’s the whole sequence written out:
let coffee = newCoffee();
coffee = setSize(coffee, “galactic”);
coffee = setSize(coffee, “mega”);
return value from one is used
as an argument for the next
coffee = setRoast(coffee, “burnt”);
coffee = addAddIn(coffee, “soy”);
coffee = addAddIn(coffee, “almond”);
coffee = removeAddIn(coffee, “almond”);
coffee = removeAddIn(coffee, “almond”);
Operations are rarely used alone. Instead, we use them in sequence and in parallel. A composition
is two or more operations performed where the return value of one operations is used as the argu-
ment of another.
One coee please!
Sure thing! What size?
Galactic.
No, just mega.
Great. Is charcoal okay?
Burnt, please.
Anything in that coee?
Can I add soy? And almond?
Yes!
Sorry, no almond.
You got it!
let coffee = newCoffee();
coffee = setSize(coffee, "galactic");
coffee = setSize(coffee, "mega");
coffee = setRoast(coffee, "burnt");
coffee = addAddIn(coffee, "soy");
coffee = addAddIn(coffee, "almond");
presses “new coffee”
sets size
sets roast
adds soy and almond
removes almond
accidentally touches “remove almond” a second time
sets size again
coffee = removeAddIn(coffee, "almond");
coffee = removeAddIn(coffee, "almond");
Customer Barista Touchscreen
105Composition Lens Part 1
Introducing the Order GUI
We just got the UI mockups from our designers for the touch-
screen interface to enter an entire order. Our job is to build it.
This UI has dierent sections. We can begin to break down the
GUI into those sections to understand its structure. We’re going
to build this GUI in HTML. We could, of course, build this GUI
directly in HTML. But we wont. Instead, were going to use this
example as a learning journey to understand how to nd deeper
structure, model it, and encode it in our language.
Were looking for an intermediate layer between HTML and the
order GUI we are asked to build. This layer is expressed in HTML,
with structure that is more specic to our needs. Then we will
build this specic GUI out of it.
This may be over-engineering if you were solving the same
problem in the real world. But in this book, it will be a useful
learning journey, so lets go!
Super
3 coees
mega, charcoal,
2 soymilk
mega, charcoal,
2 soymilk
mega, charcoal,
2 soymilk
Total: $12.00
$5.00
$5.00
$5.00
Raw
Soymilk
Espresso
Almond
Mega
Burnt
Galactic
Charcoal
+
+
+
+
+
2
1
0 0
0
-
-
-
-
-
Chocolate
Hazelnut
HTML
Order GUI
GUIs in General
looking for
intermediate
structures
106 Chapter 4
What is a GUI?
What is a GUI? A question like this focuses our mind and forces us
to dene the essence of a thing.
There are many answers to the question. And deciding about
right and wrong, truth and falsehood, is the work of philosophers.
Were not so interested in that. We are interested in a number of
things:
1. Leverage
2. Clarity
3. Precision
4. Generality
Precision means that our denition can be concisely stated as
we mean it. Are we able to state the denition in our formal lan-
guage?
Clarity means that our denition is unambiguous. It gives us a
path forward without kicking the can down the road.
Leverage means it is worth the eort. We want to get more
out of it than we put into it. We could hack a GUI together with
spaghetti code and brute force. We want a denition that makes
building the GUI easier. The leverage we seek comes from struc-
tural similarity between the domain and our code.
Finally, the denition must be at the right level of generality.
We dont seek to maximize generality. Instead, we want some-
thing that targets the layer we’ve set up. It needs to be built out of
HTML and able to build our order GUI and similar GUIs in. Being
useful for ALL GUIs is too general. But being useful only for this
particular design is too specic.
Keeping those things in mind, try to come up with a denition.
It’s a useful exercise. You may not get it right the rst try, but its
worth trying. It will jog your thinking.
Write down your denition before you turn the page. On the
next page, youll see my denition.
107Composition Lens Part 1
A model of GUIs
Here is my denition of GUI:
A GUI presents the applications state and available operations
to the user graphically; and it translates the user’s interactions
into operations that modify the state.
Here is a picture:
As the state changes, the presenter generates an HTML repre-
sentation, which the GUI delivers to the browser. The user will
tap the touchscreen (the only kind of user interaction we need),
which generate events. The translator will interpret those events
into mutations, which will be applied to the current state. Then it
goes through the cycle again.
There are a number of concepts we need to dene, which will
form the basis of the architecture of the application.
I will proceed through the denitions of those concepts. Just
remember that there is not one right architecture for web UIs.
This oneis merely practical for teaching a number of concepts.
Here are the concepts and their types and signatures:
type HTML = string;
type UIEvent = string;
function translate<T>(uiEvent) //=> mutation<T>
function mutation<T>(T) //=> T
function present<T>(T) //=> HTML
The GUI architecture will handle everything if we pass it
present() and translate() functions.
State
UI events
mutations
current state
HTML
present
translate
108 Chapter 4
The GUI architecture
Here is the code for the GUI architecture:
let state = {};
let present = (state) => “Hello!”;
let translate = (event) => (state) => state;
const container = document.getElementById(“gui-container”);
container.addEventListener(“click”, (e) => {
constevent=ndEvent(e.currentTarget,e.target);
const mutator = translate(event);
state = mutator(state);
container.innerHTML = present(state);
});
function ndEvent(top, bottom) {
letevent=undened;
for (let e = bottom; e !== top; e = e.parentElement)
if (e.dataset.event)
event = e.dataset.event;
return event;
}
container.innerHTML = present(state);
This is a barebones GUI framework, but it will do the job. The
rst three lines dene the behavior of our specic GUI, the rest of
the lines are the machinery to make click events work.
Because it is an HTML GUI, we need to dene the HTML:
<html>
<body>
<div id=”gui-container”></div>
<script src=”/gui.js”>
</body>
</html>
As it is, this GUI will simply display the text Hello!”. We’ll mod-
ify the three variables at the top (state, present, translate)
iteratively to dene the MegaBuzz order GUI. I suggest you create
these two les and view the HTML le with a browser to test it.
109Composition Lens Part 1
Q&A
Why are we dening our own GUI framework? Can’t we just
use React? Or Vue?
It’s a good question. I’m dening the GUI framework because I
don’t want this book to be about React or Vue. I don’t even want it
to be about HTML, but I had to pick something.
The main idea behind runnable specications is that we can
run them. We needed some kind of system that would actual-
ly run. If youre familiar with React or Vue or any other way of
building GUIs, please feel free to use one of those.
However, be careful. When using a practical framework, it is
easy to slip into implementation mode, which we are trying to
avoid. This simple framework exactly models our GUI model. It
has exactly three degrees of freedom (initial state, presenter, and
translator). And each of them has very well-dened inputs and
outputs. These constraints help us stay in specication mode.
110 Chapter 4
A counting GUI
Before we get complicated, let’s understand how this GUI system
works by creating a simple counter. There will be a number, and
every time you click it, it increments by 1.
The rst thing is to dene the initial state.
let state = {count: 0};
Then we implement the presenter. We’ll return a div containing
the number. The div will have a data-event attribute that will
be picked up by ndEvent().
let present = function (state) { //=> HTML
return `<div data-event=”increment”>${state.count}</div>`;
};
Finally, we implement the translator, which takes an event and
returns a mutation function to be applied to the state. Well de-
ne it in a functional way, using a modied copy.
let translate = function (event) { //=> function (state) => state
if(event === “increment”)
return function (state) {
return Object.assign({}, state, {count: state.count + 1});
};
return state => state;
};
Give the GUI a try. Click the number and it should increment each
time.
0 1 2 . . .
click click click
111Composition Lens Part 1
Displaying the state
As Im developing the GUI, there are times when I want to know
the state as raw as possible. There could be a bug in the presenter,
and I want to see the raw state and the HTML from the presenter.
A simple way to display the state is to output it as JSON at the bot-
tom of the GUI.
let present = function (state) { //=> HTML
return `<div data-event="increment">${state.count}</div>` +
stateViewer(state);
};
function stateViewer(state) { //=> HTML
return`<pre><code>${JSON.stringify(state,undened,2)}</code></pre>`;
}
I also want to see the last event I triggered to make sure things
are working as I expect. I’ll add that to the translate function.
let translate = function (event) { //=> function (state) => state
if(event === "increment")
return function (state) {
return Object.assign({}, state, {count: state.count + 1,
lastEvent: event});
};
return state => Object.assign({}, state, {lastEvent: event});
};
The GUI should look like this:
I hope this simple example shows that we have indeed encoded
our abstraction of the GUI and that it gives us the exibility we
need to build our screen. If thats still not obvious, it will become
so as we continue.
0 1 2 . . .
click
click
click
{
count: 0
}
{
count: 1,
lastEvent: "increment"
}
{
count:2,
lastEvent: "increment"
}
112 Chapter 4
Building the basic elements
We’ve got our basic GUI framework in place. We have a very sim-
ple GUI that displays the state and accepts user input to change
the state. But our GUI looks nothing like what we need it to.
We could build our Order GUI directly out of what we already
have: The GUI Framework and HTML. The GUI Framework de-
nes the types of the presenter and the translator. HTML denes
how it will display in the browser.
However, the number of structural concepts in our GUI is
quite limited and repeated. You might already be able to see that.
But the structural concepts of HTML are abundant. We will want
to take advantage of that and add a GUI Elements layer between
the Order GUI and the GUI Framework.
Those GUI elements dene more specic concepts that are less
applicable to all apps and more applicable to our app. The reduc-
tion in generality is one of the reasons it can give us leverage. But
only if we nd concepts that help us build the GUI we want.
HTML
HTML
GUI Framework
GUI Framework
GUI Elements
Order GUI
Order GUI
113Composition Lens Part 1
Organic and Synthetic approaches
There are two main approaches to encoding a domain. We will re-
fer to them as “organic” and “synthetic. In the organic approach,
you begin by coding the Order GUI with what you already have.
As you recognize regularities in the code, you begin to factor
those out into reusable parts. Those parts form a lower layer.
The synthetic approach is the complementary process. You
rst identify regularities in the domain, then encode those as
concepts in your model in the layer. Then you build the Order GUI
out of those. Both approaches have merit. Both have negatives.
The organic approach is good for exploring a domain where
the structure is not apparent to you. By diving in and coding, you
are able to make forward progress. And you oen see the struc-
ture later as you are coding. We commonly discover structure
due to repeating patterns. But we can also nd it by encountering
diculties. Things that are harder than they should be probably
mean you’ve got the structure wrong.
The synthetic approach doesnt start with the code. It starts
with an analysis of the domain. You look for repeated patterns
and important structures. You develop an understanding of those,
then you encode it in your programming language. The synthetic
approach is good when the domain is well-understood (such as
accounting) and you have a way to explore it, such as access to an
expert (an accountant).
The synthetic approach is oen misunderstood. The carica-
ture of it is that a programmer, with little understanding of a do-
main, on their own begins dreaming up abstractions, then cod-
ing those. But this is not how it works. It takes disciplined study
and skilled analysis before coding happens.
The truth is that we code in both modes alternatively. Both do-
main analysis and exploratory coding are great sources of infor-
mation. This is why we model in code and seek rich feedback. We
might analyze the domain, encode what we discover, then nd
that there was something we missed, which we explore organi-
cally.
114 Chapter 4
Discovering the structure of our GUI
Lets take a close look at our GUI and uncover the structures with-
in. I’ll mark up the image and discuss each structural pattern.
We see several repeated elements and patterns:
text
image
border
frame
vertical layout
horizontal layout
With these six components, we can build this GUI. Lets get to
work on the next page.
Super
3 coees
mega, charcoal,
2 soymilk
mega, charcoal,
2 soymilk
mega, charcoal,
2 soymilk
Total: $12.00
$5.00
$5.00
$5.00
Raw
Soymilk
Espresso
Almond
Mega
Burnt
Galactic
Charcoal
+
+
+
+
+
2
1
0 0
0
-
-
-
-
-
Chocolate
Hazelnut
vertical layout
horizontal layout
border
image
text
frame
115Composition Lens Part 1
What is a component?
The rst thing we need to decide is, What is a component? We
are building at a certain layer in the stack. We are building on top
of the GUI framework, which only gives us three things to dene:
the structure of the state — T
the presenter — function present<T>(T) //=> HTML
the translator — function translate<T>(uiEvent) //=> mutation<T>
We will these components within the GUI Elements layer:
text
image
border
frame
vertical layout
horizontal layout
Then we can build our GUI using those in the Order GUI layer.
But that brings us to a tough quesion: What is a component? Lets
try this denition on for size: A component takes some part of the
state, renders it to HTML, and can be composed with other com-
ponents into a presenter. It will have this type signature:
function component<T>(T) //=> HTML
Notice that that is the same type signature as presenter. This is
actually a very good thing because it means that renering a par-
tial state is no dierent from rendering the full state. We can vi-
sualize each component separately using the same framework.
And if we get the composition right, those will also build up into
presenters, so we can see partial GUIs using the same framework.
HTML
GUI Framework
GUI Elements
Order GUI
these pieces are
given to us by the GUI
Framework
116 Chapter 4
Complex operations rst
We know what components we need, but where do we start? We
could start with the simple ones, like text, because then we can
knock them out before we go to the harder ones. But I have found
through experiences that it is always better to start with the com-
plex ones.
When we dene the complex operations, we immediately face
the most dicult problems. Solving those problems gives us a lot
of useful information about how the pieces work together. And
that information usually results in us having to rework some-
thing. Maybe the function needs a new argument, or perhaps the
return type needs to be augmented.
In any case, any code we’ve written will have to be revisited at
the least and rewritten at the worst. If weve written the simple
ones rst, all of that work could be scrapped.
Another way to look at it is that the complex operations have
more interesting properties. By dening them and exploring the
properties, the complex operations reveal design constraints that
help us make design decisions in other parts of the code. We have
more information. And so then we can make better decisions
elsewhere.
Contrast this with what I was taught to do, which was to cre-
ate a class, add some instance variables, then immediately tackle
the easiest methods: the getters and setters. By the time I got to
the more complex operations, I had a much better idea of what
instance variables I should even have. I would either go back and
replace the wrong instance variables or leave them, creating a
mess. Complex operations are harder, but they