nikkie-ftnextの日記

イベントレポートや読書メモを発信

『Clean Architectures in Python』を写経しました(1st Edition, Part 2 - Chapter 2)

はじめに

頑張れば、何かがあるって、信じてる。nikkieです。
新年明けましておめでとうございます🎍🐮
ブログの書き初めは、冬の休暇に手を動かした、PythonでClean Architecture本についてです。

目次

Clean Architectureと私

ソフトウェアはやっぱりソフトに(=変更しやすいように)作りたいなと私は強く思います。
2020年はClean Architectureについてたびたびインプットしました。
例えばnrslibさんのYouTube配信です。

こういったインプットを経て、「Clean Architecture、概念の理解は深まったと思うけれど、Pythonで具体的にどうやればいいんだろう?」という課題感を感じていました。
層の概念はいわゆる"完全に理解した"けれど、それをPythonのコードに落とし込んだらどうなるのかが分からなかったわけです。

7月のEuroPythonで『Clean Architectures in Python』という本を知りました1

これは積ん読になっていたのですが、ヴァケイションには「緊急なことじゃなくて、自分にとって重要なことをやろう」と写経してみました。
学んだことをここにアウトプットします。

『Clean Architectures in Python』(1st edition)

この本では、物件情報を一覧にするWeb APIを例にClean Architectureが解説されます。

本は2つのPartに分かれます:

  • 前半(Part 1)はTDD導入パート
  • 後半(Part 2)は物件情報一覧API(面積や賃料、緯度経度)を実装
    • TDDで進める(まずテストコードが出てくる)
    • Flask

nikkieのレベル感は以下です:

  • ふだんはunittestでTDD。pytestのコードも雰囲気で読めそうなのでPart 1はskip
  • FlaskHello Worldから先はあまり踏み込めておらず。この本では blueprint や configuration を駆使2してAPIを実装。
    • Flaskの本ではないとのことですが、ドキュメントへのリンクは充実しています。深く理解しているわけではない概念についてもキャッチアップしてついていけました

今回はClean Architectureの理解が目的なので、pytestFlaskの厳密な理解はヤクの毛刈りと考え、あまり深追いせずに進めました。

Part 2 - Chapter 2 での学び

ビジネスロジック部分をClean Architectureで作ると、UIはCLIでもWeb APIでもなんでも採用できそうという感触を得ました。
12月のPHPカンファレンスでnrslibさんが共有していた、「次のフレームワークに持っていく」ってこういう考え方なんですね!
PythonはWebフレームワークが乱立してますが、Clean Architectureで作ったビジネスロジック部分は移動しやすそうです。

そう思った2章のコードはこちら:

repo = mr.MemRepo([room1, room2, room3])
use_case = uc.RoomListUseCase(repo)
result = use_case.execute()

やっていることは

  1. インメモリなリポジトリを作り
  2. そのリポジトリを渡して部屋一覧ユースケースを作り
  3. ユースケースを実行(execute)して結果(=部屋の一覧)を得る

これだけ!
このコード以外はCLIを扱うコードでもWeb APIを扱うコードでもいいわけです。

3つの層:エンティティ/ユースケース/外部システム

この本ではClean Architectureを3つの層のアーキテクチャとして紹介しています(Part 2 - Chapter 1参考)。

3つの層は

内側:エンティティ < ユースケース < 外部システム:外側

という包含関係にあります。
これらの層の間には

Talk inwards with simple structures, talk outwards through interfaces

(意訳:内側の層に対してはPythonのデータ構造やエンティティに定義された単純なデータ構造で依存せよ。外側の層に対してはインターフェースを介して依存せよ)

という原則があります。

この本を読んで、一番腑に落ちたのは

  • ユースケースは内側にあるエンティティの実装の詳細を知っている
  • ユースケースは外側の外部システムの実装の詳細は知らない
    • ただ外部システムのAPIは知っているので、外部システムを利用できる

というところです。

先のコードで use_case.execute() を実行すると

