More about Stratified Design
This is an episode of Thoughts on Functional Programming, a podcast by Eric Normand.
Part 1 in the Functional architecture series. The Stratified Design, which I called "layered design" before, is a way of architecting your code as a series of layers of meaning. It's a common way of organizing your code and structuring your application.
Eric Normand: I want to talk a little bit more about stratified design. Hi, my name is Eric Normand, and these are my thoughts on functional programming. A few episodes ago, I explained something that I called layered design. I did a little bit more research about it.
It turns out it's actually got a different name. It's called stratified design. There's a whole chapter on it in "Structure and Interpretation of Computer Programs." Strata and layers, they mean the same thing. I just used the wrong term. I do want to talk more about it in more detail, and to really talk about it as an architectural pattern.
I talked about it in terms of organizing your code. As an architectural pattern, it's useful from that perspective to talk about it. Just to recap really quickly, in stratified design, you start with your base language at the bottom. The stuff your language gives you. Maybe the standard library, the data structures, you've got functions, for loops, whatever you've got.
On top of that you build a layer of meaning. I don't want to give an example yet, but I will. After that, you build another layer of meaning on top of that layer. You continue building layers of meaning until you're at the layer of meaning where the solution to your problem is very clear and concise, and you can experiment with it at the right level of meaning and abstraction.
That's stratified design. One thing that'll you see in stratified design often is that, in a business context, you're defining, say, at the bottom level, a very data model driven system. On top of that, you're adding some business rules.
There's probably more layers in between, where you have primitives that you then add on into combinations of those primitives until you have at the top of your strata, your layers. The top layer is dealing in business rules because that's the stuff that changes the most. This strata idea also talks about where things change, the speed at which things change.
Notice the bottom layer is the language. That's the thing that changes the slowest. Even if a language is getting released all the time, it's not changing that much over time. Whereas your business rules, depending on the market conditions, depending on your business strategy, that could be changing all the time.
Then the stuff underneath the business rules is a little bit more stable because it needs to be more stable to build the business rules on top of. As you go down, things get more stable. This lets you gut check.
You might even be able to make metrics about this if you could figure out an automatically generated diagram from your code based on dependencies, how things change, and what layer it should be at. Like, "Ooh, this is weird. This thing is at a high layer, but changes slower than this other thing that it depends on, so maybe there's something wrong there." It would just highlight these code smells.
Structurally, another thing that happens is that your dependencies all point down. Otherwise, it's not really a layer. They don't point sideways. That's another aspect of it. You could have two, say, modules at the same level of meaning, the same strata, but since they're at the same strata, they have no dependencies between them. Something on top might depend on both of them.
There's a directionality to the dependencies. There's also a potential code smell in there, which is if you skip layers, meaning you start writing your business rules with the lowest level of language constructs when you really should have multiple layers in there.
This comes in even when you haven't started with stratified design. It's a code smell. Why am I using a for loop to determine the direction my business is going to take? You should be doing something higher-level. You shouldn't be in there writing your business on assembly. You're skipping too many levels.
Let me give an example that's not code-related of this kind of stratified design. I like to cook. I'm not that good at it, but I like to cook. I like to cook from different cuisines. I like New Orleans cuisine, where I'm from. I also like Vietnamese cuisine, Chinese cuisine, Indian, French, Italian. I like all sorts of stuff.
I also like looking at what's happening with a lot of modern cooking, which is breaking stuff down even further into chemicals. You start learning some chemistry. It turns out, if you start to map this out, you've got this base layer at the bottom. Let's just call it chemistry.
You're starting with proteins, lipids, heat, acids, and bases. All these chemistry items are at the bottom. These things change the least. Nothing's going to change chemistry. On top of that, you add your basic techniques, let's say.
This would be how to apply heat to something. We're not talking about the something yet, but how to apply heat. The properties of your tool, your pan can transfer heat at a certain rate. You're looking at this layer that's built on chemistry. Heat transfer is a chemistry thing, but now you're talking about a pan, which is decidedly not chemistry itself. It's a layer on top of chemistry.
On top of that you might have a layer that talks about the preparation of ingredients. I'm not saying the ingredients themselves. That's probably either a layer below it or in the same layer but an adjacent module.
This is stuff like chopping techniques, milling, boiling. Those kinds of things that are built out of these very basic techniques, like applying heat or cutting. We're talking about chopping in one layer and then the next layer we're talking about julienne.
It's not just how to cut a very basic thing. It's this other idea of the kind of shape that you're trying to achieve out of your carrot. Then, on top of that, you're going to have certain basic recipes, stuff like how to make a very basic sauce. On top of that you're going to have your dishes.
I like to think of gumbo, because I cook gumbo, or jambalaya, or something like that. These are the dishes that are made out of these components. With a gumbo, you always start with a roux. A roux is a basic technique, because other dishes have roux in them as well. That's really high-level.
You could go higher. You could start talking about how to prepare a whole meal. It depends on knowing how to prepare dishes. Your meal is like, "Well, I need an appetizer, and then the main course, and then a dessert." You need dishes that fall into these categories. OK, I think you get the idea.
This is what I do when I'm programming, this is what functional programmers do, is they start to identify these layers. There's this code smell that if you're defining your jambalaya recipe in terms of chemistry, you've skipped layers. You can't build up grains of rice from proteins, starches, and water. You're not going to do that.
You're not thinking about the chemistry in that way when you're talking about jambalaya. You are thinking about it only one...You know the chemistry is there, but you're usually only going one or two layers down. What this lets us do is build these things.
One, they're understandable. Two, they're capturing this notion of frequency of change, which is huge when you're talking about architecture. Architecture is isolating changes that don't need to be done together.
Here's a really good example. You go to a different part of the world, you have a different cuisine. Chemistry is not going to change. Maybe the ingredients, you add some. A carrot is still a carrot. There's new ingredients that are going to be added to the same...but you're not changing how carrots works.
You're not changing how cutting works. You're not changing how your pan works. All those things are staying the same. At the top level, you are going to be changing stuff. Just a lot more churn in how things are defined. Then, of course, you're probably going to throw out all the recipes and start with totally new recipes.
That's how your codes should be structured as well. You should be making these layers, these strata of meaning, and identifying them so that you're grouping them together in whatever is the appropriate way to group stuff in your language. That might be a module, or a library, or a namespace. It just depends on what you're doing.
You also might get some help from your language because, in some languages, you can have circular dependencies. You're going to enforce a certain direction to your dependencies, which is perfect.
This is part one of what I think will be a three-part series on functional architecture, so stay tuned. I'd love to hear examples of where you've used this stratified design. Like I said, I'm taking the name from Structure and Interpretation of Computer Programs, so it has some really nice pedigree. That's my opinion. I'd love to hear from you.
I'm on LinkedIn. Just search for Eric Normand, Clojure, and you'll find me. On Twitter, I'm @ericnormand. Follow me. Tweet at me. I love to get into discussions. You can take the discussions to email. I'm email@example.com. Be sure to subscribe and share this episode with your friends. All right, thank you so much. Bye.