はじめに
デジタルアーカイブの要件は、現場ごとに大きく異なります。考古遺物の写真管理、福祉施設の支援記録、医療機関の画像ファイル、いにしえの街の風景写真——それぞれでメタデータの項目も、操作UIの複雑さへの許容度も、まったく違います。
OmniArchiveは、IIIF Presentation API v3 に対応したデジタルアーカイブ基盤として、前身プロジェクト AlchemIIIF を汎用化する形で開発・構築中です。コアはElixir/Phoenix LiveViewで書かれており、業務ドメインの差異をYAMLプロファイルとして外部化することで、同一のコードベースで複数の現場に対応できることを目指しています。
この記事では、その設計の核心である YAMLドメインプロファイルシステム と、Phoenix LiveViewでどう実装しているのかを中心に解説します。
1. 技術スタック
| 層 | 技術 |
|---|---|
| バックエンド | Elixir / Phoenix LiveView |
| DB | PostgreSQL15以上 |
| フロントエンド | LiveView + TailwindCSS |
| ライセンス | Apache 2.0 |
| CLIツール | Go(pdf2png / img2png) |
2. 設計の核心:YAMLドメインプロファイル
問題意識
AlchemIIIFは当初、特定の施設向けに設計されていたため、メタデータ項目やUIの構造がハードコードされていました。これは障がい特性に合わせて、ミスを誘発しないようにするためでした。しかし、他に転用するためには、コードを直接書き換える必要がありました。
OmniArchiveでは、この業務ドメイン固有の差異をYAMLファイルに分離することにしました。
プロファイルの構造
# profiles/archaeological_site.yaml
profile_name: archaeological_site
display_name: 考古遺跡記録
metadata_fields:
- key: site_code
label: 遺跡コード
type: string
required: true
- key: material
label: 素材
type: string
required: false
- key: rights
label: 権利情報
type: string
required: true
iiif:
required_statement: true
provider: true
このYAMLがあれば、pdf_sourcesテーブルのprofile_nameカラムを参照するだけで、レコードごとに異なるメタデータ定義を動的に適用できます。
DBスキーマとの連携
pdf_sourcesテーブルにprofile_nameカラムを追加しています。
ALTER TABLE pdf_sources
ADD COLUMN profile_name VARCHAR(64) NOT NULL DEFAULT 'default';
Elixir側では、プロファイルをETS(メモリキャッシュ)にロードし、LiveViewから参照する形をとっています。
# プロファイルのロード(起動時)
defmodule OmniArchive.Profiles do
use GenServer
def load_all do
Path.wildcard("priv/profiles/*.yaml")
|> Enum.each(&load_file/1)
end
defp load_file(path) do
profile = YamlElixir.read_from_file!(path)
:ets.insert(:profiles, {profile["profile_name"], profile})
end
def get(profile_name) do
case :ets.lookup(:profiles, profile_name) do
[{_, profile}] -> {:ok, profile}
[] -> {:error, :not_found}
end
end
end
3. Phoenix LiveViewでの動的フォーム生成
プロファイルのメタデータ定義を受け取り、LiveViewが動的にフォームフィールドを生成します。
# live/pdf_source_live/form_component.ex(概略)
defp render_metadata_fields(assigns, profile) do
profile["metadata_fields"]
|> Enum.map(fn field ->
~H"""
<.input
field={@form[String.to_atom(field["key"])]}
label={field["label"]}
type="text"
required={field["required"]}
/>
"""
end)
end
フィールドの追加・削除はYAMLを編集するだけで完結し、LiveViewコードを触る必要がありません。
4. IIIF Presentation API v3 への対応
IIIFマニフェスト生成時に、プロファイルの設定に従って任意のメタデータを埋め込みます。
対応フィールド:
-
requiredStatement(必須情報表示) -
rights(ライセンスURI) -
provider(提供機関情報) -
site_code(独自拡張:遺跡コードなど) -
material(独自拡張:素材情報)
defp build_manifest(source, profile) do
base = %{
"@context" => "http://iiif.io/api/presentation/3/context.json",
"id" => manifest_url(source),
"type" => "Manifest",
"label" => %{"ja" => [source.title]}
}
base
|> maybe_put_required_statement(source, profile)
|> maybe_put_rights(source, profile)
|> maybe_put_provider(source, profile)
end
5. 管理者専用プロファイル管理という設計判断
プロファイルの切り替えは管理者のみに限定しています。
この制約は意図的なものです。OmniArchiveの主な利用想定には、ITリテラシーが多様なスタッフが使う福祉・医療現場も含まれます。エンドユーザーがプロファイルを誤って変更できる状態は、データ構造の不整合とサポートコストの増大を招きます。
「機能を隠すことで、使う人を守る」 という設計判断をここに明示しておきます。
6. Go製CLIツール(pdf2png / img2png)
まだOmniArchiveそのものは対応しておりませんが、各種画像ファイルを非エンジニアスタッフが直接扱えて、取り込むためのCLIツールをGoで別途作成しています。
- pdf2png:PDFをページ単位でPNG変換
- img2png:各種画像フォーマットをPNGに統一
ポイントはドラッグ&ドロップで使えること。Finderやエクスプローラーからファイルをターミナルにドロップすれば、引数入力なしで動きます。macOS/Windowsのバイナリを GitHub Actions でクロスコンパイルして配布しています。
7. 今後の課題
- プロファイルのバリデーション強化(YAML構造の検査)
- Web UIからのプロファイル閲覧(管理者向け)
- マルチテナント対応の検討
- 画像ファイルを直接読み込めるように
まとめ
| 関心 | OmniArchiveの答え |
|---|---|
| 多現場対応 | YAMLプロファイルで業務差異を外部化 |
| UI安定性 | 管理者専用プロファイル管理でユーザー保護 |
| IIIF準拠 | Presentation API v3の主要フィールドを動的生成 |
| 非エンジニア対応 | Go製CLIでドラッグ&ドロップ取り込み |
Phoenix LiveViewの「サーバー駆動UI」の特性は、このような動的フォーム生成との相性が非常に良いと感じています。YAMLを足がかりにしたドメイン外部化のパターンは、他のPhoenixプロジェクトにも応用できると思います。
まだまだ開発・デバッグ中ではありますが、興味を持った方はぜひリポジトリを見てみてください。