better explanations through coupling
Previously, we explored how coupling and cohesion are not separable concepts.
When our software is cohesive, everything fits. Each part is shaped by its relationships. Together, they comprise an undirected graph, which we will call a structure. Each structure is an amplifier; by explaining one vertex, we begin to explain the others.
Often, these vertices have a natural order. There is one vertex that, when explained first, simplifies all the others. We will call this the locus of a structure.
These two concepts — structures and loci — will become our primary tools for understanding software design. To begin, let's look at three examples.
model/view/controller
An MVC web application has three major components:
Sometimes these components are drawn as part of a narrative. The request goes into the controller, the response comes out of the view, and somewhere in between we interact with our database via the model.
This narrative, however, is not the structure of MVC. Instead, the structure describes the relationship between the model and the contents of the view and controller.
This is reflected in the code generation offered by the Rails web framework. When adding a new database entity, developers are expected to use a command line tool:
rails generate scaffold Widget name:string cost:decimal
This creates a database table which can store our widgets. It also, however, generates a dozen ancillary files allowing the application to create, edit, and display widgets.
This code, once generated, can be changed. The relationship between the model and the rest of the application is predictable rather than fixed. Even so, by understanding this vertex, we gain a broad understanding of the rest of the structure. The model is a locus.
MVC frameworks are, today, a bit out of fashion. The point is not to extoll their virtues, but to analyze their success; for over a decade, they shaped how people thought about an entire category of software. As designers, we should aspire to do the same.
entities and hierarchy
The MVC structure describes an entire web application. Vertices within a structure, however, often contain their own sub-structures. For instance, our model might contain these entities:
The entities, and their relationships, are easy to grasp. A student
attends course
s, each of which is taught by a teacher
. Each course
is in a classroom
, which is cleaned by a janitor
.
This structure, however, provides no hints as to what each entity should contain. There are countless things we could know about a classroom
: location, size, seating capacity, layout, and so on. We could track every work order ever assigned to the room, or every piece of gum ever removed from the bottom of a desk.
Like all relational schemas, this graph is undirected. There are no roots; we can start anywhere, and traverse everywhere else. In-memory data, however, does have roots; usually, we start with the user.
Here, we have chosen a locus. All paths begin with student
; each entity is seen through a student
's eyes. A classroom
is where a course
is attended; what matters most is its location. Some parts of the schema are nebulous, hidden from view; what should a student
know about a janitor
?
By changing our locus, we change our perspective:
Now, a classroom
is where a course
is taught. We care about the location, but other aspects as well. What's the seating capacity? Are there chalkboards, whiteboards? Is there a projector? Are lectures easily recorded?
In our early prototypes, most of these facets will be unrepresented. They are, nevertheless, part of our design. That design describes what our software is, but also what we expect it to become.
what kind of queue?
A queue is a line of people, all waiting for the same thing. When we describe part of our software as a queue, we are using a metaphor.
To make this metaphor work — to perceive our software as a line of people — we must answer a number of questions. How often do the people arrive? How long are they willing to wait? Where are they waiting? How many people can that space hold?
The queue is a useful metaphor because it makes us ask useful questions. This remains true even when we have a more abstract association — a conveyor belt, or simply a bounded space between two processes.1 And these questions, asked in context, often have intuitive answers.
Consider a system which contains client process A and service B. Someone suggests adding a queue between them. What do they mean?
Absent any context, they could mean any number of things. They could mean adding an in-memory queue to A, or a disk-backed queue that can survive process restarts. They could mean adding in-memory queues for each process in B, to buffer requests. Or, perhaps, they could mean adding an entirely new service to act as a queue between A and B.
If it's a new service, that only raises further questions. Do messages persist on the queue after being passed to B? If so, how long? Is it a task queue, requiring processes in B to confirm completion? If so, how long do we wait before a task is considered abandoned? Should we have a heartbeat, to support long-lived tasks? The decisions, large and small, are nearly endless.
But when using the queue-metaphor, there is a context: the system. And, typically, everyone involved in a design discussion understands that system. They know its purpose, its structure, and its shortcomings. These are all part of the metaphor's prefix.
And so, when someone suggests a queue, the conversation doesn't devolve into endless questions. In context, their meaning is clear.
The queue-metaphor is, again, a locus. It gives us a broad understanding of the underlying implementation. But that understanding is a product of both the metaphor and the system that surrounds it. A piece can only be understood through its relation to the whole.
One association which doesn't raise useful questions is a generic
Queue[T]
data structure. Real-world queues are associated with waiting, and raise questions about latency and capacity. The data structure, on the other hand, has few associations beyond implementing breadth-first searches. ↩
This post is an excerpt from my (incomplete) book on software design. For more about the book, see the overview.
"MVC frameworks are, today, a bit out of fashion. The point is not to extoll their virtues, but to analyze their success; for over a decade, they shaped how people thought about an entire category of software. As designers, we should aspire to do the same."
I'm fuzzy on what "do the same" is really supposed to mean here. At the high end, are you encouraging me to build a framework that becomes globally well known for decades? At the low end, are you encouraging me to influence how my coworkers think when they read my code?