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.

8480daec7137f28565bc2d2e666b915a?s=128

Tobias Pfeiffer

May 26, 2018
Tweet

Transcript

  1. None
  2. functional

  3. functional ErlangVM

  4. functional ErlangVM Parallelism

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

  6. None
  7. None
  8. None
  9. None
  10. You shouldn’t

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

  12. Your Monolith and You

  13. None
  14. Your Monolith now?

  15. Your Monolith?

  16. Tear it all down and rewrite it

  17. None
  18. Sooooon!

  19. “We rewrote it all in Language x, now it’s 10

    times faster and only 50% of the code!” Someone
  20. “Language x, is 10 times faster and requires 50% of

    the code of language Y!” Someone Often implies...
  21. What nobody tells you...

  22. Business Value?

  23. Sooooon!

  24. Replace it step by step

  25. We have a bedroom, for the bathroom please check out

    legacy!
  26. Terraform allows you to incrementally transform an older API into

    one powered by Phoenix - one endpoint at a time. poteto/terraform
  27. 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
  28. 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
  29. Replace a critical component

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

  31. Check out our sweet new heating system!

  32. Write a new component

  33. Check it out we built a brand new high tech

    entertainment facility!
  34. Write a new component Obvious in microservices

  35. “Is the technology I’m choosing the right fit for the

    problem I’m trying to solve?” Everyone, hopefully
  36. Reasonably separate and limited problem

  37. Requirement that poses a problem for the current stack

  38. Spark of Enthusiasm

  39. Courier Tracking

  40. Courier Tracking

  41. Courier Tracking

  42. Courier Tracking Websockets

  43. Courier Tracking Websockets Basic Knowledge

  44. How to make them talk?

  45. Monolith Frontend Courier DB

  46. Monolith Tracking Frontend Courier DB DB

  47. Connect them?

  48. Monolith Tracking Frontend Courier DB DB

  49. JSON Web Token

  50. Header Payload Signature

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

    }
  52. Monolith Tracking Frontend Courier DB DB Shared Secret

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

  54. Monolith Tracking Frontend Courier DB DB JWT JWT JWT Shared

    Secret JWT
  55. def connect(%{"token" => token}, socket) do token |> JWT.verify_token |>

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

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

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

    %{role: role, ...})} end Socket
  59. def join("courier:" <> courier_id, _, %{assigns: %{user: user}}) do if

    authorized?(courier_id, user) do # login else # unauthorizd end end Channel
  60. 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
  61. 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
  62. 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
  63. 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
  64. 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
  65. Monolith Tracking Frontend Courier DB DB Locations Locations Most of

    the time
  66. Monolith Tracking Frontend Courier DB DB Locations Locations Websocket

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

  68. Fulfillment

  69. Fulfillment CRUD

  70. Fulfillment CRUD Experience

  71. Monolith Fulfillment DB DB

  72. Monolith Fulfillment DB DB Own UI

  73. Monolith Fulfillment DB DB OAuth OAuth

  74. Monolith Fulfillment DB DB /current_user Who?

  75. Monolith Fulfillment DB DB Products

  76. Monolith Fulfillment DB DB Order Valid? Order

  77. Monolith Fulfillment DB DB Packaging Create

  78. Monolith Fulfillment DB DB HTTP REST Create

  79. Railway Oriented Programming

  80. 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
  81. 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
  82. 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
  83. 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
  84. 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
  85. 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
  86. 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
  87. No Kafka/ Message Queue/ GraphQL?

  88. Be careful with new technologies!

  89. Microservices!

  90. Microservices! ”Macroapplications”

  91. All challenges are clearly technical

  92. All challenges are clearly technical

  93. Stakeholders

  94. Your team

  95. First Major Version

  96. Get the others on board

  97. Workshops

  98. To know it, you got to build it

  99. None
  100. Pair Adopter with Alchemist

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

  102. Knowledge

  103. Knowledge

  104. “We are still using this like Rails – we should

    use GenServers!” Someone on your team
  105. You don’t need to kill your Monolith, Complement it

  106. Your Apps

  107. Enjoy Using Elixir effectively to Help Your Company Tobias Pfeiffer

    @PragTob pragtob.info