Making Our Server into a Component
Understand how to split the server code into major components.
We'll cover the following
Earlier, we said that what Elixir calls an application, most people would call a component or a service. That’s certainly what our sequence server is: a freestanding chunk of code that generates successive numbers.
Implementation
Our implementation puts three things into a single source file:
- The API.
- The logic of our service (adding one).
- The implementation of that logic in a server.
Have another look at the code in the previous lesson. If we didn’t know what it did, how would we find out? Where’s the code that does the component’s logic? Imagine working with a really complex one with lots of logic. That’s why we’re experimenting with splitting the API, implementation, and server into three separate files.
We’ll put the API in the top-level lib/sequence.ex
module, and we’ll put the implementation and server in the two lower-level modules.
The API is the public face of our component. It’s simply the top half of the previous server module:
defmodule Sequence do
@server Sequence.Server
def start_link(current_number) do
GenServer.start_link(@server, current_number, name: @server)
end
def next_number do
GenServer.call(@server, :next_number)
end
def increment_number(delta) do
GenServer.cast(@server, {:increment_number, delta})
end
end
This forwards calls on to the server implementation:
defmodule Sequence.Server do
use GenServer
alias Sequence.Impl
def init(initial_number) do
{ :ok, initial_number }
end
def handle_call(:next_number, _from, current_number) do
{ :reply, current_number, Impl.next(current_number) }
end
def handle_cast({:increment_number, delta}, current_number) do
{ :noreply, Impl.increment(current_number, delta) }
end
def format_status(_reason, [ _pdict, state ]) do
[data: [{'State', "My current state is '#{inspect state}', and I'm happy"}]]
end
end
Unlike the previous server, this code contains no business logic (which in our case is adding either 1
or some delta to our state). Instead, it uses the implementation module to do this:
defmodule Sequence.Impl do
def next(number), do: number + 1
def increment(number, delta), do: number + delta
end
Run the code using the following:
iex> Sequence.Impl.start_link 123
{:ok, #PID<0.179.0>}
iex> Sequence.Impl.next(123)
124
iex> Sequence.Impl.increment(123,100)
223
Use commands in the above snippet to run the code below:
Get hands-on with 1400+ tech skills courses.