LoginSignup
31
41

More than 5 years have passed since last update.

5年ぶりに前線復帰して、Python Django をリファクタした話

Posted at

背景

昨年からスタートアップに参画して、Python のフレームワーク Django で開発しています。5年近く開発のブランクがあったので、「今どきの開発はこうなのかー」と日和見を決めこんでいました。でも、いろいろなアンチパターンや突っ込みどころが満載だったのです。

前の前の担当者と前の担当者がやめて、そのつけがまわってきました。残されたコードを見て、頭をかかえたり、あきれかえったり。

そんな中でリファクタリングしたときの話、これからリファクタリングする話をまとめようと思います。

やらかされていた問題

語り始めると止まらなくなりそうですが、アーキテクチャから見て残念なものが二つありました。

スマートすぎるビュー

もともとバッチ処理で作成されたデータをビューが取得するだけのシンプルなアプリだったようです。しかし機能追加があって、ビューでいろいろ計算するようになりました。

※ よくわかっていなかったころの私は、ある時期にコピペ駆動開発でそれに加担してしまいました。
orz

散らかされたロジック

前の担当者が何とかしようとしたようなのですが、ビューにロジックが残ってたり、モデルにロジックがあったり。二つの異なるパッケージで循環参照を生じさせたり。ろくに動作確認もしてないし。もうひどいことになりました。

根本問題は…

時間が足りなかったとか、担当者がアホだったとか、状況や人に依存することは横に置きます。

そもそも Django はテーブルとモデルが1:1になっている、シンプルな構成を想定しています。本来の意味とは若干ずれるのですが、Active Record パターンに該当するでしょう。

この構成はアプリが複雑になってくると問題が出てきます。

  • データ構造の問題

機能が複雑になってくると、テーブルが変更されます。当初想定していたモデルと合わなくなってくることがあります。

  • 結合したデータの問題

Django だとビューからモデルを直接呼びがちになると思います。

複数のテーブルの情報を結合させるとビューでロジックを書くことになりがちです。複雑な計算をさせると「え?これってこのモデルが担当するの?」と責任の所在が分かりにくくなります。

  • アーキテクチャの混乱

そもそも Model-View-Controller はプレゼン層のパターンです。それがモデルにデータソースのアーキテクチャパターンが混じっているようで、分かりにくくなっています。(単純に私が理解していないだけとも思いますが)

解決策

基本に立ち返ってレイヤー化することから始めてみました。その際、ドメイン駆動設計や、(訳がひどいと評判ですけど)エンタープライズアーキテクチャパターンを参考にしてみました。

個人的にはエンタープライズアーキテクチャパターンの内容が腹に落ちました。

http://www.amazon.co.jp/dp/4798121967
http://www.amazon.co.jp/dp/4798105538

おさらい:基本的なアプリケーションレイヤー

いくつかパターンがありますが、4層にわける方法が分かりやすかったです。

  • プレゼン層: View やデータローダー、外部連携
  • サービス層: プレゼン層にたいして粗い API を提供、ドメイン層への Facade でもある。この層でリモート Facade にすることも。
  • ドメイン層: データとビジネスロジックを持つ。細かい API を提供する。
  • データ層: データソースと CRUD したり、オブジェクトに変換したり

繰り返しになりますが、個人的に Django が分かりにくかったのが、プレゼン層のモデルとドメイン層とデータ層がひとつにまとまっていたように見えたところです。このあたりを分割するのがキモになりました。

サービス層の導入

  • インターフェースと引数

一つのビューに対して一つのサービスを提供するようにしました。メソッドのパラメータが複雑化することがいやなので、Data Transfer Object っぽいパラメータをわたして処理させました。

  • サービスの分類

サービスも二種類にわけました。

一つは基本的な CRUD を処理するもの。命名規則も XxxService。

もうひとつは複数のオブジェクトを結合して、複雑な計算・処理をするもの。命名規則も XxxEngine。

色々な解説をよんでもよくわからんかったので、以下のコードを参考にしました。
http://www.infoq.com/jp/news/2015/04/ddd-trading-example
https://github.com/archfirst/bullsfirst-server-java

  • サービスの Singleton 化

サービスのインスタンスを都度作成するのも非効率なので Singleton 化しました。

  • Strategy の導入

これは今後の課題ですけど。

XxxEngine の中に似たような処理がいくつかありました。これは Strategy パターンで分離すれば、もっと見通しがよくなるハズです。

モデルの扱い

Django のモデルはデータ層と見なすことにしました。

ドメイン層の導入

今後の予定ですが、ドメイン層を導入しようと考えています。Django のモデルを継承したクラスでドメインロジックを実装します。

昔、テーブルからオブジェクトに変換するときには、POJO(Plain Old Java Object)へマッピングしていました。ドメインモデルとしてロジックを実装するには POJO を継承したクラスで対応していました。
それを参考に、POMO(私の造語w:Plain Old Model Object) を継承して、ドメインモデルにしよう考えているわけです。

まとめ

まだ微妙な気がしますが、現状はこんなところです。

Rails でサービス層を導入した話をみかけて「同じことを考える人がいるものだなー」と思い、Django で考えたこと/対応したことをまとめました。

ただ、今回悩んだことはエンタープライズアプリケーションパターンで語られていた問題でした。今となってはフレームワークが隠蔽していて意識していないものが多くあります。しかしフレームワークの限界を超えようとするとき、基本をしっかり知っていることが大事だなー、と思ったのでした。

31
41
3

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
31
41