新人教育用およびふとした時にわからなくなった場合用
Data Transfer Objectとかそういうのどうでもいいから他の人がどう使い分けているのか知りたい人向けです
Form/DTO/Entityとはなんぞや
見た目は似ているが使い道が違う
ざっくり言うと
最悪、これだけわかればOK
Form:ユーザーが入力する(した)値の入れ物
DTO:ユーザーが入力した値やDBから取得した値を、システム用に使いやすくしたもの
Entity:DBから取得した値やDBに保存する値の入れ物
ざくざくっというと
わかんない言葉はググってね
- 全部Java Bean(プライベートなメンバがあってGetter/Setterがあって…)
- キホン的にFormはコントローラーでしか、EntityはDAO(または、DBと直接やりとりをするクラス)でしか使わない
- 全部MVCモデルで言うとM
実際どのように使い分けているのか
- Formは画面・コントローラーでしか、EntityはDAO(DBと直接やりとりをするクラス)でしか使わない
- DTOはDAO以外いろんな場所に出現する
- ユーザー入力がない場合(情報を表示するだけの画面とか)ならFormはいらない
以下、「フロント」って言葉がわかる人は「画面」を「フロント」に読み替えてください
「新規登録画面からの登録」の例
- ユーザーが画面上のFormに情報を入力し、送信ボタンを押す
- 画面が、Formに詰まった情報をコントローラーに送信する
- コントローラーが、Formの内容をチェックしてDTOに変換する
- コントローラーが、DTOをサービスに渡す
- サービスが、ロジックに基づいてあれやこれやした後DTOをEntityに変換する
- サービスが、EntityをDAO(DBと直接やりとりするクラス)に渡す
- DAOがEntityの情報をDBに渡す
「会員情報変更画面の表示」の例
- DAOがDBから取得した情報をEntityに詰める
- DAOがEntityをDTOに変換してサービスに渡す
- サービスがロジックにロジックに基づいてあれやこれやした後DTOをコントローラーに渡す
- コントローラーがDTOの内容をFormに詰めて画面表示する
※4.の「DTOの内容をFormに詰める」はコントローラーじゃなくてHTMLでやる場合もある
「会員情報参照画面の表示」の例
- DAOがDBから取得した情報をEntityに詰める
- DAOがEntityをDTOに変換してサービスに渡す
- サービスがロジックにロジックに基づいてあれやこれやした後DTOをコントローラーに渡す
- コントローラーがDTOの内容を面表示する
※「会員情報変更画面の表示」から「Formに詰める」がなくなっただけ。ユーザー入力がないからFormがいらなくなった。
なんでわざわざ使い分けるの。ひとつでよくない?
「ユーザーに入力してほしいデータ」「システムで使いたいデータ」「DBに保存したいデータ」が異なるからです
逆に言えば
「ユーザーに入力してほしいデータ」「システムで使いたいデータ」「DBに保存したいデータ」が完全一致するシステムなら使い分ける必要はありません
が、その3つが一致することはほぼありません
(2つだけ一致することはまあまあある。とくに「システムで使いたいデータ」「DBに保存したいデータ」は同じ場合があるのでEntityをDTO代わりにしているシステムはよくある)
FormとEntityの使い分け
「年」「月」「日」で入力するテキストボックスを分けている画面を想像してみる(よくありますね)
この場合
まずFormは『ユーザーに入力してもらう値』と一致させるため、birthYear, birthMonth, birthDateの3項目を用意します
birthYearは4桁の数字、birthMonthは12以下、birthDateは……とかいろいろチェックします
しかし、DBには「birthday」という1項目しかないとします
その場合EntityはDate型の「birthday」を定義する必要があります
これでまずFormとEntityは使い分けが必要であるとわかりました
FormとDTOの使い分け
さきほど言った通りFormは「年」「月」「日」に分かれているためbirthYear, birthMonth, birthDateの3項目が必要です
でも実際システム的にはDate型のほうが使いやすいです。
Date型にすることによって「成人か?」などのチェックがやりやすくなるし、「YYYY年MM月DD日」を「YYYY/MM/DD」で表示することも容易です
なので入力値チェックが終わったらさっさとDate型に変換してサービスに渡しましょう
サービスにデータを渡すときにはDTOに変換するんでしたね
よってDTOでも生年月日はDate型の「birthday」を定義しましょう
これでFormとDTOも使い分けが必要であるとわかりました
DTOとEntityの使い分け
生年月日に関して言えばDTOもEntityもDate型のbirthdayで一致するので、別の項目について考えましょう
例えばこのシステムが通販システムで、
登録してから3年以上経過&年間100,000以上お買い物しているユーザーを「太客」と判定し
専用のキャンペーンに誘導したり送料が無料になったり割引が適用されたりするとします
さらに、DBの項目に「太客フラグ」は存在しないとします
専用のキャンペーンに誘導したい時、送料を計算する時、値段を計算する時
毎回DBを呼んで計算することもできますが、そのたびに通信するのは面倒ですよね1
そんなときは最初の一回だけDBを呼んで登録日とお買い物金額を取得&太客かどうかを判定し、
DTOに「太客フラグ」を設定しておきます
後はDTOの太客フラグを参照すれば毎回DBを呼ぶ必要はありません
太客フラグはユーザー入力値ではない&改ざんされたら困るのでFormには設定しないし、DB項目にもないのでEntityにも設定しません
というわけでEntityともFormとも使い分けられます
さらに言うと
絶対改ざんされたくない情報はFormに定義しない
HTMLで<input>タグに載せた情報は改ざんされる恐れがあります type="hidden"にしてもダメです
しかしForm定義していない項目ならHTMLに書いたところで受け取れません
「それならForm定義だけしといてHTMLに書かなければよくない?」甘いな アナタがHTMLに載せなくても誰かがするかもしれないからだ
そもそもFormに定義されていなければ「なんでFormにないんだ? 入力されちゃダメなのか?」って思ってくれる(かもしれない)
おわりに
こんなに書いておいてなんだけど結構プロジェクトによって違ったりするから、実際にはプロジェクトのルールに従ってください
参考
-
毎回DBを呼んで計算するほうが正確だとは思うので、本物の通販サイトなら毎回DB呼んでいるのかもしれません。もうちょっといい例えがあったら教えてください。 ↩