コーディングの原則

以下の原則は、モジュールまたはコネクタのすべてのコンポーネントに適用されます。これらは、すべての操作、配信元、設定、接続プロバイダー、関数に適用されます。

コネクタは配信先システムを正確に反映する必要がある

Anypoint Platform のコンテキストで言えば、コネクタの目的は配信先システムを Mule 言語に翻訳することです。コネクタは、高レベルの抽象表現を追加したり、特定のビジネスユースケースを実装する操作を含めたりしては​なりません​。

たとえば、請求書を作成し、請求書に項目を追加して、請求書を送信するためのリソースを持つ請求 API があるとします。このような API のコネクタは、これらのリソースのそれぞれに対して操作を 1 つずつ含む​必要があります​が、3 つのコールを 1 つに組み合わせる操作を含んでは​なりません​。

例外としては自動ページング操作があり、ページング効果を実現するために、単一の Mule 操作からの出力の処理において、複数のコールを実行します。

プロトコルを混在させない

1 つのコネクタは、同時に 1 種類のプロトコルにのみ接続する​必要があります​。たとえば、1 つの外部システムが REST API、SOAP API、そして OData API を公開するとします。

これらは同じ外部システムにアクセスしていますが、Anypoint Platform では 3 つの異なる API が存在することになります。そのため、1 つのコネクタで 3 種類のプロトコルを処理するのではなく、それぞれの API に対して別々のコネクタを用意する​必要があります​。

エクスポートされたモジュール Java API

このセクションを読む前に、Mule SDK の​「クラスローディング分離」​を参照してください。

モジュールは、モジュールで定義されたパッケージのみをエクスポートする​必要があります​。連動関係ライブラリからのパッケージをエクスポートしては​なりません​。

Java パッケージ

モジュールは JDK からのパッケージをエクスポートせずにコンシュームするだけであるため、これらのパッケージは扱いが異なります。Mule は、使用するモジュールで使用できるすべての Java パッケージを自動的にエクスポートします。

ですが、モジュールは、そのモジュールと互換性のある Mule バージョンでサポートされているすべての JDK で使用できる Java パッケージのみを使用する​必要があります​。com.sun などのベンダー固有のパッケージを使用しては​なりません​。

Java 11 との互換性

Mule 4.2.0 からは JDK 11 もサポートされるようになりました。そのため、ネイティブ Java API に依存しているモジュールでは、必要な API が Java の新しいモジュール型アーキテクチャ (Java 9 で初めて登場した Project Jigsaw​) で使用できない場合があるという事実を考慮する​必要があります​。

たとえば、モジュールが Java トランザクション API (JTA) を必要とする場合、そのモジュールは pom.xml ファイルで次の依存関係を宣言する​必要があります​。

<dependency>
  <groupId>javax.transaction</groupId>
  <artifactId>javax.transaction-api</artifactId>
  <version>1.2</version>
</dependency>

Java Mail API を使用する場合は次のように宣言します。

<dependency>
  <groupId>javax.mail</groupId>
  <artifactId>javax.mail-api</artifactId>
  <version>1.6.2</version>
</dependency>

スレッドセーフなデザイン

Mule は、同時性の高いワークロード向けにデザインされたリアクティブ実行エンジンです。そのため、すべてのコンポーネントは、最小限の競合で同時実行をサポートするように常にデザインする​必要があります​。

スレッドセーフ​は、同時実行を扱い慣れた Java 開発者にとっては基本的な概念です。SDK のユーザーも、この概念に慣れておくことが期待されます。

不変のペイロードと属性オブジェクトを使用する

ペイロードまたはメッセージ属性として使用されるすべての出力データ型は、不変のオブジェクトである​必要があります​。

コンテンツパラメーターの正しい使い方

@Content パラメーターは、MuleSoft ​「コンテンツパラメーター」​ドキュメントに従って正しく使用してください。

フロー変数にはアクセスしない

Mule 4 では、変数にはエンドユーザーのみがアクセスできます。モジュールから直接フロー変数にアクセスす​べきではありません​。

SDK にはフロー変数にアクセスするための API は用意されていませんが、省略可能なパラメーターを利用することでこの制約を破ることができます。次に例を示します。

@Optional(defaultValue = "#[vars.foo]") String foo

@Optional(defaultValue = "#[vars]") Map<String, TypedValue<Object>> vars

ただし、これらのステートメント (およびモジュール内から vars にアクセスする他の手段) は禁止されています。モジュールで必要とされるすべての情報は、パラメーターで明確に提供する​べきです​。変数に値を割り当てるかどうかはエンドユーザーが決めることです。

