Upgrade to Pro — share decks privately, control downloads, hide ads and more …

Your Monolith, Elixir and You

Your Monolith, Elixir and You

Elixir is great, so clearly we'll all rewrite our applications in Elixir. Mostly, you can't and shouldn't do that. This presentation will show you another path. You’ll see how at Liefery, we started with small steps instead of rewriting everything. This allowed us to reap the benefits earlier and get comfortable before getting deeper into it. We’ll examine in detail the tactics we used to create two Elixir apps for new requirements, and how we integrated them with our existing Rails code base.

Tobias Pfeiffer

May 26, 2018
Tweet

More Decks by Tobias Pfeiffer

Other Decks in Programming

Transcript

  1. functional
    ErlangVM

    View full-size slide

  2. functional
    ErlangVM
    Parallelism

    View full-size slide

  3. Who wants to go and
    build a system in Elixir?

    View full-size slide

  4. You shouldn’t

    View full-size slide

  5. Elixir, Your Monolith and You
    Tobias Pfeiffer
    @PragTob
    pragtob.info

    View full-size slide

  6. Your Monolith and You

    View full-size slide

  7. Your Monolith now?

    View full-size slide

  8. Your Monolith?

    View full-size slide

  9. Tear it all down and
    rewrite it

    View full-size slide

  10. “We rewrote it all in
    Language x, now it’s 10
    times faster and only
    50% of the code!”
    Someone

    View full-size slide

  11. “Language x, is 10 times
    faster and requires 50% of
    the code of language Y!”
    Someone
    Often implies...

    View full-size slide

  12. What nobody tells you...

    View full-size slide

  13. Business Value?

    View full-size slide

  14. Replace it step by step

    View full-size slide

  15. We have a bedroom,
    for the bathroom please
    check out legacy!

    View full-size slide

  16. Terraform allows you to
    incrementally transform an
    older API into one powered by
    Phoenix - one endpoint at a
    time.
    poteto/terraform

    View full-size slide

  17. Terraform allows you to
    incrementally transform an
    older API into one powered by
    Phoenix - one endpoint at a
    time.
    poteto/terraform
    Not the Infrastructure
    As Code

    View full-size slide

  18. defmodule MyApp.Terraformers.Foo do
    alias MyApp.Clients.Foo
    use Plug.Router
    plug :match
    plug :dispatch
    get "/v1/hello-world", do: send_resp(conn, 200, "Hi")
    get _ do
    res = Foo.get!(conn)
    send_response({:ok, conn, res})
    end
    end
    poteto/terraform

    View full-size slide

  19. Replace a critical
    component

    View full-size slide

  20. Damn our heater always
    breaks down when it’s cold!

    View full-size slide

  21. Check out our sweet new
    heating system!

    View full-size slide

  22. Write a new component

    View full-size slide

  23. Check it out we built a
    brand new high tech
    entertainment facility!

    View full-size slide

  24. Write a new component
    Obvious in microservices

    View full-size slide

  25. “Is the technology I’m
    choosing the right fit for
    the problem I’m trying to
    solve?”
    Everyone, hopefully

    View full-size slide

  26. Reasonably separate and
    limited problem

    View full-size slide

  27. Requirement that poses a
    problem for the current
    stack

    View full-size slide

  28. Spark of Enthusiasm

    View full-size slide

  29. Courier Tracking

    View full-size slide

  30. Courier Tracking

    View full-size slide

  31. Courier Tracking

    View full-size slide

  32. Courier Tracking
    Websockets

    View full-size slide

  33. Courier Tracking
    Websockets
    Basic Knowledge

    View full-size slide

  34. How to make them talk?

    View full-size slide

  35. Monolith
    Frontend
    Courier
    DB

    View full-size slide

  36. Monolith
    Tracking
    Frontend
    Courier
    DB DB

    View full-size slide

  37. Connect them?

    View full-size slide

  38. Monolith
    Tracking
    Frontend
    Courier
    DB DB

    View full-size slide

  39. JSON
    Web
    Token

    View full-size slide

  40. Header
    Payload
    Signature

    View full-size slide

  41. {
    "role": "admin",
    "courier_ids": [1337, 55],
    "id": 42,
    "exp": 1520469475
    }

    View full-size slide

  42. Monolith
    Tracking
    Frontend
    Courier
    DB DB
    Shared Secret

    View full-size slide

  43. Monolith
    Tracking
    Frontend
    Courier
    DB DB
    JWT
    JWT
    Shared Secret

    View full-size slide

  44. Monolith
    Tracking
    Frontend
    Courier
    DB DB
    JWT
    JWT
    JWT
    Shared Secret
    JWT

    View full-size slide

  45. def connect(%{"token" => token}, socket) do
    token
    |> JWT.verify_token
    |> respond_based_on_token_contents(socket)
    end
    Socket

    View full-size slide

  46. def connect(%{"token" => token}, socket) do
    token
    |> JWT.verify_token
    |> respond_based_on_token_contents(socket)
    end
    Pattern Match

    View full-size slide

  47. def connect(%{"token" => token}, socket) do
    token
    |> JWT.verify_token
    |> respond_based_on_token_contents(socket)
    end
    Pipe

    View full-size slide

  48. defp login_user(%{"role" => role, ...}, socket) do
    {:ok, assign(socket, :user, %{role: role, ...})}
    end
    Socket

    View full-size slide

  49. def join("courier:" <> courier_id, _,
    %{assigns: %{user: user}}) do
    if authorized?(courier_id, user) do
    # login
    else
    # unauthorizd
    end
    end
    Channel

    View full-size slide

  50. defp authorized?("", _), do: false
    defp authorized?(courier_id, %{courier_ids: ids}) do
    Enum.member?(ids, String.to_integer(courier_id))
    end
    defp authorized?(_, _), do: false
    Channel

    View full-size slide

  51. defp authorized?("", _), do: false
    defp authorized?(courier_id, %{courier_ids: ids}) do
    Enum.member?(ids, String.to_integer(courier_id))
    end
    defp authorized?(_, _), do: false
    Channel

    View full-size slide

  52. defp authorized?("", _), do: false
    defp authorized?(courier_id, %{courier_ids: ids}) do
    Enum.member?(ids, String.to_integer(courier_id))
    end
    defp authorized?(_, _), do: false
    Channel

    View full-size slide

  53. defp authorized?("", _), do: false
    defp authorized?(courier_id, %{courier_ids: ids}) do
    Enum.member?(ids, String.to_integer(courier_id))
    end
    defp authorized?(_, _), do: false
    Channel

    View full-size slide

  54. defp authorized?("", _), do: false
    defp authorized?(courier_id, %{courier_ids: ids}) do
    Enum.member?(ids, String.to_integer(courier_id))
    end
    defp authorized?(_, _), do: false
    Channel

    View full-size slide

  55. Monolith
    Tracking
    Frontend
    Courier
    DB DB
    Locations
    Locations
    Most of the time

    View full-size slide

  56. Monolith
    Tracking
    Frontend
    Courier
    DB DB
    Locations
    Locations
    Websocket

    View full-size slide

  57. Monolith
    Tracking
    Frontend
    Courier
    DB DB
    Locations
    Locations
    HTTP REST

    View full-size slide

  58. Fulfillment
    CRUD

    View full-size slide

  59. Fulfillment
    CRUD
    Experience

    View full-size slide

  60. Monolith
    Fulfillment
    DB DB

    View full-size slide

  61. Monolith
    Fulfillment
    DB DB
    Own UI

    View full-size slide

  62. Monolith
    Fulfillment
    DB DB
    OAuth
    OAuth

    View full-size slide

  63. Monolith
    Fulfillment
    DB DB
    /current_user
    Who?

    View full-size slide

  64. Monolith
    Fulfillment
    DB DB
    Products

    View full-size slide

  65. Monolith
    Fulfillment
    DB DB
    Order
    Valid?
    Order

    View full-size slide

  66. Monolith
    Fulfillment
    DB DB
    Packaging
    Create

    View full-size slide

  67. Monolith
    Fulfillment
    DB DB
    HTTP REST
    Create

    View full-size slide

  68. Railway Oriented Programming

    View full-size slide

  69. defmodule Fulfillment.OrderCreation do
    def create(params, customer, auth) do
    with {:ok, order} <- validate_order(params, customer),
    {:ok, order} <- validate_in_backend(order, auth),
    {:ok, order} <- Repo.insert(order) do
    {:ok, order}
    else
    {:error, changeset} -> {:error, changeset}
    end
    end
    end

    View full-size slide

  70. defmodule Fulfillment.OrderCreation do
    def create(params, customer, auth) do
    with {:ok, order} <- validate_order(params, customer),
    {:ok, order} <- validate_in_backend(order, auth),
    {:ok, order} <- Repo.insert(order) do
    {:ok, order}
    else
    {:error, changeset} -> {:error, changeset}
    end
    end
    end
    “Success” Rail

    View full-size slide

  71. defmodule Fulfillment.OrderCreation do
    def create(params, customer, auth) do
    with {:ok, order} <- validate_order(params, customer),
    {:ok, order} <- validate_in_backend(order, auth),
    {:ok, order} <- Repo.insert(order) do
    {:ok, order}
    else
    {:error, changeset} -> {:error, changeset}
    end
    end
    end
    “Error” Rail

    View full-size slide

  72. defmodule Fulfillment.OrderCreation do
    def create(params, customer, auth) do
    with {:ok, order} <- validate_order(params, customer),
    {:ok, order} <- validate_in_backend(order, auth),
    {:ok, order} <- Repo.insert(order) do
    {:ok, order}
    else
    {:error, changeset} -> {:error, changeset}
    end
    end
    end
    Validate internally

    View full-size slide

  73. defmodule Fulfillment.OrderCreation do
    def create(params, customer, auth) do
    with {:ok, order} <- validate_order(params, customer),
    {:ok, order} <- validate_in_backend(order, auth),
    {:ok, order} <- Repo.insert(order) do
    {:ok, order}
    else
    {:error, changeset} -> {:error, changeset}
    end
    end
    end
    Validate externally

    View full-size slide

  74. defmodule Fulfillment.OrderCreation do
    def create(params, customer, auth) do
    with {:ok, order} <- validate_order(params, customer),
    {:ok, order} <- validate_in_backend(order, auth),
    {:ok, order} <- Repo.insert(order) do
    {:ok, order}
    else
    {:error, changeset} -> {:error, changeset}
    end
    end
    end
    Save

    View full-size slide

  75. defmodule Fulfillment.OrderCreation do
    def create(params, customer, auth) do
    with {:ok, order} <- validate_order(params, customer),
    {:ok, order} <- validate_in_backend(order, auth),
    {:ok, order} <- Repo.insert(order) do
    {:ok, order}
    else
    {:error, changeset} -> {:error, changeset}
    end
    end
    end
    Done

    View full-size slide

  76. No Kafka/
    Message Queue/
    GraphQL?

    View full-size slide

  77. Be careful with new
    technologies!

    View full-size slide

  78. Microservices!

    View full-size slide

  79. Microservices!
    ”Macroapplications”

    View full-size slide

  80. All challenges are
    clearly technical

    View full-size slide

  81. All challenges are
    clearly technical

    View full-size slide

  82. Stakeholders

    View full-size slide

  83. First Major Version

    View full-size slide

  84. Get the others on board

    View full-size slide

  85. To know it,
    you got to build it

    View full-size slide

  86. Pair Adopter with Alchemist

    View full-size slide

  87. Pair Adopter with Alchemist
    Alchemist doesn’t touch keyboard

    View full-size slide

  88. “We are still using this
    like Rails – we should
    use GenServers!”
    Someone on your team

    View full-size slide

  89. You don’t need to kill
    your Monolith,
    Complement it

    View full-size slide

  90. Enjoy Using Elixir effectively to
    Help Your Company
    Tobias Pfeiffer
    @PragTob
    pragtob.info

    View full-size slide