class RoomListUseCase:
    def __init__(self, repo):
        self.repo = repo

    def execute(self):
        return self.repo.list()

ユースケース初期化時に渡されたリポジトリlistメソッドを呼び出します。
ユースケースリポジトリ(外部システム)のAPIを知っていて、listを呼び出せば物件が取得できると分かっているわけです。

ユースケースという概念

今回ユースケースという概念を知れたのが大きいと思っています。

私がプログラミングに入門したとき、クラスの入門例がDogやCat、CarやSuperCarでした。
これらの例は、現実の具体的な事物と対応しているので(私が過学習してしまって)、現実の具体的な事物と対応しないクラスは独力ではなかなか思いつきません。
処理を表すクラス(現実の具体的な事物と対応するわけではない)を作るというのを知ったときは衝撃でした。
今回のユースケースもそのときと同じくらいの衝撃です。

ユースケースを初期化する時にリポジトリを渡すというのも変更しやすさの妙だなと思います。
Chapter 2ではインメモリのリポジトリを渡していますが、これはDBに接続するリポジトリに変わるでしょう。
そうなってもユースケースからはlistというAPIを使うだけなので、リポジトリを付け替えるだけで動作確認が簡単にできますよね。
DBを準備しなくてもインメモリのリポジトリを用意すれば動作させられます(nrslibさんたちが言ってたやつだ!)

用語:モデル/リポジトリ

この本でありがたかったのは、用語の誤解に先手を打っていたこと。

エンティティはドメインモデルの表現と説明する中で、ドメインモデルはDjangoなどのフレームワークにおける"モデル"とは異なると言っています。
ドメインモデルは軽量(lightweight)なモデルで、自身をストレージに保存するメソッドや、JSON文字列としてダンプするメソッドは持ちません。

リポジトリもGitのリポジトリとは無関係です。
外部システムのストレージにアクセスするものはリポジトリと呼ばれます。
リポジトリドメインモデルを返します
先の例ではユースケースリポジトリから返ってきたドメインモデルを扱うわけです。

Clean Architecture以外での学び

JSON文字列を返すためのserializer実装

json.JSONEncoderを継承したクラスを定義し、defaultメソッドを実装、それをjson.dumpscls引数に指定しています。

pytestのdeprecation

py.testというコマンドが出てきて気になったのですが、pytestコマンドが推奨されているそうです。

pytest-flask向けにtests/conftest.pyを作ったところ、@pytest.yield_ficturesPytestDeprecationWarningが上がりました。
deprecateされていて、@pytest.fixtureでいいそうです。

終わりに

「Clean Architecture、Pythonで具体的にどうやればいいんだろう?」
冒頭の問に対して暫定的な答えは得られました。
ユースケースを使った実装を練習していきます!

層の数の違いなどUncle Bobの『Clean Architecture』とは厳密には違うのかもしれませんが、「変更しやすい設計でどう作ればいいか」かなり具体的に分かったので、今の私としては大満足です。

今回のコードはこちら

続くChapter 3では部屋を絞り込む機能を追加します。
実装する上で、リポジトリへのリクエストを表すオブジェクトとレスポンスを表すオブジェクトが登場しました。
これらの扱いも学びが多く、またの機会にアウトプット予定です!

ここまで読んで『Clean Architectures in Python』に興味を持った方、先日2nd Editionがリリースされました🎉
サンプルは変わっていないそうですが、dataclassを使っていたり、分かりやすそうな図が追加されていたりするので、復習がてら読んでみようかと思います。

そしてなんと、月末にはUncle Bobの話が聞ける機会があるので、今よりもう少し理解度を上げて参加するのを目指します。

それでは!


  1. Clean Architectures in Python — EuroPython 2020 Online · 23-26 July 2020

  2. FlaskHello Worldで入門するのは簡単ですけど、うまく使おうと思ったらblueprintやconfiguration など結構キャッチアップがいりますよね(キャッチアップの総量はDjangoとあまり変わらない感覚)