Kotlin + DDD × クリーンアーキテクチャで作るマイクラ用のサーバーリスト
クライアントサイドのアプリケーション層の実装や、Jetpack Composeのコンポーネント(UI)が含まれる。
View層はInteractor等の実装に依存してはイケナイため、おそらくUIは別のサブプロジェクトで記述した方が管理がしやすい。
Kotlin/JSとJetpack Compose for Webで作るSPAプロジェクト。
compose.material
がfor Webに対応するまでは、/clientCommon
とは別にこのプロジェクト内で全てのコンポーネントを作成する必要がある。
現状フロントエンド・バックエンドの両方でクリーンアーキテクチャの採用は難しいという結論に至る。
-
サーバーサイドでは全てのユーザーに対してUseCaseが使われるため、IDによってUseCaseがユーザーを識別する必要がある。 クライアントサイドではCookieなどを使って誰であるかを表現するため、UseCaseでIDを使うべきではない。 つまり、UseCaseの引数の統一が困難である。
-
また、サーバーサイドとクライアントサイドで必要となるUseCaseやUseCaseのパッケージの場所(CQRSを適用している場合)が異なる。
Note
|
本プロジェクトでは、:application サブプロジェクトでサーバーサイドとクライアントサイドのアプリケーション層のコードを管理している。そこではUseCase本体の共通化はしていないものの、Entityは共通化しているためある程度の冗長性の削減は達成できている。それらのEntityはGraphQLを介して共有されるため、スキーマの管理さえ徹底していれば型の安全は保証される。
|
サーバーサイドでの厳密なクリーンアーキテクチャは、フレームワークが対応していない限りは実装が困難である。
本プロジェクトで使用しているKtorを含め、ほとんどのWebフレームワークは一つの関数でリクエストを処理している。そのため、関数が終了された時点でレスポンスを返し終えている必要があり、Controller→PresenterとFlowが定義されているThe Clean Architecture
のサーバーサイドでの適用は困難を極める。
ここからは、本プロジェクトが真のサーバーサイドクリーンアーキテクチャに近づくために実装した手法を紹介する。
基本的に上記のフレームワークの性質から逃れられないので、suspendCoroutine()
を使ってControllerでPresenterの完了を待機することで、ControllerとPresenterに分離したままControllerが持つハンドラが終了するまでにレスポンスを返却することを実現した。Controllerの責務が若干肥大化するが、UseCaseInputPortがControllerに直接値を返す、いわゆる「なんちゃってクリーンアーキテクチャ」は回避することができたと思われる。
サーバーサイドクリーンアーキテクチャを実現するためにさまざまな試行錯誤を重ねてきたが、実際はより使いやすいアーキテクチャの適用、またはクリーンアーキテクチャと相性の良いWebフレームワークを用意するのが賢い選択になる。アーキテクチャが意味もなく開発を阻害しては本末転倒であることを改めて実感することができた。
MVCフレームワークでは実装に難がある為、サーバーサイドではより簡易なオニオンアーキテクチャを採用する。(commit: 79a29718d1ed879e4aab949ee98f55c9ce1a0873)
Presenterの運用が比較的難しい印象を受ける。ViewはPresenterに内包されている訳ではなく、ReactやJetpack Compose上ではあくまで対等の依存関係となり、PresenterからViewへの操作で従来のやり方とは若干異なる。
ViewModelを介さない独立したPresenterクラスの作成はViewの操作に手間がかかるため、OutputPortを無名オブジェクトかFun Interfaceを使って外部の変数をキャプチャすると記述が楽になる。
ViewModelを介した操作も、状態の変更のみではなく直接Viewを操作する場合で課題が残るので、今後解消していきたい。
フロントエンドでも、状態管理ライブラリ等に剃ったアーキテクチャを採用するべき。