概要
バックエンドとフロントを同時に開発し、同じAPI契約とタイプを二度書くボトルネックを解消し、ルーター・ハンドラー・MongoDBフィールド文字列まで繰り返される開発作業をソースベース生成に変える背景と原則を説明します。
起源の物語
私はバックエンドとフロントを一緒に作業しています。1つの機能を作成する際に、Java WebFluxでendpointとrequest/response DTOを作成し、再度フロントで同じURLとTypeScriptタイプを作成しなければなりませんでした。Swaggerドキュメントまで直接合わせるには、すでにバックエンドに存在する情報を別の形式で一度移す作業が繰り返されました。
最大のボトルネックは、バックエンドで作成したAPI契約をフロントが使用できる形に再作成するプロセスでした。URL、HTTPメソッド、path/queryパラメータ、request/response構造のいずれかが両方で異なって修正されると、コンパイルより遅く問題を発見しました。単に面倒な作業を超えて、同じ内容を人が二度管理することで生じる不一致がより大きな問題でした。
RouterFunctionとhandlerを新たに作成する際の繰り返しも減らしたいと思いました。ルーターにApiAccountHandler::searchのような参照を追加した場合、存在しないhandlerクラスとメソッドの骨格程度は自動的に作成されても良いと考えました。MongoDBクエリを作成する際にentityフィールド名を"username"のように文字列で繰り返すのも面倒で、タイプミスが起こりやすかったため、entityソースを読み取ってJava enumを生成する機能も同じツール内に組み込みました。
そのようにSwagger/OpenAPI生成、handler骨格生成、Mongo entityフィールドenum生成が最初に作成され、その後RSocketを使用しながらAsyncAPI生成も追加しました。それぞれ別のアイデアのように見えますが、出発点は同じです。バックエンドソースにすでに書かれた事実を人が再度書かず、機械が読める部分は開発ツールが代わりに作成することです。
開発哲学
バックエンドソースを基準とします
RouterFunction、handler、DTOとentityにすでにある情報をAPIドキュメントと生成コードの原本として使用します。同じ契約を別ファイルで再管理しないことが重要です。
ランタイムマジックより開発時点の自動化を選択します
productionリクエストを傍受するフレームワークではなく、local開発環境でソースを分析し、実際のファイルを生成します。結果を目で確認し、バージョン管理できます。
プロジェクトの慣習を予測可能に従います
すべてのJavaコードを理解する汎用コンパイラを目指しません。私が実際に使用するWebFlux functional endpoint構造を明確なルールで分析する方を優先します。
繰り返される接続区間をなくします
バックエンドendpointでSwaggerを作成し、フロント生成器がその文書を読み取ってserviceとtypeを作成する流れまで1つの自動化チェーンでつなげます。
バックエンドで何を読むか
RouterFunctionソース
HTTPメソッド、ネストされたパス、ハンドラーメソッド参照と述語を読み取ります。
ハンドラーソース
リクエストボディ、クエリ/パス値とレスポンスパブリッシャータイプを読み取ります。
リクエスト/レスポンスDTO
バックエンドに既に作成したJavaタイプをOpenAPIスキーマに接続します。
Mongoエンティティソース
Javaフィールドと@Fieldのストレージ生名、@Documentコレクションを読み取ります。
RSocketコントローラー
@MessageMappingルートとリクエスト/レスポンスペイロードタイプを読み取ります。
繰り返し作成の代わりに何を作るか
swagger.json
RESTエンドポイントをフロントサービスとTypeScriptタイプ生成器が使用できるAPI契約にします。
asyncapi-rsocket.json
RSocketルートとペイロードをフロントRSocketクライアント生成器が読み取れる契約にします。
ハンドラーソース
RouterFunctionに最初に記載したハンドラ参照を基にクラスとメソッドの骨組みを生成・補正します。
{Entity}Fields enum
文字列フィールド名を繰り返さないようにエンティティのJavaフィールドとストレージ生名をenumにします。
CollectionNames enum
@Documentに宣言されたコレクション名を文字列の代わりに使用できるように生成します。
現在のプロジェクトでの自動化フロー
• バックエンドでRouterFunction、ハンドラーとJavaリクエスト/レスポンスDTOを作成します。
• ローカルプロファイルのウォッチャーがソース変更を検知してswagger.jsonまたはasyncapi-rsocket.jsonを更新します。
• フロントの@byeolnaerim/typed-rx-http生成スクリプトが文書を読み取りサービス関数とTypeScriptタイプを生成します。
• 画面コードではURLとレスポンスタイプを再記述せずに生成された関数をインポートして使用します。
• エンティティが変更されると、クエリに使用するフィールドenumとコレクションenumも更新します。