Domain Driven Design Quickly Online を読んでいく。 その4
Domain Driven Design Quickly Online を読んでいく。 その1 - 僕とコードとブルーハワイ
Domain Driven Design Quickly Online を読んでいく。 その2 - 僕とコードとブルーハワイ
Domain Driven Design Quickly Online を読んでいく。 その3 - 僕とコードとブルーハワイ
の続き。
読んでいる本
www.infoq.com
今回は「第4章 リファクタリングのためのさらに深い洞察」をまとめる。
継続的なリファクタリング
前回は、より良いモデルの作るための姿勢やパターンをまとめた。
今回は、リファクタリングのお話。
リファクタリングとは、アプリケーション動作を変えずにコードの再設計をして改善する作業。
コードのリファクタリングは大事だが、設計段階でのリファクタリングはもっと重要。
設計はモデルの本質を作る作業なので、ドメインとモデルの関係を整理できれば、コードを読むだけでモデルの全体を把握できたりドメインの存在理由がわかるようになる。それに、設計内容は結局コードにも影響してくるため、設計段階での深い考察が重要となってくる。
鍵となる概念を明らかにする
リファクタリングは小さな改善の積み重ね。
ドメインについての深い知識に基づいて、新しい概念や抽象を付け加え、設計の考察を繰り返してより深いモデルにしていく。この作業が小さな改善となる。
これを積み重ねていくと、あるときブレイクスルーが起こり、モデルの見方が変わる。
潜在する新しい概念を見つけるコツ:
- モデリングや設計時に使われる言葉に耳を傾ける、つまりユビキタス言語の構築作業をする。構築されたユビキタス言語から概念をあぶり出す。
- 設計が曖昧な部分を見つけて、足りない概念がないかを探して、設計に反映してみる。
- ドメイン構築の際に発生したドメインに対する主張の矛盾を解消する。矛盾の原因は、同じものを別の視点から見ていたり単なる説明不足という場合がある。これを解消することて重要な概念の発見につながるかもしれない。
- 書籍からドメインについて学ぶ
明確になっていると役に立つ概念:
- 制約
- 制約とは不変性を表す方法
- もしある概念に制約入れてそれをコードとして書く場合、概念の表現と制約の評価は別のメソッドにしておく方が良い。制約の評価部分のコードを外出しして、概念側ではそのコードを呼び出すという構成。こうすると、制約が明確に表現できるし、制約が増えてもロジックの組み込みが簡単だよねという話。
- プロセス
Domain Driven Design Quickly Online を読んでいく。 その3
Domain Driven Design Quickly Online を読んでいく。 その1 - 僕とコードとブルーハワイ
Domain Driven Design Quickly Online を読んでいく。 その2 - 僕とコードとブルーハワイ
の続き。
読んでいる本
www.infoq.com
今回は第三章をまとめる。
前章の振り返り
モデル駆動設計
モデルをコードとして実装してく作業で重要なのは、簡単かつ正確にコードへ落とし込めるモデルの選定。おすすめの設計手法に「分析モデル」と呼ばれるものがある。分析モデルとは、雑にいうと業務ドメインを分析した結果。
分析モデルを作り上げていく上で発生しうる問題:
- アナリストが予見できなかった問題がモデルに含まれていた場合、開発者はコードへ落とし込むときに独自の決断をして、設計されたモデルとかけ離れた実装を余儀なくされることがあるかもしれない。
- モデルとコードの乖離が広がる・モデルの情報が欠損する
より良いモデル作成のためのポイント:
- アナリストとソフトウェア開発者は積極的にそれぞれが持つドメインの知識を共有していく必要がある。開発者もモデリング段階から参加して、ソフトウェアを正確に表現するモデルを作っていこう。
- 実装とモデルを強く結びつける。
- コードの変更=モデルの変更を自覚すること。
- ドメインモデルから設計に使用される用語抜き出し、モデルの要素に責務を割り当てること
結局のところアナリスト、ソフトウェア開発者の持つドメインの知識を共有する場がないと良いモデルを作れないしモデルをうまくコードへ落とし込めないので、ドメインからモデルが作られていく過程をちゃんと共有する仕組みを作りましょう、という話。
レイヤーアーキテクチャ
ビジネスロジックが他のレイヤに混ざるとそのレイヤの変更に引っ張られる形でビジネスロジックが変異してしまったり、全体的な修正を強いられるので大変。
なので、レイヤ分割し、下位のレイヤだけに依存するレイヤの設計が求められる。
つまり、UI,アプリケーション、ドメイン、インフラなどの各レイヤの責務を明確にまとめることで、変更の管理をしやすくしていこうぜ、という話。
エンティティ
エンティティとは、雑にいうと一意性。現実の例で言うと、マイナンバーがエンティティにあたる。DB主キーもそう。ドメインモデルにおいて重要なオブジェクト。
エンティティの重要性としては、以下の一文から読み取れる。
重要なのは、システムが一意性の異なるオブジェクトを簡単に区別できることであり、
一意性を保証する属性や振る舞いが同じであれば、そのオブジェクト同士が同
一だと判別できることです。そうでないと、システム全体が台無しになるかも
しれません。
バリューオブジェクト
オブジェクトをエンティティとして作成する場合の注意点として、あるオブジェクトのインスタンスが大量に作成されるとシステムのパフォーマンス低下になるというのが挙げられる。
本書の例だと、Customerオブジェクトは再利用できないため、顧客がいる分だけインスタンスが生成されますよね、とある。
ある属性を含むオブジェクトを識別することよりも、どのような属性を含むかに関心がある場合は、ドメインのひとつの側面を表現するためのオブジェクトであるため、一意性の保証は必要ない。
このようなオブジェクトをバリューオブジェクトとよぶ。
エンティティを見つけ、そのほかをバリューオブジェクトと定義すると設計は単純になる。
バリューオブジェクトは共有可能なら不変でなければならない。他のオブジェクトが必要とする場合は、値だけを渡すか、バリューオブジェクトをコピーしたものを渡すこと。
なぜ不変でなければならないかという話は、以下の説明で納得した(というか当たり前のことでした)。
変更できるオブジェクトを共有するとどうなるのか想像してみましょう。
例えば、航空予約システムではそれぞれのフライトを表すオブジェクトを作成します。ある顧客がある目的地へのフライトを予約したとします。
もうひとりの顧客が同じフライトを予約しようとします。このときシステムはフライトコードを保持するオブジェクトを再利用します。同じフライトを予約しようとしたからです。
そうしているうちに、顧客は考えを変えて、違うフライトを予約します。このときシステムはフライトコードを変更してしまいます。変更ができるオブジェクトだからです。
その結果、最初の顧客のフライトコードまで一緒に変更されてしまいます。
サービス
動詞は適切な名詞と関連し、オブジェクトの振るまいの一部になる。
オブジェクト指向言語ではドメインに属する振る舞いでも何らかのオブジェクトに属さなければならない。振る舞いは必ずなんらかのオブジェクトに属する。
でも、エンティティやバリューオブジェクトに含めるには的確ではないような、振る舞いがいくつかある。
こういったものをサービスと呼ぶ。
サービスの特徴:
1. サービスとして作成される操作はドメインの概念をあらわしているが、エ
ンティティやバリューオブジェクトに含めると違和感がある。
2. サービスとして作成される操作はドメイン内の他のオブジェクトから参照
される。
3. サービスとして作成される操作は状態をもたない。サービスは独立したインターフェースとしてモデルに追加し、ドメインモデルの言語を使って定義し、処理の名前もユビキタス言語に含まれなければなりません。
サービスは内部に状態を保持せず、単純に機能だけを提供するものというイメージ。
個人的には、サービスは特定のエンティティやバリューオブジェクトのための機能をまとめるもの、という説明が一番しっくりきた。
モジュール
モジュールは雑にいうとドメインの集まり。
モジュールの各部分が同じデータを操作する場合は通信的凝集になり、モジュール全体にまたがってひとつのうまく定義されたタスクを実行する場合は機能的凝集になる。
ポイント:
- モジュール同士は疎結合にする。
- モジュールへはインターフェースにアクセスさせるようにする。
- モジュール名はユビキタス言語からつける、ドメイン内部に対する洞察を反映するため。
- モジュールの役割は固定、中身は柔軟に。すると設計ミスが見つかった時に修正しやすくなる。
ドメインオブジェクトのライフサイクルを管理するための3つのパターン
アグリゲート
オブジェクトの所有権と境界を定義するのに使うパターン。
特徴:
- 関連するオブジェクトの集まり(エンティティとバリューオブジェクト)で、データの変更について一括して扱うことができる。
- アグリゲートの外部からアクセスできる唯一のオブジェクトはひとつのルート(エンティティ)だけ。
- アグリゲート内部のオブジェクトは互いに参照を持つことができるが、外部のオブジェクトはルートの参照しか保持できない。
- ルートが削除されメモリ上からも取り除かれた場合、アグリゲート内の他のオブジェクトも削除される。
つまり、アグリゲートという壁を作ってやって、アクセスできるオブジェクトをルートだけに制限することで、不用意なオブジェクトの操作を防ぐ(不変性の維持)、という感じ。
ていうか関連オブジェクト群のカプセル化。
データベースのトランザクションはデータの完全性を保証する上でとても重要な役割を果たしますが、もっと望ましいのはデータの一貫性についての問題をモデル内で直接解決することです。
ファクトリ
ファクトリとは、オブジェクト作成のための知識(というか必要な操作群)のカプセル化をするパターンのこと。やってることは、factory_boyのイメージと同じかなぁ。
特徴:
- アグリゲートに含まれるすべてのオブジェクトを作成するファクトリをつくることで、不変性を保つ。
- バリューオブジェクトはあとから変更できないのでファクトリ適用時にすべての属性を有効な値に初期化する必要がある。適切に生成できない場合は例外を発生させて無効な値が返らないようにすること。
リポジトリ
まず背景となる問題について。
アグリゲートのルートへバリューオブジェクトのアクセスを要求すると、クライアント側でルートの参照を保持しないといけない。オブジェクトはもともと保持しなくてもいいかもしれないオブジェクトへの参照を保持しなければならず、オブジェクト間の結合が強くなる。
バリューオブジェクトならエンティティの関連をたどりDBから直接アクセスできるけど設計に悪影響を及ぼす。
以下のような悪影響がある:
- ドメイン全体にデータベースへアクセスするコードがまき散らされ、ドメインモデルが汚くなる。
- 本来はドメインの概念だけを扱えばいいのに、インフラストラクチャに対する細かな処理をしないといけなくなる。
- DBの変更をした場合、それにひきづられるようにドメインモデル内にばらまかれたデータアクセスのコードを全て修正することになる。(あー、身に覚えが...)
- DBに直接アクセスしてオブジェクトを取得すると、クライアントは取得したオブジェクトをアグリゲート内に戻せる。つまり、アグリゲートのカプセル化が破綻する。
- ドメインにあるはずのロジックがクライアント側のコードに浸透すると、ドメインレイヤが薄くなり、ドメインレイヤとドメインモデルの関連性がなくなる。ドメインモデルとはなんだったのか、という感じになる。
上記解決方法:
ThinkPad X220i メモリ増設した
Chromeでタブ開きまくったりメモリを結構使うようなコード書きたいなーとなると4Gじゃ足りなかったので8Gに増設した。
メモリ選定
まずはX220iの仕様を確認する。
以下の仕様書によると「DDR3(動作クロック 1333MHz)」とのことなので、これに合ったメモリを探す。
ちなみに、最近のマシンだとクロック数が多少合わなくても使えるものがあるらしいと教わった。どういう制御をしているのだろう。
今回は値段、性能、手元に届く速さからAmazonで以下のメモリを買った。
CFD販売 ノートPC用メモリ PC-10600(DDR3-1333) 4GB×2枚 204pin SO-DIMM (無期限保証)(Panramシリーズ) W3N1333PS-4G
5000円未満で買えるんだよね。これで4G増えるなんて、安いなぁ...。
メモリの脱着
ついき
ThinkPadをひっくり返したら、ネジを外してメモリを入れ替えるだけ。
メモリを押さえている銀色ホルダーを外側に広げて外すとメモリが斜めになった状態で上にひょこっとでてきて取れやすくなる。
メモリを差し替えたら、ホルダーに収まるようにしまってあげるだけ。
起動して、ちゃんとメモリが増えてることを確認した。
7.7Gって書いてあるけど、残りの0.3Gはどこへ行ってしまったんだろう。MacとかWindowsだときっちり8Gと表示されてた気がするんだけれど。OSで少し食ってる部分を差し引かれてるのかな。
起動直後にメモリの使用状態を見てみた様子。
710Mくらい使ってる。こっちがOSで食ってる部分なのかなー。psで詳細調べるとかしないとわからなさそう。Linux のメモリー管理について詳しくないので勉強が必要だ。
追記
くろはこ先生から「なぜ8GBを一枚ではなく4GBを二枚買ったのか」という質問が飛んで来たのでここにも書いておくと、デュアルチャネルで動作させてちょっとでも処理速度上がればいいなーと思ったからでした。多分そんなに大差無いと思うけども。
8GB+2GBで10GBでも動くらしいのですが、もう一度仕様書を見返してみたところ以下のことが書いてありました。
主記憶(RAM)容量(標準/最大) ※5 2GB(2GB*1) (PC3-10600 DDR3 SDRAM)/8GB
※5 メモリーを最大まで拡張されるには、5GBメモリーを2枚装着する必要があります。本体、あるいはOSがサポートする最大容量を超えての仕様はできません。
最大容量が8GBなのは確認してたけど※5のところは見てなかったよ...。あぶないところだったぜ。
Lenovo ThinkPad X220i に Ubuntu16.04 LTS Desktop を入れた
最近は個人ではMacbook Proを使用していたのだけど、せっかくそこそこ良いThink Padを放置するのも宝の持ち腐れだと思い、Ubintu16.04 LTS Desktop を入れることにした。
中にはWindows7が入っていたのだけど、正直使わないのでまるっと削除してすべてをUbuntu色に染めた。
以下、使えるようにするまでの手順のメモ。
UbuntuのライブメディアをUSBで作成する
用意するもの:PC, フォーマットしてもいいUSB (2Gあれば十分)
- PCにUSBを挿しておく
- Ubuntu 16.04 LTS 日本語 Remix リリース | Ubuntu Japanese Team からISOファイルをダウンロードしておく
- Universal USB Installerをダウンロードする
- Universal USB Installerを起動して、Linux Distribusionをubuntu、.isoファイルを先ほどダウンロードしたISOファイル、USB Flash Device をPCに挿しているUSBデバイスに選択してcreateを押す。
- create を押すとフォーマットされてubuntuのライブメディアが生成されるのでしばらく待つ。
Ubuntuのインストール
日本語の設定
デフォルトだと日本語が使えなかったので設定変更する必要があった。幸いubuntuのセットアップ時にmozcなど日本語入力に必要なドライバが入ってきていたので、そのあたりは苦労しなかった。
- desktop右上にあるキーボードアイコン -> 設定 -> 入力メソッドの設定 へ飛ぶ
- 以下画像左下にあるプラスボタンを押したあと、入力メソッドを検索できるフォームにmozcと入力する
- mozcを入力メソッドにしてメソッド追加したら、最初から入ってた入力メソッド「キーボード - 日本語」よりも上に持っていく
- PC再起動する。すると英字/日本語の切り替えができて日本語入力可能になっている
Domain Driven Design Quickly Online を読んでいく。 その2
Domain Driven Design Quickly Online を読んでいく。 その1 - 僕とコードとブルーハワイ
の続き。
読んでいる本
www.infoq.com
ユビキタス言語
ユビキタス言語とは、ドメインの専門家とソフトウェアの専門家の両者が正しくドメインを理解/表現するための共通言語のこと、と捉えた。
例えば、ドメインモデルの作成の過程で、ドメインの専門家は自己の専門範囲である業務の専門用語を使い、ソフトウェアの専門はプログラミング脳でドメインを捉えてたり技術的な専門用語などで説明してしまったりする場合、両者の間で共通の認識を得ないまま、ドメインモデルを構築してしまう。
そうなってしまうと、
ドメインについての正しい表現は記録されず適切でない表現の中に埋もれてしまったり、
相手がわかるように簡単な(と思っている)言葉で説明することに消耗したり、
専門分野を理解しなければいけないがためにまた別の今回限りな専門用語を生み出してしまったり
する。
ドメインモデルの構築、そして設計が困難になってしまう。
これを回避するために、ユビキタス言語(共通言語)を使っていこうね、というお話。
では、ユビキタス言語(共通言語)はどのように構築するか。
監視システムの例を読むと、
ユビキタス言語というものはこれだ!と言って名付けるものではなく、ドメインモデルを構築していく過程のお互いの会話から発せられた、ある程度まとまった概念を持つ単語の持つ意味の認識をすり合わせていく、という作業に考えられた。
そして、すり合わせをした結果できた概念(ユビキタス言語で表現できるもの)をドメインモデルに反映してみて、齟齬をなくしていく、というのが理想なのだろう。
ちなみにユビキタス言語を構築することで得られるコードを書くときの恩恵はこの文章に集約されていた。
ドメインモデルの概念をクラスへと実装することで、ドメインモデルとコードを対応づけることができます。そして、それは共通言語とコードを対応づけることでもあります。しっかり対応づけられていると、コードが読みやすくなり、コードからドメインモデルを再作成できるようになるのでとても役に立ちます。
ふむふむ。
今の仕事でユビキタス言語っぽいものがwikiにまとまっていたりしていて、コードにもそれらが反映されているんだなーというのがなんとなくわかって、それって仕様把握に一役買っていて私は結構助かった記憶がある。
ただ、この本にも書いてあったけどユビキタス言語はどんどん変化していくものだからwikiと同期しないといけないよねという話がある。
それを怠ると、なんでこの名称になったんだっけどか、この部分何に使ってんの?もういらないやつだっけ?とかなったりして、チャットのログやチケットから経緯を追ったりしないといけなくて、結構辛くなってしまうんだよなこれが。(†戒め†
では、ユビキタス言語はどう表現すればよいのか。
UMLは、クラスやその属性、クラス間の関係を表現するのが得意です。 しかし、クラスの振る舞いや制約を表現するのは簡単ではありません。そのた め、UMLでは図のなかにメモを入れて文章で補足します。したがって、UML ではモデルについての2つの重要な側面、つまりモデルがあらわす概念の意味 とオブジェクトがするべきことを伝えられません。
UMLは全体像を把握するには良いけど、実際のところどういう働きをするの?って部分を表現するのは不得意だよねーという話。
では、「モデルがあらわす概念の意味とオブジェクト」をどう表現したら良いかというと、
この本では、モデルをあらわすクラスたちの関係やその説明文や画像などを図に起こすといいよ、と書いてある。
雑にいうと、各ドメインについて理解できるように、膨大なクラス群を適切なモデル単位の概念に集約・区切っていこう、という話だと思われる。
すなわち、ソフトウエアアーキテクト、開発者、ドメインの専門家を含む設計チームに必要なのは、作業を統一し、モデルの作成とコーディングを助ける共通言語だということです。
上記の言葉で第2章は締め括られている。
しかし、ユビキタス言語の構築は思っているより難しいところだなぁと感じる。
今日はここまで。
Django1.9 admin get_urlsのoverrideでハマってた
get_urlsで正しいっぽいurlpattern渡してるのに、なんでそんなパターン見つからないって怒られるんじゃーとなって先輩とデバッグしてたら、1.9から追加されたという以下のコードに遭遇した。
urlpatterns = [
url(r'^$', wrap(self.changelist_view), name='%s_%s_changelist' % info),
:
# For backwards compatibility (was the change url before 1.9)
url(r'^(.+)/$', wrap(RedirectView.as_view(
pattern_name='%s:%s_%s_change' % ((self.admin_site.name,) + info)
))),
]
django.contrib.admin.options | Django documentation | Django
つまり、カスタムURLが先に来るように繋げないと
def get_urls(self): from django.conf.urls import url urlpatterns = super().get_urls() urlpatterns = [ url(r'^somepattern/$', self.admin_site.admin_view(self.my_view), name=''), ] + urlpatterns # ここのリストに入れる順番が今回の問題 return urlpatterns def my_view(self, request): return TemplateResponse(request, template , {})
1.9にバージョンアップするときは注意が必要だなぁーと思った。
というかまずよくコード読めという話でした、先輩すみませんでした。。
Domain Driven Design Quickly Online を読んでいく。 その1
先に分厚いDDD本を読んでいたが、読み進めていてもどうも霧の中を歩いている感覚から抜け出せずにいるということで、まずは友人の勧めにより「Domain Driven Design Quickly」を読むこととし、そのメモを書いていく。
イントロ〜第1章 ドメイン駆動設計とは何か
ソフトウエアを開発する目的は、実世界の作業を自動化したり、ビジネス上の 問題を解決することです。つまりソフトウエアは、業務の自動化や現実世界の問題など、具体的なドメインのためのソフトウエアなのです。
ドメインとは、実世界で解決したい問題、要求の解決方の枠組みの集合またはそれ自体、ということだろうか。
もう少し雑にいうと、ドメインとはつまりソフトウェアが行う業務の範囲?内容?指していると思われる。
ドメイン駆動設計とは、ソフトウェア設計手法の一つ。
ドメインを明らかにするためには、ドメインを詳細に把握している人物からヒアリングするべきである。例えば、銀行業務システムであれば、業務流れや潜在的な問題を熟知している銀行員である(担当者および依頼者が問題を認識していない場合もあるけど)。
そうしてドメインを探り出していきつつ、ドメインの抽象化をしていく。
ドメインの抽象化とは、ドメインのモデル化のこと。
ドメインは多くの情報を含み、かつ複雑なものであるため、通常は全てをモデルに落とし込むことはできないと考えて良い。この取捨選択が設計作業であり、ソフトウェア作成でもある。
なお、ドメインモデルについては本文中にあったこの一文がわかりやすかった。
ドメインモデルとは特定の図で表されるものではなく、そのような図が表そうとする概念である。
ドメインの知識を構築する
次に、飛行機の飛行制御システム開発を例にドメイン設計を考える。
さらに議論すると、あなたは興味深い単語を聞きます。それ は「航路」です。あなたがこの単語にすぐに着目したのはそれなりの理由があ ります。航路は飛行行程の重要な概念を含んでいるからです。航路にしたがう。 これが飛行機が飛行中におこなうことです。飛行機の出発地と目的地は航路の 始点と終点でもあることが明らかになります。したがって、飛行機を出発地と 目的地に関連させるよりも、航路と関連させた方が自然な気がします。この場 合、航路は出発地と発着地に関連します。
飛行機の航路、つまり問題(飛行機)の遷移/あるいは状態に着目するというのが一つのポイントだと思われる。
例では、飛行機を出発地と目的地に関連させるよりも、飛行機ー航路ー出発地/目的地と関連付ける方が自然、とある。構成する出発地/目的地というものを抽象的に考えて飛行機と関連付ける見方ができるかどうかが第一歩。
抽象化なので、考えているとプログラムでいうクラスのようなイメージを持つ。
私たちソフトウエアの専門 家(アーキテクトと開発者)とドメインの専門家はドメインモデルを一緒に作成します。ドメインモデルはこの2つの専門領域が出会う場所でもあるあります。これは多くの時間を費やさなければならない作業に思えるでしょう。そして、実際その通りです。そうするべきです。なぜなら、ソフトウエアの最終的な目的は実際の生きたドメインでの業務上の問題を解決することであり、そのためにはソフトウエアはドメインと完璧に一体になる必要があるのですから。
アーキテクトと開発者とドメインエキスパートが揃ってドメインの抽象化を行い、ソフトウェアが本来行うべき業務のための設計をしていく、という流れなのだろう。
大雑把だが、DDDの概念は把握した。
今日はここまで。