読者です 読者をやめる 読者になる 読者になる

理系学生日記

おまえはいつまで学生気分なのか

忍者TOOLS

PoEAA: Unit of Work

poeaa technology

今日からは Chapter 11. Object-Reational Behavioral Patterns という新章に突入します。 その一つめのパターンは Unit of Work でした。

Patterns of Enterprise Application Architecture (Addison-Wesley Signature Series (Fowler))

Patterns of Enterprise Application Architecture (Addison-Wesley Signature Series (Fowler))

Unit of Work の概要

Unit of Work は、データベースに対して実施しなければならない操作 (一般には CRUD ということになります) を維持していくパターンです。

DB に対する変更の必要が生じる都度 INSERT/DELETE/UPDATE なんかを実行していくとパフォーマンス影響が生じますし、トランザクションがクライアントサイドからの複数のリクエストに跨るのを制御するのは難しいため、これらを解決するパターンとして登場しました。 Unit Of Work では、業務トランザクションにおいて実行すべき DB 操作を維持し、業務トランザクション完了時点でそれを DB に commit します。

Unit of Work の使われ方

当然ながら、どういうオブジェクト(が表現する DB データ)にどういう更新が入るかを Unit of Work に知らせる必要がありまして、どういう風に知らせるかというパターンは 2 種類存在します。

  1. 呼出側が Unit Of Work に登録する -- これは単純に、ドメインロジックの開発者が unitOfWork.registerDirty(object) といった形でコーディングするイメージですね。単純ですが分かりやすく、そして、開発者が登録を忘れると当然 Unit of Work は何もできません。
  2. オブジェクトが自動的に Unit of Work に登録する -- これは、データを表現するドメインオブジェクト自身が、自動的に Unit Of Work に登録するイメージです。例えば、ドメインオブジェクトが DB のデータから生成された場合は、その生成メソッドの中で unitOfWork.registerNew(this) みたいなかんじなるでしょうし、Setter が呼び出された場合は、unitOfWork.registerDirty(object) というようなメソッド呼び出しが必要になります。 -- 1. のパターンと比較すると、Unit Of Work に対する登録の責務が、ドメインオブジェクトの開発者に移ったイメージでしょうか。

ただ、こんなことを人類が完全に行うことができないのは歴史が証明しているので、ここはアスペクト指向の出番かもしれません。

また、DB に対するコミットが Unit of Work という仕組みに一元化されることから、

  • 参照整合性を確保しやすくなる (update するときのテーブル順が容易に制御できる)
  • デッドロックの可能性を極小化できる
  • batch update を実行しやすく、パフォーマンスメリットがある

といったメリットも生じます。

感想

とても便利な仕組みだと思います。 そして便利な仕組みであるが故か、正しく実装するのはとてもダルいだろうなぁという印象を持っていましたが、PoEAA 上のサンプルが非常に簡潔だったことにわりと衝撃を受けました。 ※もちろん、ここでは参照整合性を確保やデッドロック云々は実装上入っていないのですが。

このパターンの実装、とても綺麗だと思うのですが、この実装を使う側への説明とその徹底(登録や更新をするときは必ず Unit of Work を使用する)が難しいのと、それを自動化しようとすると可読性が下がるのとで、なかなか適用はハードルが高そうです。。。

PoEAA: Data Mapper

poeaa technology

Data Mapper で、とりあえずは Chapter 10. の Data Source Architectural Patterns はおわり。

Patterns of Enterprise Application Architecture (Addison-Wesley Signature Series (Fowler))

Patterns of Enterprise Application Architecture (Addison-Wesley Signature Series (Fowler))

オブジェクト指向言語におけるデータの表現と、RDBMS 上のデータの表現は本質的にインピーダンスアンマッチが発生します。 一方は継承等の技巧が使える一方、RDBMS ではそれを使うのが難しい。

こういうところは、アーキテクチャとして解決していかないといけない、ということで、Data Mapper が登場します。 Data Mapper は、プログラム実装上の"オブジェクト"を DB 上のデータから切り離すためのレイヤとして機能します。この層の存在によって、開発者は SQL や DB 上のテーブル構造を意識せずに、ドメインロジックを構築していけるようになります。はい。

