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. View Slide

  2. functional

    View Slide

  3. functional
    ErlangVM

    View Slide

  4. functional
    ErlangVM
    Parallelism

    View Slide

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

    View Slide

  6. View Slide

  7. View Slide

  8. View Slide

  9. View Slide

  10. You shouldn’t

    View Slide

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

    View Slide

  12. Your Monolith and You

    View Slide

  13. View Slide

  14. Your Monolith now?

    View Slide

  15. Your Monolith?

    View Slide

  16. Tear it all down and
    rewrite it

    View Slide

  17. View Slide

  18. Sooooon!

    View Slide

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

    View Slide

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

    View Slide

  21. What nobody tells you...

    View Slide

  22. Business Value?

    View Slide

  23. Sooooon!

    View Slide

  24. Replace it step by step

    View Slide

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

    View Slide

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

    View Slide

  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

    View Slide

  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

    View Slide

  29. Replace a critical
    component

    View Slide

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

    View Slide

  31. Check out our sweet new
    heating system!

    View Slide

  32. Write a new component

    View Slide

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

    View Slide

  34. Write a new component
    Obvious in microservices

    View Slide

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

    View Slide

  36. Reasonably separate and
    limited problem

    View Slide

  37. Requirement that poses a
    problem for the current
    stack

    View Slide

  38. Spark of Enthusiasm

    View Slide

  39. Courier Tracking

    View Slide

  40. Courier Tracking

    View Slide

  41. Courier Tracking

    View Slide

  42. Courier Tracking
    Websockets

    View Slide

  43. Courier Tracking
    Websockets
    Basic Knowledge

    View Slide

  44. How to make them talk?

    View Slide

  45. Monolith
    Frontend
    Courier
    DB

    View Slide

  46. Monolith
    Tracking
    Frontend
    Courier
    DB DB

    View Slide

  47. Connect them?

    View Slide

  48. Monolith
    Tracking
    Frontend
    Courier
    DB DB

    View Slide

  49. JSON
    Web
    Token

    View Slide

  50. Header
    Payload
    Signature

    View Slide

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

    View Slide

  52. Monolith
    Tracking
    Frontend
    Courier
    DB DB
    Shared Secret

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

  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

    View Slide

  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

    View Slide

  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

    View Slide

  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

    View Slide

  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

    View Slide

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

    View Slide

  66. Monolith
    Tracking
    Frontend
    Courier
    DB DB
    Locations
    Locations
    Websocket

    View Slide

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

    View Slide

  68. Fulfillment

    View Slide

  69. Fulfillment
    CRUD

    View Slide

  70. Fulfillment
    CRUD
    Experience

    View Slide

  71. Monolith
    Fulfillment
    DB DB

    View Slide

  72. Monolith
    Fulfillment
    DB DB
    Own UI

    View Slide

  73. Monolith
    Fulfillment
    DB DB
    OAuth
    OAuth

    View Slide

  74. Monolith
    Fulfillment
    DB DB
    /current_user
    Who?

    View Slide

  75. Monolith
    Fulfillment
    DB DB
    Products

    View Slide

  76. Monolith
    Fulfillment
    DB DB
    Order
    Valid?
    Order

    View Slide

  77. Monolith
    Fulfillment
    DB DB
    Packaging
    Create

    View Slide

  78. Monolith
    Fulfillment
    DB DB
    HTTP REST
    Create

    View Slide

  79. Railway Oriented Programming

    View Slide

  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

    View Slide

  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

    View Slide

  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

    View Slide

  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

    View Slide

  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

    View Slide

  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

    View Slide

  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

    View Slide

  87. No Kafka/
    Message Queue/
    GraphQL?

    View Slide

  88. Be careful with new
    technologies!

    View Slide

  89. Microservices!

    View Slide

  90. Microservices!
    ”Macroapplications”

    View Slide

  91. All challenges are
    clearly technical

    View Slide

  92. All challenges are
    clearly technical

    View Slide

  93. Stakeholders

    View Slide

  94. Your team

    View Slide

  95. First Major Version

    View Slide

  96. Get the others on board

    View Slide

  97. Workshops

    View Slide

  98. To know it,
    you got to build it

    View Slide

  99. View Slide

  100. Pair Adopter with Alchemist

    View Slide

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

    View Slide

  102. Knowledge

    View Slide

  103. Knowledge

    View Slide

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

    View Slide

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

    View Slide

  106. Your Apps

    View Slide

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

    View Slide