What Lies Ahead?
Explore the roadmap for building a stateful web application using Elixir and Phoenix. Learn to separate business logic from the web framework, manage state with GenServer and OTP, and create persistent connections with Phoenix Channels. Understand how to compose decoupled application layers to enhance maintainability and scalability, culminating in a functional, fault-tolerant game engine with a web interface.
Lay the foundation with Elixir
Let’s have a look at what we will learn in each part of the course.
In the first part, we begin by defining the data structures and logic of the game in pure Elixir. We won’t use a database to store the game state and will define our domain elements with native Elixir data structures instead of
We will bring in a finite state machine to manage state transitions, like switching from one player’s turn to the other and moving from a game in progress to one player winning.
Building logic first
Building the game engine solely in Elixir solves a long-standing problem in web development—the tendency for the framework code to completely entangle application logic so the two can’t be easily separated. Without that separation, it’s hard to reuse application logic in other contexts. As we build Islands, we won’t even begin to work with the Phoenix framework until our game logic is complete.
In the second part, we layer on OTP for concurrency and fault tolerance. We hold the data structures we’ve defined in the GenServer as a state. Then we build a supervisor to monitor the GenServer and restart it with a known, good state in the event of a crash.
By the time we’re done with the first and second part, we’ll have a fast, fault-tolerant game engine that spins up a new GenServer for a game almost instantly. We’ll be able to reuse it with any interface we want—the web, a native mobile app, plain text, or whatever else we can think of. If we look at it the right way, the GenServer for each game is really a microservice, or a nano service living right inside the virtual machine.
Adding a web interface with Phoenix
Let’s have a look at how we will add Phoenix with a web interface.
In the third part, we generate a new Phoenix application without Ecto. Ecto is the database layer that ships with Phoenix. We’ll use our Islands’ engine as a dependency and make it a part of our new Phoenix application’s supervision tree. We’ll also see how to wire it up with the standard Phoenix
Including Phoenix Channels
After this, we move on to the really exciting part. We replace HTTP’s temporary client-server connections with persistent ones via Phoenix Channels. Channels provide a conduit for lightning-fast message passing between front-end applications, and in our case, a stateful back-end server. We’ll make good use of Channel-naming conventions to allow two players to connect to their own private GenServer running Islands. We’ll also be able to run thousands of games simultaneously on a single server. Many languages struggle to keep persistent connections open for all the players of all current games. However, Elixir’s incredible concurrency model makes it easy.
As we finish up, we’ll have a web interface to our Islands engine. The main component will be a Phoenix Channel able to connect two players directly to an individual Islands game. We’ll customize the JavaScript files that Phoenix provides to get it primed and ready for our favorite front-end framework. When we’re done, it’ll have much less code and far fewer moving parts than a conventional web application.
Functional web development
With all this in mind, one may be wondering about the title of the course and how this represents functional web development. One of the most characteristic patterns of functional programming is composition. With function composition, we take a big, complex piece of work and split it up into smaller, decoupled, and more focused functions. Then we re-create the full behavior by chaining these functions together.
This does not only helps us reason about our programs because smaller functions reduce cognitive load. It also helps with maintainability because smaller functions are easier to work on.
In this course, we’ll take the idea of composition from the level of functions and scale it to the level of applications. We’ll take the full, complex behavior of a web application and separate it into independent, decoupled layers. Each layer will have a focused responsibility. It will do its job and nothing else.
Then, we’ll re-create the full behavior of the application. We will have each layer call into the next and pass the return values back up the chain, out to the client. By doing this, we’ll gain clarity and maintainability for our whole application.
Now we’re ready to introduce the game itself.
The game of Islands
Let’s talk a little bit about Islands. It’s a game for two players. Each player has a board that consists of a grid of a hundred coordinates. The grid is labeled with the numbers 1 through 10 across the top for the columns and down the left side for the rows. We name individual coordinates with this row-column combination. The players cannot see each other’s boards.
The players have matching sets of islands varying in shapes and sizes. They place these islands on their own boards. The players can move the islands around as much as they like until they say that they are set. After that, the islands must stay where they are for the rest of the game.
Once both players have set their islands, they take turns guessing coordinates on their opponent’s board, trying to find the islands. For every correct guess, we plant a palm tree on the island at that coordinate. When all the coordinates for an island have palm trees, the island is forested.
The first player to forest all their opponent’s islands is the winner.
Before we get to work, let’s make sure we have all our dependencies installed. For the first part of the course, we’ll only need Elixir and Erlang. For the second part, we’ll need to install the Phoenix archive, Node.js, and npm. We’ve got a plan! Time to start building.
Note: You do not need to install these dependencies on our platform. You only need to install them for local development from your machine.