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

# はじめに

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

https://daimon-ex.connpass.com/event/334725/

この講演の直前まで、Ash Frameworkについては全く知識がなかったのですが、講演に参加して話を伺って、いままで聞いたことのない切口で、とても面白いとおもいました。
詳しい事は、[Zachさんの講演スライド](https://drive.google.com/file/d/1LK4qMBskZ9na6H73E91ViCtVVlbmcbEa/view?usp=sharing)を見ていただくのが良いと思います。

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

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

# コードよりデータを重視

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

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

```Elixir
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を中心に考えた場合は、次のようになります。

```Elixir
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
```

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

```Elixir
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 という言葉がでてきます。
手書きのコードよりも自動生成を重視するという事だと思います。

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

```Elixir
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)を表示しています。

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

```Elixir
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だけでなく、他のフィールドについても生成するコードを書くこともできます。

```Elixir
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までリリースされています。詳しくは、以下のリンクを参照してください。

https://hexdocs.pm/ash/readme.html

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