ITと哲学と

IT系エンジニアによる技術と哲学のお話。

コードネーム U.N.C.L.E見ました

コードネームU.N.C.L.E見ました。

あ、テレビ版は見たことないです。

結構面白かった!

一番良かったシーンはソロがワインを飲みながらボートを眺めてるあのシーンですねw 船の光がカメラの方に向いて強まるたびに、笑ってしまいました。

しかも裏で流れている音楽が謎にガラスの部屋

歌詞の意味を調べてみましたが「月は私たちと共に連れ添っていた」とかあって、月が出てたかもしれないなあのシーンそういえばとか思いました。

まあ歌詞はいなくなってしまった恋人への歌って感じなんですけどね。

こういう印象に残るあのシーンみたいなのがあるとやっぱりいいなーと思います。 ただ、そういう意味ではキングスマンの威風堂々のあのシーンには少し劣るかなーと思ってしまったのが残念。

まあでもキングスマンでもそうですけど、やっぱりスパイはスーツがいいですねー。 スマートな感じというか理知的な感じが出てていいです。まあガンガン銃打ったりするんですけどね。

ありがちな裏切りの例の展開も後から考えると伏線というかなんというかが貼られてていい感じです。 ネタバレになるので説明できませんが、普通そこまで××!って思ってたところが解決しました。なるほどそりゃそうだわ、と。

ソロとイリヤのキャラが正反対に立っていたのでそれも良かったです。 ソロはスーツでプレイボーイ、イリヤはカジュアルでチェリー感。 細かい技術に得意不得意があって、装備の質に差があったりして。 でも同じ目的のために力を合わせて戦って、イリヤは過去のトラウマに打ち勝って、最後には2人で酒を飲み交わすっていう。

あ、最後の展開でチームが結成されたわけですが次回作もあるんですかねー あったらいいなーと思います。あのチームの活躍をもっと見てみたい。

スパイ映画は今年3本目できっと来週あたり4本目のスペクターも見ると思うので楽しみです。

キングスマン(字幕版)

キングスマン(字幕版)

デザインパターンと共に学ぶオブジェクト指向のこころ 第6章

Facadeパターン

Facadeとは見せかけという意味の単語です。 このパターンは複雑な既存システムを自分たちのプロダクトとうまく連携させるための方法を示しています。

複雑な既存システムを簡潔に呼び出せるようなインタフェースを用意するというパターンです。

こうしておくことでクライアント側は簡潔にシステムを呼び出せるようになります。

また、カプセル化によって既存システムのアップデートの際に影響を受ける範囲を限定的にできます。

Facadeパターンを使うと、既存システムに外から新たな機能を擬似的に持たせることもできます。

例えば既存システムの呼び出し回数を記録するような機能を追加したいとした場合、すべてのシステム呼び出しをFacade経由で行わせるようにしつつ、呼び出し回数をFacadeの中でカウントするような仕組みを作るだけで実現が可能になります。

f:id:masamasah:20151129121328p:plain

デザインパターンと共に学ぶオブジェクト指向のこころ001

第1章:オブジェクト指向パラダイム

オブジェクト指向を説明するために手続き的な方法とオブジェクト指向を対比して解説をしています。

オブジェクト指向以前のパラダイムであった手続き的な方法は機能分解と表現されており、機能に着目して メソッドを分解していくやり方として説明がされています。

実生活において複雑な問題に取り組むときは問題を小さい範囲に切り分けて小さく小さく解決していくという方法はよくある方法であり、それが有効な手段であることは実生活を送る中でイメージがつきやすいし、単純明快なので、楽です。 なので、易きに流れるという意味で、何にも考えずに開発を進めていくと手続き的になってしまうのは当然のことなんだということがわかりました。 なんとなく感覚として持っていたものですが言葉にされると腑に落ちる感じがしますね。

ただ、ソフトウェアの要求は絶えず変更されることが常であり、手続き的な方法だとその変更に柔軟な設計にならないという点が問題です。なのでオブジェクト指向を学びましょうねということです。

移譲や責任についての話がその後に出てきているのですが、Martin Fowlerのソフトウェア開発における3つの視点の話を知らなかったので学びになりました。

  • 概念:私は何に対して責任があるのか?という視点
  • 仕様:私はどのようにして使用されるのか?という視点
  • 実装:私はどのようにして自身の責任を全うするのか?という視点

