所感など
先日CleanArchitectureを読んで感想をまとめました。
なんとなく書いてある意味は伝わったし、完全に理解したと確信したので「いっちょ趣味アプリでも作ってみますか〜w」と軽い気持ちでアプリを作ってみました。
やはり手を動かしてみると気づくことが多く、それこそ途中で「CleanArcitectureなにもわからない」状態に陥ったりもしました
何度か立ち止まって書籍を読み返したりして、一度読んだだけでは理解できていなかった部分を掘り下げることができました。しんどいですけど、やっぱり読みっぱなしじゃなくアウトプットすることは大切ですね。(自戒
成果物
MH-Z19C(二酸化炭素濃度センサー)を使って部屋の二酸化炭素濃度を測定するCUIアプリです。
co2checker
ちなみにラズパイZeroで作ったのですが、本筋と外れるので結線や環境構築は割愛します。知りたい方がいたらコメントください。
ディレクトリ構成
src/
├ main
├ domain/ ・・・図でいうところの「Entity」 。システムの登場人物たち。
├ driver/ ・・・図で言うところの「Gateway」。低レイヤ層との通信を担う。
├ handler/ ・・・図で言うところの「Controller」。
└ usecase/ ・・・そのままUsecase層
クラス図
Pythonなので厳密には抽象クラスはないですが、依存性の逆転箇所がわかりやすいのでAbstract扱いにしています
迷ったポイントなど
まずそこまでpython慣れてないのでその辺は辛かったのですが、もう少しアーキテクチャ寄りのはなしなど。
ユースケースって、何?
途中ゲシュタルト崩壊するくらい悩みました。
具体的には「センサーで二酸化炭素濃度を測り、1001以上ならLINEで通知する」を実装するときに、どこまでを1ユースケースにするかを迷いました。
「測定する」と「LINEで通知する」の2つに分けるか、「測定して濃度次第では通知する」の1つにまとめるかの2パターンが考えられましたが、今回は後者を選択しました。
前者は測定結果を見て通知するかどうか判断するのがController層になってしまうのですが、Entityが持つメソッドを叩くのにふさわしいのはUsecaseだと判断したので、後者を選びました。
とはいえ、今回DTOを使わなかったのでこういう形になったのかとも思います。仮にプレーンなオブジェクトをControllerが扱えるなら、やはりユースケースを2つに分ける選択肢もアリだったんじゃないかと思います。
DTOって本当に要る?
いや要るんですが、今回はどう考えても冗長なので作りませんでした。
書籍では「レイヤをまたぐオブジェクトと、ドメイン知識を表すオブジェクトは変更される理由が異なる。なのでDTOでEntityをシステム都合の変更から守りましょう」という趣旨のことが書かれていたと思います。とはいえ今回のCo2
エンティティはvalue
とis_dangerous()
メソッドしか持たないミニマムなクラスです。
Co2NotifierCommand
みたいなDTOクラスを作ったところでvalue
くらいしか渡すものがないので今回はわざわざDTOを作るのは控えました。
でもお仕事で作るようなシステムなら絶対最初から作っておいたほうがいいんでしょうね・・・。
Boundaryどこに置くか問題
依存性の逆転を担う抽象クラスですが、ディレクトリ構成的にどこに置くかは悩みました。
UseCase層に置くと境界線が際立つのですが、いまいちパッとしないんですよね・・・。Usecaseディレクトリにユースケース以外のクラスが置いてあるのが少し気持ち悪いと言うか・・・。
ということで今回は実装クラスとまとめてGateway層の「Driver」ディレクトリに突っ込んでおきました。
この辺も正解はないところだと思いますが…。皆さんは抽象はどこに置いているんでしょう?
作ってわかるイケてるポイント
単体テストの書きやすさ
依存がコントロールされているため、基本的に1つ下の(内側の)レイヤだけをモックするだけでテストができるため、考えるコストが少ないです。
差替可能
こういったシステムだと効果が実感しやすいと思いました。
例えば二酸化炭素濃度を測定するセンサーを変えたとしても、AbstractSensorクラスさえ実装すればこれまでのクラス群がそのまま使えます。
WebアプリでUIやDBを差し替えることはそうそうないと思いますが、IoTといった世界では差し替え可能のアーキテクチャは強みだと感じます。
このあと
次はフロント側も作ってみたいです。
Reactでクリーンアーキテクチャとか本当にできるのか・・・?と懐疑的なので、それらしい書籍などを見つけて実装してみたいなと思っています。