このルールに違反すると、位置の独立性にも違反することになります。

メッセージ属性をパラメーターとして使用しない

コンポーネントでは、属性オブジェクトが同じモジュールの一部として定義されている場合であっても、メッセージ属性としても使用される種別のパラメーターを定義す​べきではありません​。

たとえば、コネクタに LocalFileAttributes クラスを出力属性として使用する操作がある場合、次の定義は不正です。

public void checkAccess(@Content LocalFileAttributes attributes) {
        doCheckAccess(attributes);
}

その代わりに、実際に必要な属性クラスからの情報を反映したパラメーターを追加してください。

public void checkAccess(String path) {
 ...
}

正しいエンコードを使用する

複数のエンコードを受け入れるシステムに接続する場合は、使用するエンコードを​設定の上書き​によって設定可能にする​必要があります​。つまり、使用するエンコードを設定レベルだけではなく、エンコードを必要とする各コンポーネント (操作、配信元、関数など) でもパラメーター化可能とす​べきです​。

設定レベルでは、エンコードパラメーターのデフォルトを、@DefaultEncoding アノテーションで取得できる Mule のデフォルトエンコードとする​必要があります​。

次に例を示します。

public class MyConfig implements Initialisable {

        @DefaultEncoding
        private String defaultEncoding;

        @Parameter
        @Optional
        private String encoding;

        public void initialise() throws InitialisationException {
                if (encoding == null) {
                        encoding = defaultEncoding;
                }
        }

        public String getEncoding() {
                return encoding;
        }

}

期間パラメーター

多くのモジュールでは、タイムアウトやキャッシュエビクションといった期間を設定する必要があります。

期間は、2 つのパラメーターの組み合わせとして表現する​必要があります​。

  • Timeout サフィックスの付いた int (整数) スカラーパラメーター

  • スカラーを限定する java.util.concurrent.TimeUnit パラメーター

    このパラメーターの名前は、対応するスカラーと完全に同じ名前に TimeUnit サフィックスを付けたものである​必要があります​。TimeUnit パラメーターのデフォルトは SECONDS にす​べきです​。

次に例を示します。

/**
* The socket connection timeout value. This attribute works in tandem with {@link #connectionTimeoutUnit}.
*/
@Parameter
@Optional(defaultValue = "5")
@Placement(tab = ADVANCED_TAB, order = 1)
@Summary("Socket connection timeout value")
private int connectionTimeout;

/**
* A {@link TimeUnit} that qualifies the {@link #connectionTimeout}
*/

@Parameter
@Optional(defaultValue = "SECONDS")
@Placement(tab = ADVANCED_TAB, order = 2)
@Summary("Time unit to be used in the Timeout configurations")
private TimeUnit connectionTimeoutUnit;

タイムアウトには設定可能な設定の上書きが必須

タイムアウトの影響を受ける操作を実行するすべてのコンポーネントでは、パラメーターの​設定の上書き​によってタイムアウトを設定できるようにする​必要があります​。

これらのタイムアウトは、期間パラメーターとして表現する​必要があります​。

プライマリコンテンツのみのデフォルトをペイロードにすべき

プライマリコンテンツ以外のパラメーターのデフォルトをペイロードにす​べきではありません​。つまり、以下の定義を使用す​べきではありません​。

@Optional(defaultValue = "#[payload]")

日付パラメーターと時刻パラメーターの処理

日付と時刻のパラメーターは、java.time.LocalDate 種別、java.time.LocalDateTime、または java.time.ZonedDateTime 種別を使用して実装する​必要があります​。java.util.Date や java.util.Calendar は使用しないでください。

エンドツーエンドのエクスペリエンスの確認

モジュールをパブリッシュする前に、Anypoint Studio と Anypoint Design Center でモジュールをテストします。使い勝手とエクスペリエンスが優れていることを確認してください。正常に機能しても使い勝手が悪ければ、そのモジュールは不完全です。

以下を確認する​必要があります​。

  • すべての入力パラメーターと出力値について DataSense が正しく解決される。

  • 各コンポーネントのパラメーターの​レイアウト​が正しい。

  • 表示ラベルや説明にタイプミスがない。

  • モジュールの主なユースケースを使用するアプリケーションをユーザーが簡単にビルドできる。

POJO はできるだけ使用しない

SDK では、入力パラメーターと出力データ型のどちらでも POJO を使用できます。ですが、これらの使用はお勧めできません。すべてのモジュールでは、JSON または XML を使用するように工夫す​べき​です。動的データ型の場合は、Java マップも使用できます。