Finder

ぼくは Mapper と聞いて、DB のレコードの情報を実装上のオブジェクトに変換する Converter をイメージしていましたが、ここで述べられている Data Mapper はそれに留まる概念ではないようです。 例えば、ロジックのレイヤが DB からデータを取得するにあたり、その「情報取得の要求」は Data Mapper に送られます。言い換えれば、Data Mapper は DB に問い合わせてデータを取得するという Finder の役割を担います。よくよく考えてみれば、Data Mapper はロジックと DB の中間のレイヤに位置付けられるので当然ですね。

Data Mapper を実装する上での課題

  • ロジック層に Finder とかを意識させないための技巧として遅延ロードがあるけど、遅延ロードを実装するのにそれだけの価値があるか
    • Hibernate とかはこのあたりを結構やってくれるけど、ぼくはあの動作が分かりにくくてスゴく嫌いだった
  • Data Mapper の配置
    • Data Mapper はドメインロジックと密に結合することになるので、ドメインロジックにいくつかのメソッド実装を要求する。このようなメソッドを public とするか、それともパッケージプライベートにするか。
    • public にする場合、他のロジッククラスに対して、「何このメソッド」というようなメソッドが公開されることになる
    • パッケージプライベートにする場合、Data Mapper とドメインロジックは同一パッケージに配置されることになるので、ドメインロジックを使用する他のロジック/実装は Data Mapper の存在を意識できてしまう

Emacsの最大化/全画面化が圧倒的簡単な時代に

emacs technology environment

Emacs を最大化/全画面化するのって、かつては結構な elisp を書かなくてはいけなかったような覚えがあります (単純に知らなかっただけかも) が、 今はなんと、以下のような 1 行だけ書いてれば良くなりました。

; 最大化 <=> 元に戻す
(toggle-frame-maximized)

; 全画面化 <=> 元に戻す
(toggle-frame-fullscreen)

標準でインストールされる frame.el に定義されていたので、追加 elisp のインストール不要で導入可能です。

PoEAA: Active Record

technology

今日の PoEAA は、Active Record。

Patterns of Enterprise Application Architecture (Addison-Wesley Signature Series (Fowler))

Patterns of Enterprise Application Architecture (Addison-Wesley Signature Series (Fowler))

全体感としては、Chapter 10. Data Source Architectual Patterns の中の 1 節で、いわゆるパーシステント層の 1 パターンに該当します。

Acitve Record

Active Record といえば Ruby On Rails (RoR) ですが、RoR の Active Record は今よんでいる PoEAA の Active Record パターンを適用したものになります。 ぼくは RoR については門外漢(研修でさわったくらい) なので語る言葉を持ちませんが、RoR の Active Record と PoEAA の Active Record の内容にあまり差異はないように見受けられました。

Active Record の概要をまとめると以下のようになります。

  • DB のテーブルあるいはビューの 1 レコードを表現するオブジェクトである
  • DB アクセスとともに、オブジェクトが表現するデータに対するドメインロジックを含む

先日の http://kiririmode.hatenablog.jp/entry/20161119/1479539592:tilte と良く似ているパターンですが、Active Record はドメインロジックを含むという点が大きな違いです。

個人的感想

Active Record で言うドメインロジックの範囲をどこまで絞るかが開発上のポイントなのかと思っています。 オブジェクトが表現するデータのバリデーションだったり型変換くらいであれば「アリ」かなーと思うのですが、他のオブジェクトとのコミュニケーション/インタラクションまでを Active Record に持たせると、もはやパーシステント層とは何なのか、みたいな部分が出てきて開発が混乱しそうな気配があります。

また、テスト戦略はどうすべきなのかは悩みどころ(ただし決めの問題?)で、DB とは密結合する層のパターンであるので、DB 依存のテストを実施するのか、それともその DB 依存のテストは実施しつつも最小限に留める(バリデーションとかは DB 非依存にする) のか、それとも DB 非依存にするのかっていうところの見地などを聞いてみたいところです。

