6
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

ElixirAdvent Calendar 2024

Day 20

Ash Frameworkを知る第一歩!65ページ資料から抽出した2つの重要なポイント

Posted at

はじめに

Ash Framworkの作者のZachさんが来日されて、Ash Frameworkについての講演をされました。

この講演の直前まで、Ash Frameworkについては全く知識がなかったのですが、講演に参加して話を伺って、いままで聞いたことのない切口で、とても面白いとおもいました。
詳しい事は、Zachさんの講演スライドを見ていただくのが良いと思います。

このうち、ポイントになる2点を紹介したいと思います。

この記事のプログラムは、Zachさんの講演スライドにあるコードをみやすいようにテキスト化したものです。

コードよりデータを重視

Zachさんの講演スライドの最初の方に、Data > Code という言葉が出てきます。
Data > Codeとは、データを重視するという意味だと思います。

次の例を見てみましょう。

defmodule InputValidator do
  def validate_agename(input) do
    case Map.get(input, :name) do
      nil -> {:error, :name, "Name is required"}
      value when is_binary(value) -> :ok
      _ -> {:error, :name, "Name must be a string"}
    end
  end

  @rules [
    &validate_name/1,
    &validate_age/1,
    &validate_email/1
  ]
end

このコードは、入力値を検証するモジュールです。このモジュールは、入力値を検証するための関数を定義しています。よく見られるパターンですね。
Dataを中心に考えた場合は、次のようになります。

defmodule InputValidator do
  @rules [
    {:name, &is_binary/1, "Name must be a string"},
    {:age, &(&1 > 18), "Age must be 18 or older"},
    {:email, &String.contains?(&1, "@"), "Email must contain an @"}
  ]
end

検証のルールをデータとして定義しているのです。
もちろんこのデータだけでは、検証を行うことはできません。検証を行うための関数も必要です。

defmodule InputValidator do
  @rules [ ... ]

  def validate_one_field(field, value) do
    Enum.reduce_while(@rules, :ok, fn {rule_field, validator, error}, :ok ->
      if rule_field != field || validator.(value) do
        {:cont, :ok}
      else
        {:halt, {:error, error}}
      end
    end)
  end
end

このように、データと検証のルールを定義することで、Validatorを実現する事ができます。

手書きよりも自動生成

Zachさんの講演スライドでは、次に、Derive > Handwrite という言葉がでてきます。
手書きのコードよりも自動生成を重視するという事だと思います。

まずは、手書きの例を見てみましょう。

defmodule MyForm do
  use MyAppWeb, :live_component

  def render(assigns) do
    ~H"""
    <.form for={@form}>
      ...
      <.input field={@form[:age]} />
      <.field_tip>
        Age must be greater than 18.
      </.field_tip>
    </.form>
    """
  end
end

このコードは、よくある、フォームを表示するコンポーネントです。:ageのフィールドについての説明(field_tip)を表示しています。

これに対して、自動生成の例を見てみましょう。

defmodule MyForm do
  use MyAppWeb, :live_component

  def render(assigns) do
    ~H"""
    <.form for={@form}>
      ...
      <.input field={@form[:age]} />
      <%= for requirement <- InputValidator.requirements(:age) do %>
        <.field_tip>
          <%= requirement %>
        </.field_tip>
      <% end %>
    </.form>
    """
  end
end

この例には、Age must be greater than 18. という文字列はなく、InputValidator.requirements(:age)から取得していて、自動生成されています。

さらに、:ageだけでなく、他のフィールドについても生成するコードを書くこともできます。

defmodule MyForm do
  use MyAppWeb, :live_component

  def render(assigns) do
    ~H"""
    <.form for={@form}>
      <%= for field <- InputValidator.fields() do %>
        <.input field={@form[field]} />
        <%= for requirement <- InputValidator.requirements(field) do %>
          <.field_tip>
            <%= requirement %>
          </.field_tip>
        <% end %>
      <% end %>
    </.form>
    """
  end
end

この例は、Web画面の例ですが、モデリングする対象は、Webサーバだけではなく、データベース等も対象となっています。
この方法で、モデリングすると、データベースの種類がかわっても、モデリングのデータ部分は変わらず、処理の部分がデータベースによって変わるだけという事になります。

まとめ

Zachさんの講演スライドには、まだポイントになることがあるんですが、

  • Data > Code
  • Derive > Handwrite

この2つAsh Frameworkの特徴をよく表していると思いました。

わたしも、類似したバリデーションがたくさんあった時に、パラメータと共通化した関数にしてみた事は、ありますが、これを突き詰めて、開発対象のモデリングにまで広げた所が、Ash Frameworkの凄い所だと思いました。

このAsh Framworkは、現在すでにバージョン3.4.47までリリースされています。詳しくは、以下のリンクを参照してください。

今後AIによる開発が進んでくると、何を作るのかを表現する事が重要になってくると思います。その時に、Ash Frameworkのような思想が役立つのかもしれないなぁ。

6
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
6
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?