Removing the state-machines from a LiveView

I’ve been playing with an idea for LiveView… I wanted fast tests of LiveView page logic - which isn’t really possible while the logic is mixed up in the LiveView.

PageMachine is the result - it’s very much a WIP and hasn’t seen any real use yet.

It models the page logic as a collection of concurrent Spindles - coroutine-based state machines which manage state and effects for the LiveView.

defmodule MyApp.CheckoutSpindle do
  use Skuld.Syntax
  alias MyApp.StoreContract.Checkout

  defcomp run(product) do
    {:ok, _} <- MyApp.Inventory.reserve(%{product: product})
    %Checkout.ShippingEvent{shipping: shipping} <- Checkout.Yield.shipping()
    %Checkout.PaymentEvent{payment: payment} <- Checkout.Yield.payment()
    {:ok, order} <- MyApp.Orders.place(%{product: product}, shipping, payment)
    {:ok, order}
  else
    {:error, :sold_out} -> {:error, :sold_out}
    {:error, reason} -> {:error, reason}
  end
end

The Spindle is a pure computation, and can be tested by stepping it directly - there’s no need for a LiveView Process - tests run in microseconds.

I think the coroutines allow the back-and-forth logic of the interaction between the LiveView and the state to be expressed very straightforwardly.

Plenty more detail here, including how these Spindles get wired up to a LiveView, how they get bundled into a PageMachine and more: PageMachine — skuld v0.33.0

What do you think ?

I think this is way over engineered. What I do is for every LiveView I define a protocol, basically a view model from the MVVM architecture. This allows me to test both the protocol implementations and the live view with a stubbed view model. That’s it.

2 Likes

Ha - you might be right!

But I’m really liking the way the coroutines express state machines as normal(ish) code, and you get a conversation between the page and the coroutine - so complex branching logic gets written as you think of it, rather than decomposed into event handlers.

Gonna try it on some real pages and see how it works out!