設計
目標
サービス開発では避けて通れない,サービスを『設計』するための知識をつけよう
『設計』って?
みなさんがWebサービスを作るとき,「デザイン」を必ずすると思います.この"デザイン"はWebサービスの"見た目を整える"ということを指します.言い換えれば,Webサービスの"見た目を設計する"とも表現できます(※ 英語の Design には 設計 という意味もある).
この教科書では今までのような見た目のデザインだけでなく,Webサービスの中身のデザインについての知識を深めていきます.
DB設計
データベース(DB)とは
データベースにもたくさんの種類がありますが,ここでは関係データベース(RDB: Relational DataBase)を扱い,便宜上これをデータベースと呼ぶことにします.
[for mentors] なぜデータベースなの?
- 変数に入れっぱなしにしたら?
- 再起動するとデータが消えてしまう(永続化出来ない)
- 大容量になってくるとメモリに入りきらなくなる
- 普通にファイルに保存したら?
- トランザクション がつかえない
- お金振り込んだときに保存する直前でシステムエラーが発生して口座の残高変わらないのに現金持って行かれた… みたいなのを防ぐ仕組み
- 連続したデータ更新処理などが途中で止まった場合に,途中まで出来てたやつを全部なかったことにできる(Atomic実行)
- 自分で実装するのめんどくさくてムリ
- データの競合を防ぐことができる
- 複数箇所から同時に更新されたら?みたいなのをよしなにやってくれる
- トランザクション がつかえない
[for mentors] なぜDB設計が重要なの?
- 一度作ったものが動き出すと,途中から変えづらい
- 保存してるデータ量が大きくなると変更にかなり時間かかったりする
- 設計がまずいと…
- DBアクセスは多くなりやすいので,パフォーマンス低下に繋がる
- あとから機能追加したいときなど,それが困難になる場合がある
データモデリング
データベースの設計をする際,まず最初に「僕たちが住んでいる現実世界」のモノの関係を「コンピュータの世界」で再現しなければなりません.これには下のようなER図(実体関連図: Entity-Relationship Diagram)という図を利用することがあります.
この図からは,例えば以下の様なことがわかります.
- 「メンバーは名前,学年,コースを登録している」
- 「キャンプは会場,住所,期間が決まっている」
- 「メンバーがキャンプに申し込むと申込番号が渡される」
また,上の図の黄・緑・青の四角の内容それぞれには以下のような名前がついています:
- 黄色の「(メンバーの)名前」「(キャンプの)会場」: 属性(Attribute)
- 青や緑の「メンバー」「キャンプ」「参加」: 実体(Entity)
- 「メンバー」「キャンプ」と「参加」のつながり: 関連(Relationship)
- 関連が「この参加登録はこのメンバーのものである」「この参加登録はこのキャンプのことである」といったモノとモノとの関係を表します
他の関連の例としては,以下の様なものが挙げられます.
- 1対1関連
- 1対N関連
- N対N関連
ここから,実際のデータベースを設計します.ActiveRecordの教科書では「データベースはテーブルの集合である」と習いました.先ほどのER図から,「実体 → テーブル」「属性 → テーブルのカラム」といったように当てはめていくと下図のようになります.
しかし,それだけではまだ足りません,関連がテーブル上に表現されていません.そこで,テーブル同士の関連を表すのに外部キー(Foreign Key)というものを利用します.注文テーブルのメンバーIDやキャンプIDがそれにあたります.例えば「参加登録をしたメンバーは誰なのか」を見たいときは,外部キーになっている「メンバーIDをIDに持つ人」を探してきます.このとき,同じIDを持つメンバーが何人もいると誰のことかわからなくなります.外部キーになれるのは,同じ値が存在しないカラムのみになります(「同じ値が存在しない」ということを「ユニークである」「一意である」と表現します).ActiveRecordでは普通,絶対にユニークになるid
というカラムが用意されているため,それを外部キーとして用いることが多いです.
ひとまずこれで,現実世界のモノをデータベースとして表現することが出来ました!
正規化
実は,データベース設計はこれで終わりではありません,ここからさらに正規化と呼ばれる作業を行います.正規化とは,大ざっぱに言うと「データベース中の重複をなくしていく」ことです.
[for mentors] 正規化について補足
正規化を「データベース中の重複をなくしていく」を表現しました.特に第2,第3正規形は関数従属性(FD: Functional Dependency)を排除することを目的とします.
この『第n正規形』については名称まで覚える必要はあまりないです.どんなプロセスを踏もうと,とりあえず第1〜3正規形ぐらいまでを満たすDB設計がなされていれば問題ありません.この第1〜3正規形までを満たす正規形(= 自明な関数従属性がすべて排除された状態)をボイスコッド正規形(BCNF)と呼んだりしますが,これも無理に覚える必要はありません.
第1正規形
まず,メンバーテーブルに注目します.
名前 | 学年 | コース |
---|---|---|
Aさん | 中2 | Webプログラミング |
Bさん | 中1 | Android |
上の例で,Bさんが追加でメディアアートコースにもチャレンジしたとします,どうやってテーブルに保存したらいいでしょうか.
すぐ思いつくのはコース1,コース2というカラム名にすることでしょうか.
名前 | 学年 | コース1 | コース2 |
---|---|---|---|
Aさん | 中2 | Webプログラミング | NULL |
Bさん | 中1 | Android | メディアアート |
しかし,これでは結局3つ目のコースを登録する際に困ってしまいます.また,コース2以降が空っぽになる人が出てくるのは良くありません(NULL
は何もない という意味).このようにカラムに順序がついてしまうのは避けるべきです.
では,コースをカンマ区切りで登録したらどうでしょうか.
名前 | 学年 | コース |
---|---|---|
Aさん | 中2 | Webプログラミング |
Bさん | 中1 | Android, メディアアート |
ところで,コース名からメンバーを検索するときはどうすればいいでしょうか.
ActiveRecordを触ったことがある人なら書けるかもしれませんね.
Member.where(cource: 'Webプログラミング')
しかし,Webプログラミングと一緒に他のコースもやっている人がいる場合はこれだと見つけられません.このように,コース名で検索するときに非常に面倒になるため,あるレコードのあるカラムの中には値が複数個あってはいけません.
じゃあどうすれば良いか,ここでは同じ人を指すレコードをもう一つ追加しましょう.
名前 | 学年 | コース |
---|---|---|
Aさん | 中2 | Webプログラミング |
Bさん | 中1 | Android |
Bさん | 中1 | メディアアート |
- カラムに順序があってはならない
- あるカラムの中には値が複数個あってはならない
などの条件を満たすものを第1正規形といいます.
[for mentor] 1カラムに複数の情報を保存するのは何故ダメなのか
1カラムに複数の情報が入っていると,検索時に毎回カラムの中をパースして条件に合うかどうかを判定する必要が出てくるためクエリが複雑になる.
(繰り返しグループ というらしい…)
[for mentor] SQLとActiveRecord
RDBの検索はSQLというクエリ言語を用いることが多い.ActiveRecordはデータベースの定義(スキーマ定義)からテーブルに対応するクラスを自動で生成し,データベースからのデータの読み出しや書き出しをすべてカプセル化してくれる(= クラスのインスタンスを操作したらデータベースに反映される,検索等もインスタンスに生えてるメソッドから行えるようになる).
/* Member.where(name: 'Webプログラミング') で実行されるSQL */
SELECT * FROM members WHERE cource = 'Webプログラミング';
第2正規形(2NF)
第1正規形のテーブルを見てみると,カンがいい人だとすこし違和感を覚えているかもしれません.
名前 | 学年 | コース |
---|---|---|
Aさん | 中2 | Webプログラミング |
Bさん | 中1 | Android |
Bさん | 中1 | メディアアート |
Bさんの情報が2行にわたって存在していますね.ここで,Bさんが中2に進学したらどうでしょう.2行とも更新しないといけないですが,もし片方でも忘れると大変そうです.
このように,同じ情報が複数回出てきてしまうと色々困ったことが起きそうです.そんなときはコースを別のテーブルにしてしまいましょう.
ID | 名前 | 学年 |
---|---|---|
1 | Aさん | 中2 |
2 | Bさん | 中1 |
メンバーID | コース名 |
---|---|
1 | Webプログラミング |
2 | Android |
2 | メディアアート |
これでメンバーテーブルにあるBさんの情報は1行だけになりました.これが第2正規形です.
第3正規形(3NF)
先ほどコーステーブルを新しく作りました.ここで,あらたにメンバーIDが3の人がAndroidコースに入ったとしましょう.
メンバーID | コース名 |
---|---|
1 | Webプログラミング |
2 | Android |
2 | メディアアート |
3 | Android |
さてココで問題が起きました!正しい名前は「Androidコース」ではなく「Androidアプリ開発コース」だったのです!このままだと変更する場所がたくさんあって面倒だし,更新を忘れるところが出てきてしまうと大変です.
ここで,「コース」テーブルと「メンバーが登録してるコース」テーブルを分けてみましょう.
メンバーID | コースID |
---|---|
1 | 1 |
2 | 2 |
2 | 3 |
3 | 2 |
ID | コース名 |
---|---|
1 | Webプログラミング |
2 | Androidアプリ開発 |
2 | メディアアート |
これでコース名が変わっても変更する箇所が少なくなりました!これを第3正規形といいます.
DB設計完了…?
ひとまず,メンバーテーブルとコーステーブルの正規化は終わりました!
DB設計 まとめ
実際のDB設計はこのようにして進めていきます.慣れるまではちょっとしんどいかもしれませんが,難しく考えすぎずに「あ,ここはデータがダブりそうだからテーブル分けちゃうか〜」みたいな感じでもぜんぜん大丈夫です!(第n正規化という名前も意識しなくてOK)
URI設計
全世界に公開されているWebアプリには,下の写真のようにそれぞれURI(Uniform Resource Identifier)が割り振られています.本章ではこの「WebアプリのURIをどのように決めていくか」について学んでいきます.
URI
URI(Uniform Resource Identifier)は直訳すれば「統一リソース識別子」.リソースに与えられた「世界に一つしかない名前」です.
URIの構文
URIは以下のような部品から出来ています.
http://blog.example.jp/entries/1
-
URIスキーム:
http
- 主にそのURIが利用するプロトコルを表します(プロトコルについてはHTTPの節で説明します)
-
ホスト名:
blog.example.com
- ホスト名はドメイン名もしくはIPアドレス表します
- (IPアドレス: そのコンピュータのインターネット上の住所のようなもの)
-
パス:
/entries/1
- そのコンピュータ上のどこかを表します
上に挙げた以外にもポート番号やクエリパラメータ,URIフラグメントなどと呼ばれるものが含まれることがあります(これらについて詳しく知りたい人は,Googleなどの検索エンジンで「URI ポート番号」「URI クエリパラメータ」のようなワードで検索してみよう!).
.
本章では主にパスの部分に注目し,そこの設計について学んでいきます.
[for mentors] ://
-
:
スキームとその後ろの区切り -
//
その後ろ(ユーザ情報やホスト名)の始まりを示す
[for mentors] URIで使える文字
- アルファベット:
A-Za-z
- 数字:
0-9
- 記号:
-.~:@!$&'()
日本語はそのままだと使用できないため,%エンコーディングという方式でエンコードします.
https://ja.wikipedia.org/wiki/ウサギ
↓ encode
https://ja.wikipedia.org/wiki/%E3%82%A6%E3%82%B5%E3%82%AE
[for mentors] URI,URL, URN
- URI(Uniform Resource Identifier)
- URL(Uniform Resource Locator)
- URN(Uniform Resource Name)
URLはリソースの場所を,URNはリソースの名前を示します.URNの例としては書籍のISBNなどが挙げられます(e.g. urn:isbn:9784774142043
).
このURLとURIを併せてURIと呼びます.
本稿におけるURIはすべてURLと読み替えても問題ありません(『Webを支える技術』に倣っています).
Exercise
最初に紹介した5つのURLをURIスキーム,ホスト名,パスに分割してみよう!
HTTP
HTTP(HyperText Transfer Protocol)はインターネット上でデータを送るための約束ごとです.
私たちがWebサービスを利用するとき,PCやスマートフォン(クライアント)がインターネットを通じてサーバに「このURLのデータを下さい!」というリクエストを送ります.そしてそれを受けたサーバはリクエスト通りのデータをクライアントに送り返します(レスポンス).HTTPとは,このサーバとクライアントがやりとりするリクエストとレスポンスの決まりごとのことを言います.
HTTPでは色々なこと情報をやり取りしますが,ここではHTTPメソッドとステータスコードについて説明します.
[for mentors] HTTPS
HTTPSもプロトコルの一種です.といっても,厳密に言えばHTTPS自体はプロトコルではありません.HTTPSとはSSL/TSLと呼ばれるセキュアに通信を行うためのプロトコルの上でにHTTP通信をおこなうものをいいます.
HTTPメソッド
「メソッド」という単語自体は,Rubyや他のプログラミング言語を学んでいくなかで耳にしたことがあると思います.HTTPにおけるメソッドも同じ意味で,クライアントがサーバに「こんな処理をしてほしい!」ということを伝えるためのものになります.一方,プログラミング言語におけるメソッドはたくさんあるのに対し,HTTPメソッドは全部で7+1個しかありません.しかも,そのうちでよく使うのはほんの僅かです.本節では以下の5つだけについて説明します.
メソッド | 意味 |
---|---|
GET | リソースの取得 |
POST | リソースの作成,データ追加,その他の処理 |
PUT | リソースの更新,作成 |
DELETE | リソースの削除 |
(PATCH) | (リソースの差分更新) |
これらがWebサービスを作るのにあたってよく利用するメソッドになります.HTMLの<a>
要素で作られるリンクはすべてGETになります.また,<form>
で指定できるHTTPメソッドはGETとPOSTしかないため,JavaScriptを利用しない場合,Webブラウザから利用できるメソッドはGET・POSTのみとなります.PUT・DELETE・PATCHの3種はPOSTで代用することが多いです(今までの教科書を振り返ってみよう!).
[for mentors] Ruby on RailsにおけるPUT・DELETE・PATCH
Ruby on Railsを利用したことがある人は,「<form>
でもPATCHとか出来てたしlink_to
(<a>
)からでもDELETE出来てたじゃん!」と思うかもしれません.実は,Ruby on Railsではjquery-ujsというJavaScriptライブラリは読み込まれており,POSTとGET以外のメソッドはこれにより実現されています.
[for mentors] その他のHTTPメソッド
HTTP 1.1におけるメソッドはRFC7231に定義されている以下の7つです.
- GET
- HEAD
- POST
- OPTION
- PUT
- DELETE
- TRACE
また,PATCH・LINK・UNLINKに関してはHTTP 1.1には含まれていません(RFC2068時点では存在してたらしいけど消えたっぽい?PATCHは後から復活した?詳細は知りません…).
ステータスコード
「ステータスコード」という単語を初めて聞く人でも,Webサービスで「404」や「500」といった数字を見たことがある人も多いかもしれません.ステータスコードは,HTTPレスポンスに含まれる3桁の数字です.リクエストがどんな結果になったかを簡単に表してくれています.
ステータスコードは,先頭の数字で結果が大ざっぱにどんな感じかを教えてくれます.
- 1xx: 処理中
- 2xx: 成功
- 3xx: リダイレクト
- 4xx: クライアントエラー
- 5xx: サーバエラー
ステータスコードはたくさん存在しますが,ここではよく使う一部のコードを紹介します.
ステータスコード | 意味 |
---|---|
200 OK | リクエスト成功 |
201 Created | 新しくリソースが作成された |
400 Bad Request | リクエストが間違っている |
401 Unauthorized | 認証に失敗した |
404 Not Found | リソースが見つからない |
500 Internal Server Error | サーバでエラーが発生した |
時々見かける「404」はページが見つからない,「500」はサーバ側でなんらかのエラーが発生したことを表していたのでした.
このステータスコードは,クライアント側に処理がどうなったかを伝える大切なものです,普通にWebサービス作ってる段階ではあまり意識しませんが,自分でサービスのWeb APIを公開するときなどには非常に重要です.
REST
RESTとはWebのURI設計でよく用いられるパターンのようなものです.Ruby on Railsなどは,URIの設計が基本的にはRESTに従うように作られています.実際にRESTとは何かについて触れる前に,RESTにおける重要な考え方であるリソースについて学びましょう.
リソース
リソース(Resource)はそのまま日本語に訳すと「資源」という意味です.Webにおけるリソースとは例えば以下のようなものです.
- Life is Tech ! のキャンプ情報
- Cookpadに載っているおいしいうどんのレシピ
- Amazonの商品情報
上の3つはすべてリソースです,リソースとは「Web上に存在し,名前を持つすべての情報」のことです.そして,ここでいう「名前」とは先ほど説明した「URI」のことです.
RESTは,リソースの操作をするときにどのHTTPメソッドを使ってURIにアクセスするか,ということを統一するものというのが大きな特徴です.また,RESTの思想に従って作られていることをRESTfulであると言ったりします.
リソース設計
RESTは「リソースを操作するメソッドを統一」するものであると説明しました,ということは,そのリソースを上手に設計することが非常に重要になります.リソースの設計は次のようなステップで行われます(『RESTful Webサービス』より).
- Webサービスで提供するデータを決める
- データをリソースに分ける
- 各リソースにURIで名前をつける
「Webサービスで提供するデータを決める」というのは,実はDB設計のときに近いことをやっています.その時の例を使ったリソース設計の例を見ていきましょう.
例:キャンプメンバー一覧サービスを作る
DB設計の章では以下のようなテーブルを決めました.これを元に,実際に提供するデータを決めていきましょう.
ここでは以下のようなサービスを作りたいとします.
それでは,上記のサービスで提供したいデータ(リソース)とはなんでしょうか.考えてみましょう.
ここでは次のようなリソースであると考えてみます.
- キャンプリソース
- 1つのキャンプに対応するリソース
- メンバーリソース
- キャンプごとの参加しているメンバーに対応するリソース
次に,これらに名前(URI)をつけていきます.
まずキャンプリソースですが,「キャンプの一覧」と「キャンプの詳細」の2パターンあります.キャンプ一覧はキャンプ(camp)を複数個取ってくるので,/camps
という名前がいいでしょう.キャンプ詳細は,簡単にするために各キャンプにcamp_id
という名前のIDをつけましょう./camps
の後ろにつなげる形で/camps/:camp_id
という名前にすれば各キャンプごとにユニークな名前がつけられそうですね.また,どちらもリソースを取得するためのものなのでGETメソッドを利用します.
- GET
/camps
- GET
/camps/:camp_id
-
:camp_id
にキャンプのIDが入る - e.g.
/camps/1
,/camps/2
-
続いてメンバーリソースです.今回は各キャンプごとに参加しているメンバーが一覧がほしいので,キャンプ詳細の後ろにmembers
とつなげればわかりやすそうですね.
- GET
/camps/:camp_id/members
- e.g.
/camps/1/members
,/camps/2/members
- e.g.
これでリソースから各ページのURIを決定することが出来ました!
Exercise
次のページについて,リソースとURIを考えてみよう!
- コース一覧
- メンバー一覧
- メンバー詳細
- そのメンバーが受講したことのあるコース一覧
URI設計 まとめ
RESTは数多くある考え方のうちの1つなので,これが絶対正義というわけではありません.しかし,RESTに従ってURIやメソッドを決定していくことで分かりやすいWebサービスの近道になることは間違いないでしょう.こちらもDB設計同様,最初は難しいかもしれませんが慣れてくると簡単なので心配しなくても大丈夫!
また,URIに関しては実際のWebサービスを参考にしてみると勉強になります.特にQiita,Wantedly,GitHubなどはRuby on Railsで作られたサービスであり,かなりRESTfulに近い設計になっているので非常におすすめです!
Cooking Time
マイクロブログサービスを設計しよう
-
users
,articles
のテーブル設計 -
User
,Article
のModel実装 - URI設計(タイムライン,ユーザプロフィール)
- 画面の実装(タイムライン,ユーザプロフィール)
Skill Check
- Basic
- お気に入り機能を設計しよう
- お気に入り機能を実装しよう
- Advance
- 返信機能 もしくは フォロー機能 を設計しよう
- 1.で設計した機能を実装しよう
[for mentors] 参考文献
書籍
- 松信嘉範,羽生 章洋,ミックほか: データベース徹底攻略 (WEB+DB PRESS plus),技術評論社(2014)
- 実践的な例を出して解説してくれてわかりやすいかも
- 奥野幹也: 理論から学ぶデータベース実践入門 ~リレーショナルモデルによる効率的なSQL (WEB+DB PRESS plus),技術評論社(2015)
- ほんとに理論から学ぶことになるので難しめ
- 増永良文: *リレーショナルデータベース入門―データモデル・SQL・管理システム (Information&Computing) *,サイエンス社(2003)
- 「The 教科書」 ,初心者が入門のために1人で読むとしんどいかも
- いい意味でも「教科書」で,この本以上に丁寧な資料はないかもしれないレベル
- 山本陽平: Webを支える技術 -HTTP、URI、HTML、そしてREST (WEB+DB PRESS plus),技術評論社(2010)
- Webの歴史やHTTPの基礎的なことからRESTアーキテクチャ・URI設計まで幅広く網羅されている
- Web Programmer/Web Engineerになりたいなら必読!
- 水野貴明: Web API: The Good Parts,オライリージャパン(2014)
- 様々なサービスのAPIの例が載ってて面白い
上記のリストに上がっている本は比較的難解な物が多いです….上記以外だと,基本/応用情報技術者試験のテキストがDBの基本を抑えつつ簡潔にまとまっていることが多いです!(※ 問題集ではなく教科書スタイルのやつ)
参考になる事例
QiitaとGitHubのAPIは「RalisでWebアプリのREST API作りました!」のお手本みたいな感じになってるので参考になります.スマホアプリ向けAPIとか作りたい人はWeb API: The Good Partsと併せて読んでみよう!