エラー処理

エラー種別の定義

モジュールでは、予想されるすべてのビジネス関連の条件や技術的な条件に対してエラー種別を定義して、ユーザーが明確に処理できるようにする​必要があります​。

次のような条件では、一致するエラー種別を定義す​べきです​。

  • ID で参照されたエンティティが存在しない

  • 口座に残高がない

  • ユーザーが認証されていない

  • ユーザーが特定のアクションを実行するための権限を持っていない

  • リモートシステムの容量が一杯である

次のような条件では、一致するエラー種別を定義す​べきではありません​。

  • ネットワークエラー

  • 予期しないサーバーエラー

  • 未知のエラー

エラー階層の使用

Mule 4 のエラー種別では、階層の概念をサポートしています。つまり、エラーは単体で定義することも、同じモジュールで定義されている別の ErrorType や、汎用 Mule エラーとして定義されている ErrorType の子として定義することもできます。

Mule 4 のエラー処理を活用することで、汎用エラーハンドラーを再利用して、一般的な問題に一貫した方法で取り組むことができます。たとえば、複数のコネクタを同じフローで使用し、接続の問題が発生した時点で特定のアラートをトリガーするフローがあるとします。そのフローのすべてのコネクタが、すべての接続の問題の親エラーとして MuleErrors.CONNECTIVITY を使用していれば、アラートのトリガー処理は簡単に定義できます。

内部のモジュールでも、同じ種類の内部関係を持つエラー種別を定義しているモジュールであれば、同じことが言えます。

WebSockets Connector の例を示します。

public enum WsError implements ErrorTypeDefinition<WsError> {

CONNECTIVITY(MuleErrors.CONNECTIVITY),
CLIENT_SECURITY(MuleErrors.CLIENT_SECURITY),
SERVER_SECURITY(MuleErrors.SERVER_SECURITY),
BASIC_AUTHENTICATION(SERVER_SECURITY),
UNAUTHORIZED(CLIENT_SECURITY),
FORBIDDEN(CLIENT_SECURITY),
NOT_FOUND,
SERVICE_UNAVAILABLE,
NO_SUCH_SOCKET,
INVALID_SOCKET_ID,
NON_UNIQUE_SOCKET_ID,
REMOTELY_CLOSED,
INTERNAL_SERVER_ERROR;

private ErrorTypeDefinition<? extends Enum<?>> parent;

WsError(ErrorTypeDefinition<? extends Enum<?>> parent) {
  this.parent = parent;
}

WsError() {}

@Override
	public Optional<ErrorTypeDefinition<? extends Enum<?>>> getParent() {
	  return Optional.ofNullable(parent);
	}
}

この例からは以下が見て取れます。

  • 親を使用してエラー種別を定義している

  • CONNECTIVITY エラーで Mule のエラーを拡張している

  • CLIENT_SECURITY​ エラーと ​SERVER_SECURITY​ エラーを定義している

  • BASIC_AUTHENTICATION​、FORBIDDEN、UNAUTHORIZED エラーで ​CLIENT_SECURITY​ と ​SERVER_SECURITY​ を拡張している

  • 他のドメイン特有のエラーが単体で存在している

モジュール例外の正しい使い方

ModuleException クラス (またはそのカスタムサブクラス) は、エラーが 定義されている ErrorType と一致した場合にのみスローされる​必要があります​。一致する ErrorType を持たない予期しないエラーや処理不能なエラーは、通常の例外としてスローされる​必要があります​。

エラー種別を定義し過ぎない

よくあるアンチパターンとして、「ビジネスエラーごとに 1 つのエラー種別」という概念に基づいて「すべてのエラー詳細に個別のエラー種別」を定義してしまうことがあります。たとえば、​INVALID_EMAIL​、​INVALID_ID​、​INVALID_NAME​、​INVALID_CARD​ のようにエラー種別を作り続けてしまい、エラー種別が爆発的に増えた結果、せっかくの機能が使えなくなってしまいます。

モジュールでは、同様のセマンティクスを持つエラー種別をまとめて、その代わりに例外メッセージを活用する​必要があります​。上の例では、​INVALID_DATA​ という 1 つの ErrorType のみを定義す​べき​であり、問題がどこにあったのか (クレジットカード、顧客名など) を例外メッセージの追加コンテキストで示します。

このルールは「エラーは処理可能でなければならない」という以前の概念と密接に関連しています。ID が不正であるというメッセージとメールアドレスが不正であるというメッセージを別々に受け取りたいユーザーはいないからです (Java で同じような経験を何度したことか考えてみましょう)。

例外やエラー種別は公開しない

モジュールで定義した Exception、ErrorTypeDefinition、ErrorTypeProvider クラスを API の一部としてエクスポートしては​なりません​。