このルールは、POJO を使用するユースケースが存在しない、あるいはすべての構造を JSON または Java マップに変換する必要があるという意味ではありません。このルールは単に、他のコンポーネントとの相互運用性が高く、逐次化の問題を回避できるデータ形式を使用した方がよいという意味です。

たとえば、Mule アプリケーションで活用するための Java 税金ライブラリを組織で開発したとします。このライブラリが POJO を返す場合は、このライブラリをラップする SDK モジュールをビルドし、その税金モジュールが POJO を受け入れて POJO を生成することになっても問題はありません。

一方、JSON を返す税金 API を利用する場合には、税金コネクタはその JSON を POJO に変換するのではなく、API と同じ JSON を返す​べきです​。

POJO の公開

公開されるすべての POJO は (出力データ型、入力パラメーター、メッセージ属性として使用されるため) DataWeave に準拠する​必要があります​。つまり、@Parameter アノテーションが付けられた各項目に対して、デフォルトのコンストラクターと getter メソッドが必要です。

ユーザーに Java 型を意識させてはならない

ユーザーは、モジュールを実装するために使用されている Java 型を知らなくてもモジュールを使用できる​必要があります​。

たとえば、次の変換を考えてみましょう。

{
  name: "Pedro",
  address: {
           x: 11,
         y: 12
  }
}  as com.foo.Person

このモジュールは、このような明確な変換が必要ないような方法で定義される​必要があります​。これを実現するには次のような方法があります。

  • POJO を使用して個人を表現しない (推奨)

  • com.foo.Person を DataWeave に準拠した具体的なクラスにする

  • 入力パラメーターとして使用する場合は、抽象型やインターフェースを使用するのではなく、com.foo.Person を明確に参照する

ペイロードと属性は逐次化可能でなければならない

操作またはメッセージ配信元から返されるメッセージ属性オブジェクトは、Java で逐次化可能である​必要があります​。そうでなければ、ObjectStore、VM キュー、またはクラスターモードの Mule では使用できません。

MuleContext にはアクセスしない

MuleContext は非推奨であるため、どのモジュールやコネクタからもアクセスしては​なりません​。

ほとんどの場合、モジュールが MuleContext にアクセスする理由は特定の Runtime サービスを取得するためです。次に例を示します。

muleContext.getRegistry().lookupByType(ObjectStoreManager.class)

その代わりに、@Inject を使用してこれらのサービスを取得します。

public class MyComponent {

@Inject
ObjectStoreManager osm;
}

SLF4J によるログ

すべてのログは、SLF4J API を使用して記録する​必要があります​。

この API との連動関係は MuleSoft が提供する親 pom ファイルで定義されているため、モジュールでは連動関係を定義しては​なりません​。

すべてのロガーは静的でなければならない

ロガーの作成にはリソースを大量に消費します。そのため、すべてのロガーは、非公開、静的、そして最終版である​必要があります​。

次に例を示します。

public class Operation {

        private static final Logger LOGGER = LoggerFactory.getLogger(Operation.class);

        ...

}

静的状態を避ける

すべてのモジュールクラスは、静的状態を含んでいては​なりません​。例外は、ロガーと Serializable クラスの serialVersionUID のみです。

SDK バージョンの選択

通常、開発者は特定の製品で使用可能な最新バージョンを使用する傾向にあります。これは SDK の場合には適用す​べきではありません​。Mule を拡張するには、次の手順を実行します。

  • 必要な機能を特定する。

  • 必要な機能が含まれる下位バージョンを選択します。

適切な SDK バージョンの選択についての詳細は、​「SDK バージョンの選択」​を参照してください。

ボックス入力のブールは使用しない

ブールパラメーターは、ネイティブブール型である​必要があります​。ボックス入力のブールはパラメーターの型としては使用できません。

すべてのブールパラメーターは省略可能とみなされ、デフォルトは false である​必要があります​。@Optional アノテーションを使用して、ブールパラメーターのデフォルトを変更できますが、必須パラメーターにすることはできません。

リストパラメーターでは複数形の名前を使用する

Java コレクション、マップ、配列、または何らかの配列 (JSON 配列など) を表す他の型として実装されているすべてのパラメーターには、複数形の名前を付ける​必要があります​。

Semver Versioning の使用

モジュールは、​ Semantic Versioning​ 標準を使用してバージョン設定する​必要があります​。

厳格なキャメルケースの使用

モジュールのソースコード全体では、キャメルケースを正しく適用する​必要があります​。そうしないと SDK が XML DSL の生成やレイアウトの処理を正しく行えなくなります。