gitリポジトリの中のファイルをhelmインタフェースで検索する

emacs environment technology

Emacs バッファの中で、他のファイルに検索をかけて、検索結果一覧のバッファから直接ファイルの内容を更新するというタイプの拡張というのがいくつかあって、moccur-edit.el だったり、wrep.el だったりです。

しかし、こういうときの検索あるいは編集する対象のファイルというのは、そのときのコンテキストに依存します。そしてそのコンテキストの多くは「xx ディレクトリ配下の全ファイル」というよりは、「xx プロジェクトの全ファイル」だったりするでしょう。 そういう拡張ってあったよなー何だったかなーと思ってさがしていたら、helm-eg を見つけました。 見つけたというよりは、もともとロードしていたんだけれど、そういう使い方をできるということを知らなかった。

helm-ag

helm-eg はみんな大好き helm のインタフェースで The Silver Searcher という検索ツールを使用して検索結果一覧を出すというのが主な用途なのですが、The Silver Searcher (これをよく ag と呼びます) だけでなく、検索ツールとして grep だったり、ack だったりを使用することもできる、汎用の検索ツールとして使うことができます。

基本的には、grep とかと同じく「このディレクトリ以下のファイル」という検索になるんですが、今日見つけることができたのは helm-ag-project-root

現在バッファで開いているファイルの存在するディレクトリから親ディレクトリを辿って .git.hg 等の存在するフォルダを探し、そのフォルダ配下の全ファイルを検索対象にすることができます。

;;;###autoload
(defun helm-ag-project-root ()
  (interactive)
  (let ((rootdir (helm-ag--project-root)))
    (unless rootdir
      (error "Could not find the project root. Create a git, hg, or svn repository there first. "))
    (helm-ag rootdir)))
    
(defun helm-ag--project-root ()
  (cl-loop for dir in '(".git/" ".hg/" ".svn/" ".git")
           when (locate-dominating-file default-directory dir)
           return it))
    

これにキーバインドを設定しておけば、効率は一段とあがって良さそうです。

