Next lesson playing in 5 seconds

  • Overview
  • Transcript

3.4 OTP Applications

OTP is a big collection of components and standards. One of those standards specifies how to create applications. In this lesson you're going to learn the basic architecture of an OTP application.

3.4 OTP Applications

Hi and welcome back to get started with Elixir. In this lesson, we will learn about OTP applications. According to OTP's documentation, an OTP app has to fulfill three criteria. First, an OTP app is a component that implements specific functionality. Then, it must be able to be started and stopped as a unit. And finally, it is reusable in other systems. So, how do these apps look like? In fact, you have already seen an OTP app because every mix project is one. The mix.exs file is what defines it. All the configuration in there is used to define the OTP application. So, how does it fit into the current style of designing and creating application? Very well, as it turns out. Since it encourages you to create isolated components and as, we'll learn in the later lesson, allows you to run them on distributed hardware, you have essentially created a microservice architecture without the pain of solving the communication problem that normally occurs. It's all built into the framework already. Let's do an example. I'm building an OTP app off of our wallet project from earlier lesson. We already have most of the components created, we're just reorganizing them to create a robust architecture around it. First, let's create a new mix project called wallet. As I want a supervisor created by mix, I'm adding the sup option. First, let's have a look at what that created for us. In the wallet namespace, we now have a module called Application. It looks quite similar to the Supervisor we created in the last lesson. In our new revised architecture, I want to have the supervisor monitor two things. First, the Cache and secondly, a wallet Server that manages wallets. You can already see the architecture and patterns that OTP apps embrace. Encapsulate components and specialize as much as possible. Let's start with the WalletServer. First, we need a start_link function that will start the Supervisor. I'm going to pass the module as a callback module. No arguments and the module as the name. Next is the init function, here we need to define the children. I'm going to use a Wallet Worker with no parameters that will act as a template. I will also explain the transient restart strategy in a minute. The reason why I called it a template, is the supervision strategy I'm using. It is called simple_one_for_one. It is perfect for a dynamic number of workers and uses a template for that, the worker we just defined. When starting the supervisor, no workers will actually be spawned, we are going to spawn them dynamically. The transient strategy means that if a process terminates abnormally, meaning it crashes, it will be restarted. If it exits normally, it won't. Okay, it's time to add some public API that allows us to interact with it. I'm creating a function called create_wallet, that takes a name as parameter. In the function body, we're gong to call star_chid and pause the name. This will create a new worker process with the wallet name. I'm also going to add two more functions. One will be find_wallet and the other will be delete_wallet. Find_wallet will use Enum.find on the list of wallets and iterate through them with the function callback. Here we're comparing that the name of the wallet matches the name we supplied. In delete_wallet, we can use this functionality to find a wallet by name and if we found one, we will terminate their child. Finally, we need the wallets function that will return all the wallets the Supervisor manages. I'm going to call which_children on the Supervisor, pass in the modules name and then map everything to get a list of child process ids. Okay, since we already have created a cache and divided itself, I'm going to paste the code of them and quickly go over it. Let's start with the cache server API. It has a start_link method that will start a name process. The init method creates a new table named like the cache that is named and public. Then it returns this as its state. We also have three public API functions. The first one is store which stores the state of the wallet in the table and transforming the wallet name to an item and start a wallet state. The second function is find, which finds a wallet by its name and returns the stored value. Finally, I'm adding a clear function to remove all objects from the table. If you are testing, this can be quite useful. Finally, let's have a look at the wallet module. I changed it a bit to store the name and the amount as the state in form of a map. During initialization, it will try to find the start state from the cache or initialize a new one. This time, we're storing the amount in the state as well. And not look it up every time. This saves us a call. I also renamed a methods to deposit and withdraw. And added look ups for name and amount. Let's try it out. I can start the application by starting iex with a mix script. And that's it, the application has already started. We can look at the supervision tree with the Erlang module Observer that we can start from the console. In the application step, we have our wallet application. It has the wallet supervisor, as well as the cache and the server. Now let's add a wallet to the server by calling Wallet.Server.create_wallet. And I'm calling it Personal. In the Observer we can now see a child process of the wallet server that represents the wallet. Double clicking it gives us more detail, for instance, we can see the state. Since this is an Erlang tool, it uses the Erlang syntax to display the data. Now let's create another wallet called Business, and put some money in it. In the Observer, there are now two wallet processes. But if we decide to kill the process that holds the business wallet, It immediately starts a new one, and upon inspecting the state, we can see that it does also restart. To recap, OTP applications are components with special functionality that are reusable and can be started and stopped as a unit. They are essentially microservices without the communication problem. Every mix project is an OTP application. In the next lesson, I'm going to teach you about Tasks and Agents. See you there.

Back to the top