概念レベルでそれぞれのオブジェクトが正しく会話するように設計することで低い結合性や高い凝縮性が実現できるようになるわけですね。

3つの観点からオブジェクトを見ると以下のように説明することができる。

  • 概念:オブジェクトは責任の集合である
  • 仕様:オブジェクトはその他のオブジェクトや自らを起動することができるメソッドの集合である
  • オブジェクトはコードとデータ、そしてそれらの相互演算処理である

DDD本を第2部まで読んでみて

ここまで読んでみて現場の業務でよく使われているDDD的な語彙をカバーすることがある程度できたように感じています。

エンティティやリポジトリなど設計の上で出てくるユビキタス言語を実感を持って理解できるようになったかなと感じています。

本文の中でも触れられていましたが、ユビキタス言語の中にはこのようなDDD用語も含まれており、DDDに取り組むチームの全てのメンバーはこれらを共有できてないといけないと思います。

そういう意味でやっと出発点に立てたのかなと思っています。

一方で、学べば学ぶほどもっと根本にあるオブジェクト指向デザインパターンについての理解が大切であることも強く感じるようになりました。

ということで並行してオブジェクト指向デザパタについても深く学んでいきたいとおもいます。 取り急ぎ以下の本をタイトルに惹かれて購入しました。

オブジェクト指向のこころ (SOFTWARE PATTERNS SERIES)

オブジェクト指向のこころ (SOFTWARE PATTERNS SERIES)

DDD本の学習とここへのアウトプットは継続しつつオブジェクト指向デザパタについても並行で学んでいきたいとおもいます。

エリックエヴァンスのドメイン駆動設計を読んでいる(第2部 第7章)

DDDの実践例

この章では貨物輸送会社のためのソフトウェアを例に挙げてDDDの実践例を示している。

気になった箇所を抜粋して考えてみる。

ドメインを隔離する:アプリケーションの導入

アプリケーションクラスは調停役であり、調停役は自分で出した質問に対する答えを出してはいけない。 答えを出すのはドメイン層の仕事である。

これはレイヤーの責務に関する内容。アプリケーション層はUI層とドメイン層を結ぶ薄い層であるべきで、 アプリケーション層には脳みそを持たせないというような表現がうちの現場ではされています。

エンティティとバリューオブジェクとの区別

配送記録

配送記録は他の配送記録と区別されるのでエンティティである。しかし、配送記録には貨物と一対一の関係があり、 配送記録単体での一意性は持っていない。配送記録は貨物の一意性から借用することになる。

エンティティであってもルートではない場合はグローバルな一意性はいらないということの例だと思います。

位置

位置のような基本的なエンティティは多くの理由から多数のオブジェクトによって使用される可能性があるので出来るだけ責務を小さくしておいたほうが使いやすい。

集約の境界

独自の同一性を持つものはそれぞれが独自に集約を持ち、そのルートとなる必要がある。

リポジトリの選択

リポジトリを持ちうるエンティティはルートのみであるため、リポジトリ候補は5つにあらかじめ絞れる。

どのリポジトリを実装するかについてはアプリケーションの要求に立ち戻って考える必要がある。

ここはかなり重要で、あまり理解できていなかった頃はとりあえずルートエンティティに対しては無邪気にリポジトリを作ったら良いと考えていました。 しかしそれでは意味がなくて、どのように使われるのか?どのようなことがドメイン層に求められるのか?を考えながら設計をしていくと、リポジトリが不要なエンティティも少なからずあるということを頭に置いておきましょう。

2つのシステムを接続する

異なるシステムを自身のシステムに接続する場合、自身のプロジェクトで用いているユビキタス言語に支障をきたす恐れがある。 これを防ぎ、かつ接続元のシステムから自身のシステムで必要な要素だけを取り出せるようにする薄皮的な層を腐敗防止層と言い、14章で詳しく説明があるようだ。

CloudSearchのインデックスフィールド設定について

はじめに

CloudSearchはAWSが提供しているクラウドサービスの一種で、SESとかSQSなどとともにApplication Servicesに分類されるサービスです。 フルマネージドなサービスで、検索機能を提供してくれます。

CloudSearchには検索ドメインという単位があり、検索ドメインには検索可能なデータが集まるコーパスと、検索用インスタンス群が含まれます。

