アクティブレコード系のO/Rマッパーを使用している場合のステップアップ方法

大嶋勇樹氏:次に「Controllerに全部書く」からのステップアップの例2を出していこうと思います。

(スライドを示して)今度はRuby on RailsやLaravelといった、アクティブレコード系のO/Rマッパーを使う例を考えてみようと思います。よくある苦しくなりやすい構成はこうなっています。

最近はやはりRailsやLaravelを使って開発している例も多いし、入門で勉強する方も多いのですが、このRails、Laravelに共通しているのは、MVCに加えて、アクティブレコード系のO/Rマッパーを使っていることにあります。

アクティブレコード系O/Rマッパーの意味は後で説明しますが、Ruby on RailsのActiveRecordというO/Rマッパーは、アクティブレコードパターンという設計パターンを採用したORマッパーです。そしてLaravelのEloquentというO/Rマッパーも、アクティブレコードパターンという設計パターンを採用したO/Rマッパーです。

RailsやLaravelといった、MVCとアクティブレコードみたいな構成で、よく苦しくなりやすいのは、こういうコードの役割分担です。

Controllerが利用者とやりとりしつつ、ユースケースの処理の流れも書いてあるし、例えば「リバーシ」の石は打てるのかみたいなドメインロジックのチェックもControllerにバーッと書いてあります。

そしてControllerの中では、Modelクラスのデータを取り出すメソッドを使ってデータを取り出して、Viewに渡すようなことをします。

本来のアクティブレコードの設計パターンと役割

アクティブレコード系、RailsやLaravelをあまり知らない方のためにちょっと補足すると、これ系のフレームワークは、Modelというクラスが、データの入れ物兼データベースとやりとりするメソッドを持てるような設計になっています。それをアクティブレコード系のO/Rマッパーと呼んでいます。

(スライドを示して)実は本来のアクティブレコードパターンという設計パターンはそういうものではなくて、データベースのレコードと対応するクラスを作成して、データアクセスのメソッドとドメインロジックを持たせるというものです。

せっかくなので検索してみようと思います。(画面を示して)「Active Record PoEAA」と検索すると、「アクティブレコード - Martin Fowler's Bliki」というものが出てきます。マーティン・ファウラーという、この分野の巨匠みたいな方、レジェンドみたいな方のブログの日本語版です。

(画面を示して)アクティブレコードの説明が出てきます。これはActiveRecordというて、アクティブレコードパターンという設計パターンの説明ですね。

これはなにかというとデータベースのテーブルやビューの列をラップし、データベースアクセスをカプセル化してドメインロジックを追加するオブジェクトで、詳細は本を読めとなっています。

どういうことかというと、まずデータベースのテーブルやビューの列に相当するフィールドを持っていて、さらにデータベースにアクセスするためのinsertやupdateというメソッドを持っていき、さらにドメインロジックも持っていると。getExemptionやisFlaggedForAuditあたりがドメインロジックですね。

これがアクティブレコードですよと。RailsのActiveRecordや、LaravelのEloquentは、このアクティブレコードパターンを意識したO/Rマッパーです。

アクティブレコード - Martin Fowler's Bliki

(スライドを示して)なので、本来アクティブレコードのモデルの役割は、データベースのレコードと対応するデータを持つことと、データアクセスのメソッドを持つことと、ドメインロジックを持つこと。忘れられやすいですが、この3つがアクティブレコードのモデルの本来の役割分担です。

アクティブレコード系のO/Rマッパーを使うと、これらのような役割をModelが持つので、データベースとドメインロジックがくっつきます。ドメインロジックがデータベースに強く依存する代わりに、O/Rマッパーは強力で、実装量が少なくなります。そんなメリット、デメリット、特徴があるのが、アクティブレコード系O/Rマッパーです。

(スライドを示して)MVCプラス本来のアクティブレコードを使った組み合わせはこんな感じになります。何が違うかというと、ドメインロジックをModelが持っていることです。ActiveRecordはModelが持っているものです。

これが本来的なアクティブレコードであって、Controllerからドメインロジックを移動してあげるだけで、コードの改善はだいぶ進むと思います。

さらに、もしControllerからユースケースを分離したいのであれば、ServiceやUseCaseクラスみたいなものを使って分離してあげてもいいと思います。

今日のここまでの話を聞いていると、Modelの役割が多すぎるんじゃないかと思われるかもしれませんが、それはアクティブレコードパターンのメリット・デメリットですね。

(スライドを示して)Modelの役割が多すぎると考えるなら、例えば、このような構造にすることもできます。

ActiveRecordやEloquentでRepositoryを内部的に実装して、ServiceはRepositoryを使います。プラス、このActiveRecordのデータの入れ物とは別にドメインモデル、データの入れ物かつドメインロジックを持つActiveRecordが関係ないドメインロジックのためのモデルを作って、こちらを使うような構成もできます。ただ、これは個人的にあまりおすすめしないです。

なぜかというと、アクティブレコードは、特にRails、LaravelのようなMVCとアクティブレコード系O/Rマッパーの場合であれば、アクティブレコードパターンというやり方をすることで実装量がすごく減って、早く開発できるのが魅力です。

代わりに、Modelの役割がわりと多いので、そこがネックになることはあり得ますが、そのデメリットを取ってでも、高速に少ないコードで実装できるという特性を取りたいからActiveRecordを使うというような発想でいるのが、私はいいんじゃないかなと思っています。

実際には、メンバーを集めた時点でアクティブレコード系O/Rマッパーを使うフレームワークに決まっていることもあると思いますが、アクティブレコード系O/Rマッパーを使うのであれば、アクティブレコードパターンの特性を活かすことも大事だと思います。

「どっちがいいか」「こういうことをしたほうがいいのか」「こういういっぱい分離するやり方がいいのか」「またもっと前に書いたようなやり方がいいのか」と言うと、答えがあるわけではないと思っています。

言語やフレームワークに適した設計を採用することが重要

(スライドを示して)ここまでいろいろ話をしてきましたが、まずはいろいろな言語・フレームワークで使える基本的な考え方が、あるとは思います。それに加えて、言語やフレームワークに適した設計を採用することや、逆に設計手法を言語やフレームワークに合わせる、設計手法に適した言語・フレームワークを採用すること、言語やフレームワークに適した設計を採用すること。これがすごく重要な要素の1つだと思っています。

設計手法にも、常に正解があるわけではないので、フレームワークや特にO/Rマッパーに引っ張られるケースも、引っ張られたほうが得だと判断するケースもありますが、そういうものに適合させたりしつつ、うまく設計していくとか、実装していくのが大事かなと思います。

(スライドを示して)ということで、最後に一応参考文献をいろいろ載せていますが、私が用意していた話は以上です。

最後にちょっと宣伝です。最初にも少し話しましたが、この分野でつい先日、Udemy講座を出しています。「『リバーシ』で学ぶアプリケーション設計入門」ということで、今日のテーマの分野を具体的な実装を含めて解説した講座になっています。

アプリケーション設計に大きく関わる仕様の整理みたいなところから、TypeScriptでの実装例まで解説しています。エラーの解決や疑問点もできるだけサポートします。興味がある人はぜひ手に取ってもらえればと思います。

ということで、私の発表は以上です。