完全なプログラムを設計および作成する方法

「機能的なアプリケーションを作成するための手順」、パート1。



「基本的なレベルで関数型プログラミングを理解しているようで、簡単なプログラムを作成したように思えますが、実際のデータ、エラー処理などを備えた本格的なアプリケーションを作成するにはどうすればよいですか?」



これは非常によくある質問なので、このシリーズの記事では、設計、検証、エラー処理、永続性、依存関係管理、コード編成などをカバーする手順を説明することにしました。



まず、いくつかのコメントと注意事項:



復習



この一連の記事で説明する予定の概要:



さあ始めましょう



非常に単純な例を見てみましょう。つまり、Webサービスを通じて顧客情報を更新します。



そして、私たちの基本的な要件:



これは一般的なデータ処理シナリオです。 スクリプトを実行する特定のリクエストがあり、その後、リクエストからのデータがシステムを「流れ」、各ステップで処理されます。 このスクリプトは、エンタープライズソフトウェアで一般的であるため、例として使用します。



プロセスのコンポーネントの図は次のとおりです。





ただし、この説明はイベントの成功バージョンにすぎません。 現実は決して単純ではありません! ユーザーIDがデータベースに見つからない場合、郵送先住所が正しくない場合、またはデータベースにエラーがある場合はどうなりますか?



チャートを変更して、問題が発生する可能性のあるすべてのものに注意しましょう。





ご覧のとおり、さまざまな理由でスクリプトの各ステップでエラーが発生する場合があります。 この一連の記事の目標の1つは、エラーをエレガントに管理する方法を説明することです。



機能的思考



シナリオの手順を理解したので、機能的なアプローチを使用してそれを実装する方法は?



最初に、元のシナリオと機能的思考の違いを見てみましょう。



シナリオでは、通常、要求/応答モデルを意味します。 要求が送信され、応答が返されます。 何かがうまくいかなかった場合、アクションのフローは終了し、答えは「スケジュールより先」になります(翻訳者のメモ:これはプロセスに関するものであり、費やされた時間に関するものではありません)。



私が意味することは、スクリプトの単純化されたバージョンの図で見ることができます。





しかし、機能モデルでは、関数は次のように入力と出力のあるブラックボックスです。





このようなモデルにシナリオをどのように適合させることができますか?



一方向の流れ



まず、データの機能フローが前方にのみ広がっていることを認識する必要があります。 「スケジュールより先」を返すことはできません。



私たちの場合、これはすべてのエラーがスクリプトの終了前に代替パスに沿って送信されなければならないことを意味します。





これを行うとすぐに、ストリーム全体を単一の機能(ブラックボックス)に変えることができます。





もちろん、この大きな関数の内部を見ると、スクリプトの各ステージに1つ、互いに直列に接続された小さな関数(機能的方法論では「構成」)で構成されていることがわかります。





エラー管理



最後の図は、1つの正常終了と3つのエラー出力を示しています。 関数は4つではなく1つの出力しか持つことができないため、これは問題です。



それについて何ができますか?



答えは、各オプションが可能な出力の1つを表すUnionタイプを使用することです。 その場合、関数には実際には1つの出力しかありません。



結果を出力するための可能な型定義の例を次に示します。

type UseCaseResult =    | Success    | ValidationError    | UpdateError    | SmtpError
      
      





そして、次の4つの異なるオプションが含まれる単一の出力を示すやり直しチャートです。





エラー管理を簡素化



これで問題は解決しますが、各ステップのエラーの存在は脆弱であり、再利用設計にはあまり適していません。 もっと良くできますか?



はい! 本当に必要なのは2つの方法だけです。 1つは成功例であり、もう1つはすべてエラーの場合です。

 type UseCaseResult =   | Success   | Failure
      
      









このタイプは非常に用途が広く、どのプロセスでも動作します! 実際、このタイプで作業するために、あらゆるシナリオに適した便利な関数の優れたライブラリを作成できることがすぐにわかります。



別のポイント-結果として、関数が返す結果、データはまったくなく、成功/失敗ステータスのみがあります。 関数の結果に実際の成功または失敗したオブジェクトが含まれるように、何かを修正する必要があります。 成功した型と失敗した型をユニバーサルとして宣言します(型パラメーターを使用)。



最後に、最終的なユニバーサルバージョン:

 type Result<'TSuccess,'TFailure> =   | Success of 'TSuccess   | Failure of 'TFailure
      
      





実際、F#ライブラリにはすでに同様のタイプがあります。 Choiceと呼ばれます。 わかりやすくするために、この記事と以降の記事で以前に作成した結果タイプを引き続き使用します。 より深刻なタスクに近づくと、この問題に戻ります。



ここで、スクリプトを個々のステップでもう一度見てみると、各ステップのエラーを単一の「不良」パスに結合する必要があることがわかります。





これを行う方法は、次の記事のトピックです。



まとめとガイドライン



そのため、命令には次の規定があります。



ガイドライン




All Articles