採用支援サイトの設計で、仕事を探す、検索機能の URI 設計を、やってみた。
基本は、「 query 文字列を使わない URI 設計 」のこころみ。
主に参考にした本は、
・Restful Web サービス
・Web を支える技術
実装技術としては、Spring 3 MVC の URI テンプレート機能を使う。 モデリングといいながら、@Controller の @RequestMapping の実装コードを、かなり意識している。
モデルとコードは、コインの表と裏のように結びついているし、距離も近いもの、という主義なので、だいたいこんな感じ。 Enterprise Architect で、 UML 風の絵を描きながら、頭の中は、結構、コードが飛び交っている。
求人案件の基本の分類整理軸は、悩ましい問題。
求人サイトでわりと多いのが、職種分類の階層。
http://example.com/job/{職種の大分類名}/{中分類名}/{小分類名}/{募集案件名}
(以下、http://example.com は省略)
( {パス要素} という記法は、Spring 3 MVC の URI テンプレートの {RequestVariable} の記法を使っている。)
例えば、
/job/クリエィティブ/web関連/webデザイナー/ソーシャルゲームのグラフィックデザイナ
という感じ。
下位のパス要素を記述しない場合は、
/job/ だけなら、職種の大分類の一覧
/job/クリティティブ/ なら、職種の中分類の一覧
...
という感じで、より、特定の仕事リソースに近づいていく。
画面でいえば、仕事リソースは、仕事の詳細表示画面で、そこで表示するパン屑リストが、この URI そのまま、という感じ。
実際には、上記の「ソーシャルゲームのグラフィックデザイナ」の求人案件は、この URI では、一意に識別できない。
同じ職種名の案件が他にもある可能性がある。
かといって、職種名の下に、さらに階層があるかというと、ちょっと違う感じ。
職種名は、求人企業が任意に指定しているので、それが、分類の階層構造の一部というのは、たぶん、モデルとしてまちがい。
いちおう、今の設計案は、
/job/クリエィティブ/web関連/webデザイナー/ソーシャルゲームのグラフィックデザイナ.2996411
というように、最後の要素は、
{募集案件名}.{案件管理番号}
にしようと思っている。
RequestMapping のほうは、 /job/{zone}/{segment}/{category}/{jobtitle}.{id} と記述しておけば、それぞれのパス要素を、その変数名で簡単に取り出せる。
かなり乱暴だけど、
/job/*/*/*/*.{id} というマッチングで、 id だけ変数として、コントローラで処理することもできる。
ここらへの実装上の記法は、まだ、トライアル段階のところがあって、どう記述するか、手探り。
基本的には、いろいろな URI テンプレートののリストを、Spring が、 パターンマッチングをして、「特定できる順」に、一致させてくれるはず。
ただ、パターンを駆使して、解決をフレームワークにまかせる記法は、あいまいだったり、トリッキーで、障害の種だし、また、可読性も悪く、変更の副作用が怖い。
だから、できるだけ、明示的に書くことを重視しようと思っている。
( 変数として未使用でも、 * ではなく、{zone} のように、明示しようということ)
仕事は、データベース上では、実は、求人企業を親として、その下にぶら下がっている。
上記の、職種分類体系で、整理して持ているわけではない。
データベース上の構造をそのまま素直に URI にすると、
/job/{求人企業}/{求人案件名}.{求人番号}
になる。
ここで、第一階層の要素が、/job/ だと、さっきの、職種分類の /job/ と同じになってしまう。
複数のパス階層が並立する場合には、 /job/ だけでは、まずい。
で、考えたのが、
職種分類の階層は、 /job-category/
企業を親にする階層は、/company/
というように、使い分けることにした。
http://jobs.example.com/job-category/{zone}/{segment}
というように job は、サブドメインに持っていった。
仕事を探すサイト、が明確になったので、こっちのほうが、適切。
job-category とか、company とか、検索の切り口を、サブドメインにしてしまう、というのもちょっと考えた。
ただ、サイトとして、job-category.example.com だと、(政府の統計局とかの)、職種分類体系の検索サイトのような意味になりそう。 仕事を探す、というシンプルな目的とは、ちょっとずれる感じ。
company.example.com だと、さらにはっきりして、これだと、仕事探しではなく、企業情報リソース満載のサイト、という感じ。
というわけで、検索の視点の違いを、サブドメインで分けるのは、却下。
( Spring の URI テンプレートで、サブドメインの違いで処理を分けるというは、実装がややこしくなる、という実装面の問題もある)
/company/{企業名}/{募集案件名}.{案件管理番号}
これは、データベースの構造のまま。
実際には、求職者向けのサービスとしては、こういう構造では提供していない。
/company/ で、ずらっと、求人企業が並ぶ。そこから、特定企業を選ぶと、
/company/Example株式会社/ というURIがリクエストされ、その企業の求人案件が並ぶ。
という画面は、普通なない。
/industory/{業種}/{企業名}/{募集案件名}.{案件管理番号}
というような感じで、「業種」から、企業を探す、というのがありそう。
あとは、
/area/{地域}/{都道府県}/{市区町村}
という勤務地を軸にした体系もありそう。
ナビゲーションの切り口は、職種分類、業種・企業、勤務地など、複数ある。
URI もそれぞれに応じた、体系にする。
ただし、最後の {募集案件名}.{案件管理番号} は、実体は一つにしておきたい。
同じ仕事(リソース)に、URI (一意識別情報) が複数あるのはおかしいでしょう、という、そもそも論。
実際には、一覧表示画面に、返す 正式(?)のURI は、
/company/{企業名}/{募集案件名}.{案件管理番号}
に統一しようと思っている。
/area/{地域}/{都道府県}/{市区町村}
のリクエストに対するレスポンス(リソース表現)は、 /company/{企業名}/{募集案件名}.{案件管理番号} のリストになる、ということ。
もうひとつの方法として、レスポンスは、
/area/{地域}/{都道府県}/{市区町村}/{募集案件名}.{案件管理番号}
という URI のリストにして、 この URI を受けとった、コントローラで、
/company/{企業名}/{募集案件名}.{案件管理番号}
に リダイレクトさせる、という方法も考えている。
ここらへんも、どちらが、より、開発しやすく、変更に強いか、という、現場での検討・評価が、まだなので、これから考えていこうと思っている。
後者のリダイレクト方式のほうが、意味的には、わかりやすいかなあ?
今までの内容は、ツリー構造の分類体系を辿るパターン。
実際には、キーワード検索とか、複合条件の絞り込みとか、いろいろでてくる。
まだ、設計が固まっているわけではないけど、ワード検索だったら、
/keyword/{ワード}
とかにしようと思っている。
/keyword/ だけだと、検索キーワードが人気順にでてくる?
あるいは、
/keyword.10/ という URI で、上位の10件を表示、というような方向。
複合条件のほうは、セミコロンを使った、マトリクス指向の URI にしようと思っている。
首都圏の web デザイナーの仕事、月給は、月30万円以上、とかであれば、
/job/zone=首都圏;jobtype=webデザイナー;earnings=月30万円-
という感じ。
最後の ハイフンは、範囲指定の常套(?)手段。 この場合は、下限だけ指定している。
spring 3 MVC のフレームワークがあって、spring エキスパートがいるので、このような URI の解析もさらっと実装してくれる(はず)。
そう思いこんで、私が、勝手に、こういう設計を先走っている。
(アンチパターンですね。危険ですね。設計は、関係者で、しょっちゅう、雑談的に意見交換することがたいせつ)
まあ、最悪、 ここらへんは、query 文字列方式に戻ればよい、という、リスク管理(?)をしながらやっているつもり。
現在の採用支援サイトの企画内容だと、こんな感じの URI 設計で、いけそうな気がしてきているところ。
今日一日で、がーっとやった仕事なので、ちょっと怪しいけど。
(でも、この日のために、前から URI 設計ネタはいろいろ仕込んできたつもり)
あとは、もっと具体的な仕事探しの機能と、実装技術で、この設計方針を確認しながら、成長させていこうと思っている。
今のところ、URI の要素には、積極的に日本語を使いたいと思っている。
「名前でリソースを表現する」には、やはり日本語が一番だから。
エンコーディングの関係で、問題がでるかあな? これも、早めに実験してつぶしておきたいリスク。
SEO 的には、こういう URI 設計、プラス効果があるのかな?
query 文字列方式は不利、という話は聞いたことがあるので、query 文字列なしのこっちの方式のほうが、よさげな気がするが、SEO は、門外漢なので、わかりません。 専門家に一度、相談しようと思っている。
あたかも静的ページがあるかのように、分類ツリーを網羅したサイトマップとかは、分類マスターから、簡単に自動生成できそうだな。
これは、素人考えでは、 SEO 的に意味がある気がする。
ロボットからみたら、大量の、完全に静的なページのツリー構造なので、query 方式よりも、検索の対象にうまくなってくれそうな気がする。
もちろん、これも、 SEO の専門家のアドバイス待ち。
もともと、自分は、データベース屋あがりなので、リソース指向は感覚的にあっている気がする。
CRUD と、 PUT/POST/GET/DELET とかは意味が違うけど、「定義済の少数の操作セット」という思想は、まったく同じ。
ただ、ちょっと前までは、 リソース指向の URI が良いな、と思っていても、実装のことを考えると、ちょっとなあ、と思っていた。
Spring 3 MVC で、REST スタイルがかなり実装しやすくなったのが、大きな転機。
URI をリソース指向でちゃんと設計すれば、それを、素直に、簡単に(?)、実装できそうな気がしている。
まあ、いつものことだけど、自分たちにとっては、初物で、実証済、経験済の技術ではないので、やってみると、いろいろあるとは思う。
でも、なんとなく、今回は、ちょっとしたターニングポイントの臭いがしている。
自分の勘が、どこかで、そういっている。
基本は、「 query 文字列を使わない URI 設計 」のこころみ。
主に参考にした本は、
・Restful Web サービス
・Web を支える技術
実装技術としては、Spring 3 MVC の URI テンプレート機能を使う。 モデリングといいながら、@Controller の @RequestMapping の実装コードを、かなり意識している。
モデルとコードは、コインの表と裏のように結びついているし、距離も近いもの、という主義なので、だいたいこんな感じ。 Enterprise Architect で、 UML 風の絵を描きながら、頭の中は、結構、コードが飛び交っている。
基本の階層
求人案件の基本の分類整理軸は、悩ましい問題。
求人サイトでわりと多いのが、職種分類の階層。
http://example.com/job/{職種の大分類名}/{中分類名}/{小分類名}/{募集案件名}
(以下、http://example.com は省略)
( {パス要素} という記法は、Spring 3 MVC の URI テンプレートの {RequestVariable} の記法を使っている。)
例えば、
/job/クリエィティブ/web関連/webデザイナー/ソーシャルゲームのグラフィックデザイナ
という感じ。
下位のパス要素を記述しない場合は、
/job/ だけなら、職種の大分類の一覧
/job/クリティティブ/ なら、職種の中分類の一覧
...
という感じで、より、特定の仕事リソースに近づいていく。
画面でいえば、仕事リソースは、仕事の詳細表示画面で、そこで表示するパン屑リストが、この URI そのまま、という感じ。
リソースの一意識別
実際には、上記の「ソーシャルゲームのグラフィックデザイナ」の求人案件は、この URI では、一意に識別できない。
同じ職種名の案件が他にもある可能性がある。
かといって、職種名の下に、さらに階層があるかというと、ちょっと違う感じ。
職種名は、求人企業が任意に指定しているので、それが、分類の階層構造の一部というのは、たぶん、モデルとしてまちがい。
いちおう、今の設計案は、
/job/クリエィティブ/web関連/webデザイナー/ソーシャルゲームのグラフィックデザイナ.2996411
というように、最後の要素は、
{募集案件名}.{案件管理番号}
にしようと思っている。
RequestMapping のほうは、 /job/{zone}/{segment}/{category}/{jobtitle}.{id} と記述しておけば、それぞれのパス要素を、その変数名で簡単に取り出せる。
かなり乱暴だけど、
/job/*/*/*/*.{id} というマッチングで、 id だけ変数として、コントローラで処理することもできる。
ここらへの実装上の記法は、まだ、トライアル段階のところがあって、どう記述するか、手探り。
基本的には、いろいろな URI テンプレートののリストを、Spring が、 パターンマッチングをして、「特定できる順」に、一致させてくれるはず。
ただ、パターンを駆使して、解決をフレームワークにまかせる記法は、あいまいだったり、トリッキーで、障害の種だし、また、可読性も悪く、変更の副作用が怖い。
だから、できるだけ、明示的に書くことを重視しようと思っている。
( 変数として未使用でも、 * ではなく、{zone} のように、明示しようということ)
リソースの複数の分類体系
仕事は、データベース上では、実は、求人企業を親として、その下にぶら下がっている。
上記の、職種分類体系で、整理して持ているわけではない。
データベース上の構造をそのまま素直に URI にすると、
/job/{求人企業}/{求人案件名}.{求人番号}
になる。
ここで、第一階層の要素が、/job/ だと、さっきの、職種分類の /job/ と同じになってしまう。
複数のパス階層が並立する場合には、 /job/ だけでは、まずい。
で、考えたのが、
職種分類の階層は、 /job-category/
企業を親にする階層は、/company/
というように、使い分けることにした。
http://jobs.example.com/job-category/{zone}/{segment}
というように job は、サブドメインに持っていった。
仕事を探すサイト、が明確になったので、こっちのほうが、適切。
job-category とか、company とか、検索の切り口を、サブドメインにしてしまう、というのもちょっと考えた。
ただ、サイトとして、job-category.example.com だと、(政府の統計局とかの)、職種分類体系の検索サイトのような意味になりそう。 仕事を探す、というシンプルな目的とは、ちょっとずれる感じ。
company.example.com だと、さらにはっきりして、これだと、仕事探しではなく、企業情報リソース満載のサイト、という感じ。
というわけで、検索の視点の違いを、サブドメインで分けるのは、却下。
( Spring の URI テンプレートで、サブドメインの違いで処理を分けるというは、実装がややこしくなる、という実装面の問題もある)
ナビゲーションモデル
/company/{企業名}/{募集案件名}.{案件管理番号}
これは、データベースの構造のまま。
実際には、求職者向けのサービスとしては、こういう構造では提供していない。
/company/ で、ずらっと、求人企業が並ぶ。そこから、特定企業を選ぶと、
/company/Example株式会社/ というURIがリクエストされ、その企業の求人案件が並ぶ。
という画面は、普通なない。
/industory/{業種}/{企業名}/{募集案件名}.{案件管理番号}
というような感じで、「業種」から、企業を探す、というのがありそう。
あとは、
/area/{地域}/{都道府県}/{市区町村}
という勤務地を軸にした体系もありそう。
ナビゲーションの切り口は、職種分類、業種・企業、勤務地など、複数ある。
URI もそれぞれに応じた、体系にする。
ただし、最後の {募集案件名}.{案件管理番号} は、実体は一つにしておきたい。
同じ仕事(リソース)に、URI (一意識別情報) が複数あるのはおかしいでしょう、という、そもそも論。
実際には、一覧表示画面に、返す 正式(?)のURI は、
/company/{企業名}/{募集案件名}.{案件管理番号}
に統一しようと思っている。
/area/{地域}/{都道府県}/{市区町村}
のリクエストに対するレスポンス(リソース表現)は、 /company/{企業名}/{募集案件名}.{案件管理番号} のリストになる、ということ。
もうひとつの方法として、レスポンスは、
/area/{地域}/{都道府県}/{市区町村}/{募集案件名}.{案件管理番号}
という URI のリストにして、 この URI を受けとった、コントローラで、
/company/{企業名}/{募集案件名}.{案件管理番号}
に リダイレクトさせる、という方法も考えている。
ここらへんも、どちらが、より、開発しやすく、変更に強いか、という、現場での検討・評価が、まだなので、これから考えていこうと思っている。
後者のリダイレクト方式のほうが、意味的には、わかりやすいかなあ?
複合検索条件
今までの内容は、ツリー構造の分類体系を辿るパターン。
実際には、キーワード検索とか、複合条件の絞り込みとか、いろいろでてくる。
まだ、設計が固まっているわけではないけど、ワード検索だったら、
/keyword/{ワード}
とかにしようと思っている。
/keyword/ だけだと、検索キーワードが人気順にでてくる?
あるいは、
/keyword.10/ という URI で、上位の10件を表示、というような方向。
複合条件のほうは、セミコロンを使った、マトリクス指向の URI にしようと思っている。
首都圏の web デザイナーの仕事、月給は、月30万円以上、とかであれば、
/job/zone=首都圏;jobtype=webデザイナー;earnings=月30万円-
という感じ。
最後の ハイフンは、範囲指定の常套(?)手段。 この場合は、下限だけ指定している。
spring 3 MVC のフレームワークがあって、spring エキスパートがいるので、このような URI の解析もさらっと実装してくれる(はず)。
そう思いこんで、私が、勝手に、こういう設計を先走っている。
(アンチパターンですね。危険ですね。設計は、関係者で、しょっちゅう、雑談的に意見交換することがたいせつ)
まあ、最悪、 ここらへんは、query 文字列方式に戻ればよい、という、リスク管理(?)をしながらやっているつもり。
いけそうかな?
現在の採用支援サイトの企画内容だと、こんな感じの URI 設計で、いけそうな気がしてきているところ。
今日一日で、がーっとやった仕事なので、ちょっと怪しいけど。
(でも、この日のために、前から URI 設計ネタはいろいろ仕込んできたつもり)
あとは、もっと具体的な仕事探しの機能と、実装技術で、この設計方針を確認しながら、成長させていこうと思っている。
今のところ、URI の要素には、積極的に日本語を使いたいと思っている。
「名前でリソースを表現する」には、やはり日本語が一番だから。
エンコーディングの関係で、問題がでるかあな? これも、早めに実験してつぶしておきたいリスク。
SEO 的には、こういう URI 設計、プラス効果があるのかな?
query 文字列方式は不利、という話は聞いたことがあるので、query 文字列なしのこっちの方式のほうが、よさげな気がするが、SEO は、門外漢なので、わかりません。 専門家に一度、相談しようと思っている。
あたかも静的ページがあるかのように、分類ツリーを網羅したサイトマップとかは、分類マスターから、簡単に自動生成できそうだな。
これは、素人考えでは、 SEO 的に意味がある気がする。
ロボットからみたら、大量の、完全に静的なページのツリー構造なので、query 方式よりも、検索の対象にうまくなってくれそうな気がする。
もちろん、これも、 SEO の専門家のアドバイス待ち。
リソース指向は面白い
もともと、自分は、データベース屋あがりなので、リソース指向は感覚的にあっている気がする。
CRUD と、 PUT/POST/GET/DELET とかは意味が違うけど、「定義済の少数の操作セット」という思想は、まったく同じ。
ただ、ちょっと前までは、 リソース指向の URI が良いな、と思っていても、実装のことを考えると、ちょっとなあ、と思っていた。
Spring 3 MVC で、REST スタイルがかなり実装しやすくなったのが、大きな転機。
URI をリソース指向でちゃんと設計すれば、それを、素直に、簡単に(?)、実装できそうな気がしている。
まあ、いつものことだけど、自分たちにとっては、初物で、実証済、経験済の技術ではないので、やってみると、いろいろあるとは思う。
でも、なんとなく、今回は、ちょっとしたターニングポイントの臭いがしている。
自分の勘が、どこかで、そういっている。