エラーチェックの体系的な分類方法

まず最初のエントリでは、「エラーチェック」とひとくくりにされている「エラー」を、体系的に分類することを試みてみます。このエントリでは、Web / Windows、あるいは Java / .NET などといった技術論とは無関係な部分についての解説を進めていきたいと思います。

  • エラーチェック(ユーザ入力検証)の意味
  • 正常終了/業務エラー/システムエラーの分類
  • 業務エラーの細分化
  • アーキテクチャから見たエラーチェックの実装場所

※ なお、本エントリで解説されている分類方法や命名方法は、あくまで nakama 個人の考え方・整理方法です。もしかしたらもっとよい設計パターンなどがあるかもしれませんので、その辺についてはあしからずご了承ください;。

[エラーチェック(ユーザ入力検証)の意味]

まずは、そもそもどのようなケースでエラーチェックが必要になるのか、ユーザ入力検証にどのような目的があるのかを考えてみましょう。

一般的に、業務アプリケーションは、参照系アプリケーションと更新系アプリケーションに大別されます。

image_thumb[1]

参照系と更新系では、求められるアプリケーションの機能が大きく異なりますが、更新系アプリケーションでは、適切な入力エラーチェックをどのように実装するのかが、極めて重要なポイントになります。そもそもなぜ更新系アプリケーションでは、適切な入力エラーチェックが重要になるのか? ユーザ入力検証(エラーチェック)には、大別して以下の 2 つの目的があります。

  • アプリケーションの保護
    ユーザから入力された値をそのまま利用すると、エラーやセキュリティ脆弱性の原因になってしまうため、適切にフォーマットチェックなどを行う必要があります。(SQL 挿入、Cross-Site Scripting など)
  • ユーザビリティの向上
    エンドユーザに親切なエラーメッセージを表示するように作成すると、使いやすいアプリケーションを実現することができます。

image_thumb[5]

しかし、このようなユーザ入力検証機能(エラーチェック機能)を場当たり的に実装すると、開発生産性を大きく損なうことになります。このため、通常のアプリケーション開発では、ランタイムが持っている「ユーザ入力検証のための機能」をうまく活用して実装していくことが重要になるわけです。

こうしたランタイムのユーザ入力検証機能を利用する上で欠かせないのが、以下の 3 つのポイントに関する知識です。

  • アプリケーションの終了パターンの分類
    正常終了/業務エラー/システムエラーを正しく分類できること。
    業務エラーが、さらに単体入力エラーと突合せエラーに分類されることを知っていること。
  • アーキテクチャ的な観点から見た、エラーチェックの実装方法
    論理 3 階層型アプリケーションにおいて、どこで何をチェックすべきかを判断できること。
  • ランタイムが持つバリデーション機能(エラーチェック機能)の狙い
    ランタイムが持つバリデーション機能のコンセプトの違いを理解していること。
    どの部分をカバーする目的で作られているのか、どこまでできるのか、その限界点を理解していること。

本エントリでは、まずこのうち最初の、「エラーパターンの分類」の部分について解説します。

[正常終了/業務エラー/システムエラーの分類]

業務アプリケーションの「入力エラー」を考える上で欠かせないのが、ひとくくりに「エラー」とされているものを体系的に分類することです。これを考えるには、業務処理が終了するパターンを、以下の 3 つに分類するのが最初のスタートポイントになります。(※ この終了パターン分類は、nakama による勝手な分類で、業界標準でもなんでもありません;。あしからずご了承ください、とつぶやいてみる^^)

  • 正常終了 : 特に問題なく、期待通りに業務処理が終了できた
  • 業務エラー : ユーザ入力値の問題で、処理が完遂できなかった
  • システムエラー : システムトラブルで、処理が完遂できなかった

image

業務アプリケーションの振る舞いを、まずこの 3 通りに分類することは極めて重要なので、具体例を取り上げながら考えてみましょう。

例えば、下図のような、重複顧客 ID を認めていないような、新規顧客登録業務を考えてみます。この場合、ユーザが画面からデータを入力し、ボタンを押下した場合の終了パターンは

  • 正常に顧客情報を登録できたパターン → 正常終了
  • 指定された顧客 ID がすでに他で使われていたパターン → 業務エラー
  • データベースサーバに接続できなかった → システムエラー

の 3 通りに分類することができます。

image

この終了パターンの分類は、以下のように考えるとわかりやすいでしょう。一般的に、業務画面でボタンを押下した場合、その終了パターン(終了に応じた画面の表示方法)は、下図のように 3 通りに分けられます。この分類に当てはめれば、簡単に終了パターンを分類できると思います。

  • 正常終了して、当該画面内に黒文字でメッセージを表示する。→ 正常終了
  • 何かしらの問題があり、当該画面内に赤文字でメッセージを表示する。→ 業務エラー
  • 何かしらの問題があり、別画面の「ごめんなさい」メッセージ画面(誰かがお詫びしているような画面、イメージ的には下図みたいな感じ^^)を表示する。→ システムエラー