(global-set-key (kbd "M-p") 'helm-ag-project-root)

隠しファイルも検索対象にする

で、便利に使おうと思っていたら困った事象が、. から始まるいわゆる隠しファイル、隠しディレクトリが検索対象から外されてしまうことです。 これ、elisp を見ながら「そういう実装なんてないけどなー」と思っていたら、ag コマンドの仕様でした。 ag コマンドでは何も指定しないと隠しファイル/ディレクトリは対象外となってしまうため、--hidden オプションを追加する必要があります。

$ ag -h | grep -C 1 'Search hidden file'
  -G --file-search-regex  PATTERN Limit search to filenames matching PATTERN
     --hidden             Search hidden files (obeys .*ignore files)
  -i --ignore-case        Match case insensitively

elisp からオプションを設定することは可能なので、このような場合は、

(setq helm-ag-command-option "--hidden")

と設定すれば良いでしょう。

PoEAA: Row Data Gateway

technology poeaa

Row Data Gateway は、DB 上の 1 レコードに対する Gateway として働くオブジェクトです。 1 レコードに対する DB アクセスをカプセル化して、他のレイヤに対して DB アクセスを隠蔽します。 先日の Table Data Gateway がテーブルに対する Gateway だったのとは粒度が異なる形です。

Table Data Gateway もそうですが、DB アクセスが他のレイヤから隠蔽されるため、この Gateway を差し替えることで他のレイヤのテストを DB 非依存にできる、DBMS ロックインされにくくなる、といったメリットがあります。 DB アクセスロジックを含む反面、業務ロジックは含まれません。それを含んだ Row Data Gateway は、もはや Row Data Gateway ではなく、Active Record と呼ばれるパターンになります。

Finder

Row Data Gateway は 1 レコードを表すオブジェクトなので、自分自身を DB に反映する insert/update/delete のロジックを内包するのですが、じゃぁ自分自身を返す(select) ロジックは誰が持つのか、ということを考える必要があります。 一般に、それを持つのは別オブジェクトで、Finder とか呼ばれたりします。Finder#find(id) だったり、Finder#findForCompany(company id) だったりが、Row Data Gateway のオブジェクトを返すっていう使われ方ですね。

作成方法

業務ロジックから見たときに DB 構造を抽象化するレイヤなので、Row Data Gateway のレイヤ自体を作成するときは DB を直接意識して記述する必要があります。 DB のスキーマが変化する中でその変化に人間が追随させるというのが無理であることは歴史が証明しているし実装と設計書が乖離することは残念ながら人類が関わり続ける以上は不可避であるので、スキーマ定義から自動生成させるべきだと思います。

参考

Bliskとは何であり、何ではないのか

technology

Blisk ね、Blisk。

最近ずっとレスポンシブウェブデザインのサイトを作っていたもので、そのテストをどうやったら手早く効率的にできるものかなぁと思っていたときに、まさにこの Blisk というヤツが登場しました。 コレいいなー、すごくいいなーと思って使っていたんですが、どうも思っていたものと違ったので、どこがどう違ったのかを簡単にまとめておこうと思います。

なお、このエントリは、社内のノウハウとして展開する予定の原稿の草稿になります。

Blisk

Blisk というのは、レスポンシブウェブデザイン(RWD)のサイトをクロスデバイスで効率的にテストできるように作られた、開発者用ブラウザです。 主な特徴としては、以下のようなものがあります。

  1. 予め、確認用のスマートフォンやタブレットの設定が用意されている
  2. PC 用デザインとモバイル用デザインを 同時 に確認できる
  3. PC 用画面とモバイル用画面上でのスクロールやユーザアクションの結果が同期する
  4. PC 用画面とモバイル用画面で、それぞれ開発者ツールが使用できる

f:id:kiririmode:20161117230254p:plain

モバイル端末のエミュレーションを行う上で、Blisk は

  • 解像度のエミュレーション
  • ピクセル比のエミュレーション (Retina 対応端末なら Retina のピクセル比で表示してくれる)
  • UserAgent の模倣

などを実施してくれます。

価格体系

Blisk、かつては無料で使い放題だったのですが、2016/11/1 に有料化が発表されました。 2016/11/16 時点で、$9.99/月の費用がかかります。 払わないでも trial として使用は可能ですが、この場合、1 日 30 min. のみ全機能が使用できる状態になります。

この有料化がユーザの裾野を狭めるのは間違いありません。会社で使おうにも、そのハードルは一段と高くなったと思います。

Blisk は何であり、何でないのか

レンダリングエンジン

Blisk のベースとなるのは Chromium - The Chromium Projects です。 このため、同じく Chromium から派生した Chrome とほぼ同じように動作しますし、Chrome の拡張の多くもインストール可能です。

逆に言うと、レンダリングエンジンは Blink です。Firefox (レンダリングエンジンは Gecko)や IE 11 (レンダリングエンジンは Trident)、Safari (レンダリングエンジンは WebKit)等を使用したクロスブラウザを観点としたテストは行えません。

別個の HTML のレンダリング

Blisk では端末のエミュレーションを行う際、PC 画面とモバイル用画面を同時に動作することができます (Scroll Sync 機能)。これはあたかも、PC とモバイル画面の双方が共通の HTTP リクエストを送り、その結果返却されるレスポンス (HTML) を解析し、その結果をモバイル用画面、PC 用画面のそれぞれでレンダリングするようにも思えます。 しかし実際は、PC 用画面で HTTP リクエストを送出するとともに、モバイル用画面でも別の HTTP リクエストを送出しています。 例えばログインを要求するサイトを Blisk で閲覧する場合、ログインは 2 度行われます (PC 用画面で 1 回、モバイル用画面で 1 回)。

下の画像はニコニコ動画にログインした際の結果ですが、モバイル用画面は日本語サイト、PC 用画面は英語サイトが表示されています。 このように、Blisk は 別個の HTTP リクエスト/レスポンスに紐づく、別個の HTML を表示 していることに注意が必要です。

f:id:kiririmode:20161117233215p:plain

異なる画面で同一セッションを使用

さらに、Blisk では、異なる画面で同一セッションを使い回している挙動を示します。 例えば、先の例のニコニコ動画で、モバイル端末の画面でログインを行うと、当然ながら HTTP POST が送信されますが、PC 用画面は GET のみでログイン画面に遷移できます。

このように、セッションを両画面で使い回しているため、複数タブで同じサイトを利用するというような動作となり、複数タブに対応していないようなサイトでは十分なテストを行うことができないケースが発生します。

まとめ

Blisk は開発者フレンドリなブラウザではありますが、その利用シーンは以下のようなものになります。

  1. レスポンシブウェブデザインで作成された静的なページに対する、クロスデバイスでの表示確認
  2. 複数タブでの操作に対応した Web アプリケーションのテスト

逆に、複数タブでの操作に対応していない Web アプリケーションのテストは非常に困難になると考えています。

PoEAA: Table Data Gateway

technology design pattern book

PoEAA は 10 章の Data Source Architectual Patterns に突入しました。 その一つ目は、Table Data Gateway です。

Patterns of Enterprise Application Architecture (Addison-Wesley Signature Series (Fowler))

Patterns of Enterprise Application Architecture (Addison-Wesley Signature Series (Fowler))

Table Data Gateway は、1 つのテーブルあるいは View に対して発行される select/insert/update/delete といった SQL を保持して当該のテーブル/View へのアクセスを抽象化するパターンです。これにより、データアクセスをビジネスロジックから排除することができます。このパターンを使用しているアーキテクチャはよくみますね。

これといって特筆すべきこともなさそうなんですが、まず Table Data Gateway は基本的にはステートを持ちません。ドメイン層から渡されるのが抽出条件だったりで、Table Data Gateway はそれを元に SQL を構築して DB に発行し、DB から返却された値をドメイン層に返すっていう実装にしかならないので、まぁステートレスにはなります。 あとは返却する値についてはいくつかパターンがありまして、

  1. wikipedia:Data Transfer Object (DTO) を返す
  2. Record Set を返す
  3. Domain Object を返す

このパターンだと、ぼくは 1. の DTO を返却するのが好きで、単純に POJO を返す方がテストが楽じゃん、というのが根底にあります。 ただ、採用する言語系によっては .NET などのように標準的に Record Set を返却するケースもあるらしいです。

参考文献

PoEAA: Service Layer

design pattern technology book

最近、読みかけで止まっていた、Patterns of Enterprise Application Architecture (いわゆる PoEAA) を再度読みはじめました。

Patterns of Enterprise Application Architecture (Addison-Wesley Signature Series (Fowler))

Patterns of Enterprise Application Architecture (Addison-Wesley Signature Series (Fowler))

システムというものをゼロから設計するためには、これまで積み重ねられてきた知見を知っておかないと立ち行かないなぁと思うことが多くなったのが一番の理由です。 この手の本は、自分の理解を文章にアウトプットしておかないとただ「読んだ」という状態にしかならないので、こういうエントリにして自分の言葉でできるだけまとめておきたいと思います。

Service Layer

今日のテーマはサービス層で、Java 使ってるとよく見にするパターンです。ContractsService.java なんてクラスがあると、あぁ Service Layer だろうなって感じになりますね。

Service Layer が解決する課題

Service Layer というのは「サービス」と呼ばれる層を設けることで、ビジネスロジックを系統立てるデザインパターンです。

ここでの「ビジネスロジック」は大きく 2 つに分けられるという前提を持っています。

  1. ドメインロジック
  2. アプリケーションロジック

ドメインロジックというのは、個々の問題領域に適用できるロジックで、wikipedia:Strategy パターンを適用するようなロジックと考えると分かりやすいでしょう。 一方で、アプリケーションロジックというのは、まさに今、対象としているエンタープライズアプリケーション特有のロジックで、例えば「この処理をしたらメールで責任者に通知する」とか、「この処理をしたら証跡ログを残しておく」とかになるでしょう。本の中では "workflow logic" とも呼ばれる、という記述がありましたが、そっちの表現の方がイメージが伝わるかもしれません。

Service Layer が取り組む課題の焦点は、この アプリケーションロジック をどう扱うかに当たっています。このアプリケーションロジックをドメインロジックと一体化させようとすると、ドメインロジックの対象とする問題(problem domain) 以外の要素をドメインロジックに持ち込むことになり、ドメインロジックの再利用性が著しく下がってしまいます。 Service Layer は、このドメインロジックとアプリケーションロジックとを明確に分離して「階層化(Layering)」による恩恵を受けることができるようにし、純粋なドメインロジックの再利用性を高めようというパターンになります。

実装

本の中では、基本的な実装パターンとして、

  1. wikipedia: Facade パターンでの実装
  2. Operation Script としての実装

が挙げられていましたが、本の焦点も著者のお勧めも後者でであるので、ここでは後者についてのみ記述しておきます。

Operation Script

Operation Script におけるサービス層は、

  1. アプリケーションロジックは直接実装 (書き下すわけではなく、Gateway とかに移譲する)
  2. ドメインロジックはドメインオブジェクトに移譲

という構成の Thick なクラスとして実装されます。"Script" という表現は個人的には微妙だなと思うのですが、まぁ P of EAA: Transaction Script でも同じような文脈で Script という単語が使われているので、そういう使い方なんでしょう。

初期のアプリケーションで要求されるビジネスロジックは単純な CRUD 等に留まることが多く、ドメインロジックのみで構成されることも多いのですが、メール通知や証跡ログ等、段々と「アプリケーションロジック」に属するロジックが増えていきます。 アプリケーションロジックは外部システムへの副作用をもたらすものも多く、トランザクション境界の中で処理されなければならないケースが大半です。こういうとき、サービス層を構成するサービスクラスはトランザクション境界としても定義することができ、アプリケーションロジックの追加がシステムに混乱をもたらしにくい構成になっていると感じました。

ファイルを直接開くのを禁止してセキュリティ向上 (X-Download-Options)

technology

MIME-sniffing

IE には、セキュリティを劣化させるとして悪名高い MIME-sniffing という機能があって、サーバがどんなにこのファイルはテキストファイルだよ!! (content-type: text/plain) とブラウザに教えても、そのテキストファイルに HTML の要素があると HTML と解釈してしまう場合があったりします。 そして、これを防ぐには HTTP のレスポンスヘッダに X-Content-Type-Options: nosniff を付けることが定石とされています。このヘッダを加えると、IE は MIME-sniffping を実施しなくなります。

MIME-Handling

上述の MIME-sniffing は良く知られた振舞いだと思うんですが、今日ぼくが知ったのは「ブラウザで直接ファイルを開かないようにすることで、セキュリティを向上させる」という方法でした。 IE では、ファイルをローカルに(明示的に)保存することなく開くという機能があります。ダウンロードリンクをクリックすると出てくるダイアログの「開く」ボタンを押下したときに働く機能ですね。

実はこの機能でファイルを開いた場合、そのファイルの内容はダウンロード元のウェブサイトのコンテキストで実行されるらしいです。悪意のあるスクリプトなんかをこの方法で開いてしまうと、インジェクションなんかが可能になるってことですね。 これを防ぐためのレスポンスヘッダが存在していて、それが

X-Download-Options: noopen

というレスポンスヘッダです。このヘッダをレスポンスに付与しておくと、ユーザは当該のファイルを直接「開く」ことができなくなり、明示的にローカルにダウンロードする他なくなります。 ローカルにダウンロードされたファイル(悪意のあるスクリプト込み)を後で開いたところで、既にウェブサイトのコンテキストで実行されることがないので、少なくとも Web サイトに対する脅威ではなくなると。

色々なヘッダがあるものだなぁと。なお、このヘッダが効くのは IE 8 から。Edge はどうなんだろ。

参考文献