Understanding Data Layers (Without Databases)

Write Better JavaScript With Three Tier Architecture


By Mike Cronin

A really great concept in programming is the use of logic, data, and presentation layers. However, since the data layer is so strongly tied to databases, layers often aren't discussed until fairly late in one's programming journey. If you learn about it early on though, it will really enhance your ability to write readable and maintainable code.

Layers explained

Really quickly, the concept of layers isn't all that complicated. Basically, it's just a separation of concerns into three sections. You might also hear it called Three Tier Architecture, but I think just calling it 3 layers is less ostentatious and also kind of sounds like a cake. Here are the layers:

  • Presentation: Code that controls what users see. So HTML rendering in our case
  • Logic: This is the main "business" logic of your app. For example when a button is clicked on our presentation layer, what do we want to do? (FYI, this is also called the Application/Business/Middle layer)
  • Data: This the the actual state of our application, as well as functions used to manipulate or read from it. Typically it's a database, but as you'll see, any data technically counts.

All three are important, but for my money, the most important is separating out the data layer. To demonstrate how this architecture pattern can benefit even the simplest things, let's make a game.

Tic Tac Toe

Let's render out a very simple tic-tac-toe game. Each click alternates X or O and it'll play until there's a winner, and then it sends an alert. That's it, no tie handling, score, or restarting. As such, we only need an index.html file, we'll fit all our code into a script tag. It's just going to be a grid of 9 buttons, and a p tag that displays the current player. Each button's text start of empty, and when we click them they're going to show that X and O.


Inside that script tag, let's put the following code to start:


As I said earlier, I'm cheesing a restart on wins by just blocking the screen with an alert, but otherwise it's fine. There isn't really anything wrong with this code, but there is one part that's kind of clunky: who wins?

Checking the winning cells and piece

The problem comes when we have to check the cells and the current player. In order to check the cells, I have to literally check the document's text contents. Over and over again. We have to do this because we are using our presentation layer (our html) as our data layer as well. But, what if we could separate those two things?

Adding a real data layer

Here's the same game code, but with a distinct data layer for our player and board:


Admittedly, the piece getter and setter are a bit much, but you'll notice that it's almost the exact same code, just a little more streamlined. Our data and presentation layers are now completely separated. And it's almost exactly the same line count, so it's not like we had to bend over backwards to get this clarity boost.

Unidirectional data flow

The big switch can be boiled down to not reading from html, we only ever write to html. The only mutating state that we have, the board and player state, are saved as variables, not text fields. By changing the flow of information from a read/write to only write, we've cleaned things up.

The distinct layers allow for simpler data access and one way data direction for the presentation layer
The distinct layers allow for simpler data access and one way data direction for the presentation layer

Total separation

In the real world, like if this were a database, you should completely separate out your data logic from your app logic. Our "database" in this case would be the player and board state. That would mean that nothing can touch them except our data layer code. This would usually be a class from Sequelize or Knex or something, but here we can just throw everything into a separate file to get the point across:


I'm not going to write out the entire new module of code, since this is insane overkill for a game of Tic Tac Toe, but you can see from this that the actual state is now protected. With these exported methods it is not possible to modify state incorrectly. For instance, you can't accidentally miss-set the player piece and setBoardCell will always add the current player to the given cell because it references the current player internally.

This also protects us from changes of logic. If any internal logic for checkWin or renderBoard changed, the presentation and logic layers don't need to know. In fact, if we did it like this, we could alter these methods to use an actual API and database call instead of in-memory variables, and the rest of the app would never even know! This separation is especially important in JavaScript where it's not unheard of to swap out your underlying DB stack. That's a hell of a lot easier if no other layer of your app ever talks to your database.

It may be simple now...

Tic Tac Toe is a really silly example to use, but it allows us to demonstrate the layer concept pretty clearly. What this all boils down to is that separation of concerns is a great guiding principle for your code, and you should always try to incorporate it into your projects.

Happy coding everyone,

Mike