image

[業務エラーの細分化]

さて、上述した業務エラーは、実はさらに以下のように分類することができます。(なお、以下の業務エラーの分類も、nakama による勝手な分類です。名称も一般的なものではないのでご注意ください;。)

  • 単体入力エラー
    ユーザ入力値「のみ」で正誤判定ができるエラー。これはさらに、フィールド単位の入力エラー(個々のフィールドのデータだけで正誤判定できるエラー)と、インスタンス単位の入力エラー(複数のフィールドを同時に確認しないと正誤判定できないエラー)とに分類できる。
  • 突き合わせエラー
    ユーザ入力値のみでは正誤判定できず、データベース上のデータなど、他のデータとの「突き合わせ」を行わないと正誤判定ができないエラー。

前述の、正常終了/業務エラー/システムエラーとまとめて分類パターンを示すと、下図のようになります。

image

この分類の考え方や方法は、Web アプリケーション/Windows アプリケーションを問いません。Web アプリケーションと Windows アプリケーションでは、これらのエラーの表示方法(ユーザへの通知方法)や実装方法が異なりますが、分類方法そのものは、どちらであっても変わりがありません

そのことを示すために、以下のような、新規顧客登録画面(単票形式のデータ入力フォーム)を取り上げて、パターン分類をしてみましょう。

image

上記のような画面の場合、Web アプリケーション、Windows アプリケーションを問わず、ボタン押下に伴って発生する事象は、以下のように分類することができます。

image

このように、正常終了/業務エラー/システムエラーの分類方法は、アーキテクチャタイプ(Web アプリ/スマクラアプリ/Windows アプリ)などによって変化するわけではない、ということを覚えておいてください。

[アーキテクチャから見たエラーチェックの実装場所]

さて、前述の分類のうち、システムエラーに相当するものは、.NET では例外(Java の場合には実行時例外)と集約例外ハンドラを利用して実装します。この実装方法については、本 blog にイヤというほどしつこくまとめたので、以下のエントリをご覧ください。

簡単にいえば、.NET の場合には、システムエラーに相当するケースについては集約例外ハンドラでまとめて処理するため、個々のアプリケーションモジュール(UI/BC/DAC 部)にシステムエラーを後処理するロジックを実装する必要は(基本的には)ありません。(※ 例外処理を行うのは、フローチャートの流れを調整したい場合に限ります。これについては、上記の「.NET の例外処理 Part.2」に詳細に解説していますので、こちらを参照してください。)

では、前述した業務エラーの処理はどこに実装するべきでしょうか? こうした業務エラー処理を実装する際に、必ず守るべき、以下の 2 つの基本的な実装セオリーがあります。

  • 業務エラーのチェックは、可能な限り、ユーザに近い場所で行う
    エンドユーザにとって、「UI が即時反応すること」はユーザビリティ上重要です。このため、UI 部でできるチェックは必ず UI 部で行い、即座にエラーを表示するようにします。
  • 信頼境界の端点では、必ずデータの再チェックを行う
    信頼境界(Trust Boundary)とは、ネットワーク的に不正な攻撃を受ける危険性のある境界のことを指します。例えば、下図のような Web アプリケーションの場合、BC や DAC はリモートからの通信を受け付けるわけではないので、これらのモジュールが直接、不正なユーザから攻撃を受ける危険性はほとんどないと言えます。一方、UI 部分は、不正なユーザから攻撃を受ける可能性が極めて高いと言えます。このため、リモートからのネットワークアクセスを受け付ける場所では、受け取ったデータを必ず再チェックする必要があります

image

先に述べたように、業務エラーは以下の 2 つに大別できますが、このうち、突き合わせエラーについては、UI 部のみで正誤判定することはできません。このため、突き合わせエラーは、バックエンドの BC や DAC と連携してエラーチェックを行うことになります。

  • 単体入力チェック → UI 部のみでチェックが可能
  • 突き合わせ入力チェック → BC や DAC と連携したチェックが必要

このことを踏まえ、業務エラー実装の基本セオリーを、論理 3 階層型の Web アプリケーションと、スマートクライアントアプリケーションに適用すると、業務エラーのチェック場所は以下のようになります。

A. Web アプリケーションにおける業務エラーの実装場所

まず、Web アプリケーションの場合には、単体入力チェックや突き合わせチェックを以下のように実装することになります。