ユーザーは自身が持っているデータをCloudSearchにアップロードすることで、(事前に設定されたインデックスフィールドの設定に基づいて) 検索対象であるコーパスを作成することができます。

インデックスフィールドの設定はAWSコンソールやAWS CLISDKを用いて行うことができます。 AWSコンソールを用いると簡単に設定を行うことが出来ますし、公式のドキュメントでも詳しく説明されています。

ですが、自前のサービスに組み込んで運用するためには、何度でも同じ設定を復元できるようにしておく必要があると考えます。 例えば設定手順書等を作ることでAWSコンソールからの設定をサポートすることはできますが、わりと高頻度で行われるUIの変更や個々人の能力に依存するという問題が残ります。

一方、AWS CLIを用いて設定を行うことも可能であり、コードとして設定を残すことが出来ます。 そうしておくと版管理も楽にできますし、だれが実施しても同じ結果を得ることが出来ます。

そのため、インデックスフィールドの設定はAWS CLIを用いてコードで解決することが望ましいと考えます。

この記事の目的

上記の理由からインデックスフィールドの設定をAWS CLIを用いて行おうとしていたのですが、なかなかまとまった情報が見つからず、結構詰まってしまったので情報を残したいと思います。クライアントのバージョンは以下です。

 $ aws --version
 aws-cli/1.9.7 Python/2.7.9 Windows/8 botocore/1.3.7

インデックスフィールドの設定

公式ドキュメントにあるように、下記のコマンドでインデックスフィールドの設定を行うことが出来ます。

aws cloudsearch define-index-field --domain-name ドメイン名 --name インデックス名 --type インデックスタイプ

少数のインデックスフィールドのみであれば上記でも問題ありませんが、インデックスフィールドが増えてきたり、設定内容が複雑になると可読性がかなり下がってしまいます。

そこで、JSONファイルで設定を用意しておいて、それを読み込んで設定が行えるようにします。たとえばこんな感じで設定ファイルを用意します。

{
  "IndexField": {
    "IndexFieldType": "text",
    "IndexFieldName": "client_name",
    "TextOptions": {
      "HighlightEnabled": true,
      "ReturnEnabled": true,
      "SortEnabled": false,
      "AnalysisScheme": "_ja_default_"
    }
  }
}

用意したJsonファイルをUTF-8で保存し、下記のコマンドを実行するとインデックスフィールドの設定を行うことが出来ます。 ちなみにShift-JISだとCloudSearchに読み込んだ時に日本語が化けます

aws cloudsearch define-index-field --domain-name ドメイン名 --cli-input-json file://jsonファイル名

これを必要な分だけ実行することで複数のインデックスフィールドの設定をコードで行うことが出来ます。

例えばこんな感じです。 index_settingsフォルダの下に設定のjsonファイルがいくつか置かれている状態です。

for /f "usebackq" %%i in (`dir /B /S index_settings\*.json`) do (
    call aws cloudsearch define-index-field --domain-name %domain_name% --cli-input-json file://%%i
)

Jsonの中身

IndexFieldの中に IndexFieldType, IndexFieldName, TextOptions というキーが出てきます。

IndexFieldType

インデックスフィールドの型を選択します。 int型やlatlon型、text型などがあります。詳細はこちらです。

http://docs.aws.amazon.com/ja_jp/cloudsearch/latest/developerguide/configuring-index-fields.html

IndexFieldName

インデックスフィールドの名前です。

TextOptions

ここにはIndexFieldTypeで指定した型に対応したOptionsが入ります。IndexFieldTypeでtextが指定されているのでTextOptionsが入っています。

Optionsは下記の種類があります

  • IntOptions
  • DoubleOptions
  • LiteralOptions
  • TextOptions
  • DateOptions
  • LatLonOptions
  • IntArrayOptions
  • DoubleArrayOptions
  • LiteralArrayOptions
  • TextArrayOptions
  • DateArrayOptions

基本のデータ型とArrayのついた配列型があるイメージです。

Options

Optionsの中には各インデックスフィールドの詳細設定が入ります。

FacetEnabled

検索結果をフィルタするためのフィールドの定義です。 CloudSearchコンソールで検索を行って右の方に出てくるFilterSearchResultsに出てくる内容です。

ReturnEnabled