image

  • 単体入力チェックについて
    ブラウザ上で JavaScript による単体入力チェックを実装するとともに、サーバ側の UI 部にも、単体入力の再チェックロジックを実装します。すべてのブラウザが JavaScript をサポートしているわけではないため、ブラウザ内(JavaScript)で単体入力エラーが発見された場合だけでなく、UI 部(*.aspx など)で単体入力エラーが発見された場合も、エラーメッセージを表示するように設計・実装します。このため、単体入力チェックはブラウザ内(JavaScript)、サーバサイド(UI 部)の二か所に、重複する形で実装します。(※ なお、ASP.NET の場合、単体入力チェックは、UI 部(*.aspx 上)に検証コントロール(バリデータ)を使って実装します。検証コントロールはクライアントブラウザ上でのチェックのために JavaScript を自動出力するようになっています。このため、*.aspx 上に一度貼り付けただけで、ブラウザ上でのチェック(ユーザの手元での即時チェック)と、サーバ側での信頼境界端点の再チェックの両方をしてくれることになり、開発生産性が大きく向上することになります。)
  • 突合せ入力チェックについて
    突合せ入力チェックに関しては、BC, DAC 部で、業務処理の一部として実装します。具体的には、BC から UI 部に対して、業務エラー情報として返し、UI 部ではエラーラベルに表示を行うことになります。

※ なお、ASP.NET 検証コントロールを利用した単体入力チェックの手法については、拙著「Visual Studio 2005 による Web アプリケーション構築技法」の第 5 章「入力検証コントロール」を、また突き合わせ入力チェックの実装方法については、本 blog の 「.NET の例外処理 Part.1」の中の、「2 種類の業務エラー」と「具体的なシグネチャ設計例」の項を参照してください。

前述の例の場合、画面設計と実装コードは、おおまかに以下のようになります。(フルソースコードは、サンプルコードを参照してください。ここでは要点だけ示します。)

  • UI 部の設計
    image
  • UI 部 → BC 部呼び出しの部分の処理コード
    image

B. スマートクライアントアプリケーションにおける業務エラーの実装場所

一方、スマートクライアントアプリケーションの場合には、単体入力チェックや突き合わせチェックを以下のように実装することになります。(※ この実装パターンは、Silverlight などの RIA タイプのアプリケーションでも同じになります。)

image 

  • 単体入力チェックについて
    UI 部に、双方向データバインドを使って実装します。また、SI 部(サービスインタフェース、具体的には XML Web サービス)が信頼境界端点になるため、SI 部にも単体入力チェックを重複実装する必要があります。ただし、先の Web アプリケーションの場合と異なり、SI 部で単体入力エラーが発見された場合にはシステムエラー扱いとします。これは、クライアント側で正しい UI アプリケーション(Windows フォームや WPF アプリケーションなど)を使っている場合、SI 部に単体入力エラーを含むデータが送られてくるということはあり得ないからです。
  • 突合せ入力チェックについて
    BC, DAC 部で、業務処理の一部として実装します。そして、SI から UI 部に対して、業務エラー情報として返し、UI 部ではエラーラベルに表示を行います。

前述の例の場合、画面設計と実装コードは、おおまかに以下のようになります。(フルソースコードは、サンプルコードを参照してください。ここでは要点だけ示します。また UI 部の詳細な実装は、さらに 2 パターンに細分化できるのですが、これについては Part 2 のエントリで解説します。ここでは「なんとなく」理解していただければ十分です。)

  • UI 部の設計
    image
  • UI 部 → SI 部呼び出しの部分の処理コード
     image

上記の解説をまとめると、以下のようになります。

image

このように、業務エラーのチェックを実装する場所・方法に関しては、守るべき基本セオリーが存在します。実装方式が異なっても、これらの基本セオリーは変化しないものですので、しっかり覚えておくようにしてください。

[本エントリのまとめ]

では、ここまでの解説をいったんまとめておくことにします。

  • アプリケーションの終了パターンは、正常終了/業務エラー/システムエラーの 3 パターンに分類される。
  • システムエラーは、例外と集約例外ハンドラによって処理する。このため、通常のアプリケーションコードの中には、システムエラーに関する処理コードは出てこない。(=例外をむやみに try-catch したり throw したりすることはしない。)
  • 業務エラーは、単体入力エラーと突き合わせエラーに細分化される。単体入力エラーは、さらに、フィールド単位の入力エラーと、インスタンス単位の入力エラーに細分化される。
  • アプリケーションアーキテクチャ的な観点から見た場合、業務エラーチェック(単体入力チェックと突き合わせ入力チェック)を行う場所には、基本セオリーが存在する。

アプリケーションの終了パターン分類

image

Web アプリケーションにおける業務エラーチェックの実装場所

image

スマートクライアントにおける業務エラーチェックの実装場所

image 

さて、ここまではアプリケーションの一般的な設計原則論のようなものを解説しましたが、これらを実装する際には、ランタイムが持つ入力検証機能を活用していくことが望ましいと言えます。引き続き Part 2. では、これらのデータ入力チェックを、各ランタイムでどのように実装していくのかを解説し、それを通して、各ランタイムが持つ入力検証機能を比較してみることにしましょう。