検索結果として表示するのもを設定します。 これを適切に絞り込むことで検索結果として得られるデータを小さくすることが可能です。 検索結果データの大きさは料金に関わるものなのでこれを適切に設定することはとても大切です。

SearchEnabled

検索対象とするインデックスにはこれを設定します。

SortEnabled

これが設定されているインデックスを基準として検索結果をソートできるようになります。

型と設定できる内容一覧

それぞれ設定できる内容はフィールドの型によって下記のように異なります。

int

{
 "FacetEnabled" :true,
 "ReturnEnabled" :true,
 "SearchEnabled" :true,
 "SortEnabled" :true
}

double

{
 "FacetEnabled" :true,
 "ReturnEnabled" :true,
 "SearchEnabled" :true,
 "SortEnabled" :true
}

literal

{
 "FacetEnabled" :true,
 "ReturnEnabled" :true,
 "SearchEnabled" :true,
 "SortEnabled" :true
}

text

{
 "HighlightEnabled" :true,
 "ReturnEnabled" :true,
 "SortEnabled" :true
}

date

{
 "FacetEnabled" :true,
 "ReturnEnabled" :true,
 "SearchEnabled" :true,
 "SortEnabled" :true
}

latlon

{
 "FacetEnabled" :true,
 "ReturnEnabled" :true,
 "SearchEnabled" :true,
 "SortEnabled" :true
}

int-array

{
 "FacetEnabled" :true,
 "ReturnEnabled" :true,
 "SearchEnabled" :true
}

double-array

{
 "FacetEnabled" :true,
 "ReturnEnabled" :true,
 "SearchEnabled" :true
}

literal-array

{
 "FacetEnabled" :true,
 "ReturnEnabled" :true,
 "SearchEnabled" :true
}

text-array

{
 "HighlightEnabled" :true,
 "ReturnEnabled" :true
}

date-array

{
 "FacetEnabled" :true,
 "ReturnEnabled" :true,
 "SearchEnabled" :true
}

最後にインデックスの貼り直しが必要になりますので、下記を実行してインデックスを張りなおしたら完了です。

call aws cloudsearch index-documents --domain-name %domain_name%

エリックエヴァンスのドメイン駆動設計を読んでいる(第2部 第6章-3)

リポジトリ

DBに格納されているオブジェクトを取ってきてインスタンスを作るということはパッと見た感じ新規にオブジェクトを作っているように映るかもしれないが、ドメインモデルとして考えるとそれは既に存在するオブジェクトであり、新規作成ではない。 これは別の場所に格納されていたライフサイクルの途中にあるオブジェクトを、コード上から扱えるようにするために再生成するということになる。

クライアントが再生成を行う際に、DBに永続化されているオブジェクトをクエリを発行することによって取得することができる。クエリの実行をクライアントが自由にできてしまうと、例えば不完全な形でオブジェクトを扱おうとしたり、集約の約束を無視したアクセスを行おうとしてしまったりすることがある。 その結果、ドメインロジックがクエリやクライアントコードに移されてしまうので、実装が複雑になり、保守できないプロダクトに変貌してしまう可能性がある。

今になって考えてみるとまさにこれと同じことをプロジェクトでやってしまっていたことになんかすごく罪悪感を感じます。。。ごめんなさいー

ともあれ上記のような問題が起こることでせっかくカプセル化してたのが無駄になってしまったわけです。

これを解決してくれるのがリポジトリパターンです。

リポジトリはエンティティのライフサイクルに関わる操作を提供する責務を持つ。 再生成したり、更新したり、削除したりといった操作を提供する責務を持つ。

ここで注意すべきは、リポジトリは要求されたオブジェクトを取り出すが、データベースアクセスなどの操作は全てカプセル化されていることだ。 リポジトリはオブジェクトを取り出すだけでなく、何らかの条件を満たすインスタンスの数といった値を返すこともできる。

まとめ

この一文がすごくまとまってていい感じです。

「グローバルアクセスを必要とするオブジェクトの各型に対して、あるオブジェクトを生成し、その型の全てのオブジェクトで構成されるコレクションがメモリ上にあると錯覚させることができるようにすること」

グローバルアクセスを必要とする=ルートエンティティですし、メモリ上にあると錯覚させる=再生成する操作がカプセル化されておりクライアントが意識せずに扱えることという意味だと思います。