イントロダクション

マイクロ・フロントエンドアーキテクチャを構築するには、いくつかの選択肢があります。

  1. サーバーサイド - これは、異なるURLにロードされたページの古典的なアプローチです。それぞれのURLで、(別々のウェブサーバー上であっても)異なるチームのユニークなフロントエンドを提供することができます。これは、疎結合であり、独立して開発でき、別々にデプロイできるので、マイクロ・フロントエンドのすべての要件をすでに満たしています。しかし、このアプローチを使う場合、例えば異なるマイクロ・フロントエンド間のコミュニケーションや、他のフロントエンド間で同様のルックアンドフィールを確保するなどの課題があります。

Things Cloudではすでにアプリケーションホスティング経由でマイクロ・フロントエンドを使うことができます。アプリケーションAPIを介して異なるフロントエンドをデプロイし、アプリケーションスイッチャーを介して切り替えることができます。Web SDKを使用すれば、すべてのアプリケーションで同じルックアンドフィールが保証されます。

  1. コンパイル時 - マイクロ・フロントエンド・アーキテクチャを可能にするもう一つのオプションは、マイクロ・フロントエンドをライブラリとしてバンドルしてビルドし、(例えばnpm経由で)提供することです。パッケージは、新しいアプリケーションをコンパイルまたはビルドするために使うことができます。開発者が完全にコントロールできるので、これには多くの利点がありコミュニケーションを明確に定義し、ルックアンドフィールを揃えることができます。しかし、コンポーネントの1つが変わるたびに新しいビルドが必要になり、結合はより緊密になります。

Things Cloudはこのアプローチを使えるようにするさまざまなnpmモジュールを提供しています。例えば、APIクライアント、スタイリングされたデフォルトアプリケーションのみを使用したり、Things CloudコンポーネントライブラリからAngularモジュールとして異なる機能をインポートしたりできます。提供されているすべてのライブラリを見るには、Things Cloud npmオプション をご覧ください。

  1. ランタイム - アプリケーションの実行中に、異なるフロントエンドを動的に提供します。iframeでアプリケーションの異なる部分をロードしたり、異なるサーバーからスクリプトバンドルだけを読み込むことができます。しかし、例えばiframeとの通信や結合は、サーバーサイドの統合を使うときと同じくらい大変です。そのため、ランタイム統合はより複雑です。アプリケーションに深く差し込むことができ、例えば通信やステートレイヤを共有することができます。モジュールフェデレーションのような新しいテクノロジーは、特定の依存関係を共有し、そのスコープを定義することを可能にします。

新しいプラグインの概念が Things Cloud に導入されました。これにより、実行時に Web SDK ベースのアプリケーションを拡張できるようになります。

Things Cloudはすでにマイクロ・フロントエンドアーキテクチャに基づいています。実際、サーバーサイド・オプションは当初からのプラットフォームの概念です。コンパイル時オプションは2018年に導入されました。しかし、アプリケーションのランタイム拡張のニーズもあり、それがThings Cloudに導入された理由です。詳細は以下のセクションをご覧ください。

プラグインの紹介 プラットフォーム・ウェブ・アプリケーションを動的に拡張

プラグインは、実行時に動的に機能をロードし、Web SDKベースの Web アプリケーションを拡張するための新しい概念です。アプリケーションを拡張するには以下の手順をご覧ください。

  1. 管理アプリケーションで、拡張したいアプリケーションをクローンする(例:Cockpitアプリケーションをクローン)
  2. コックピットアプリケーションの詳細を開き、プラグインタブをクリックする
  3. プラグインのインストールをクリックし、プラグインを選択する
  4. 選択したプラグインによってアプリケーションが拡張される

これは基本的にスクリプト・インジェクションです。アプリケーションはプラグインからremoteEntry.jsというスクリプトをリクエストします。マイクロ・フロントエンドに関して言えば、呼び出しを実行するアプリケーションは"shell"と呼ばれ、そのスコープに"remote"を注入します。

ヒント
プラグインを含むアプリケーションで何らかの問題が発生した場合、?noPlugin=trueクエリパラメーターですべてのプラグインを除外することができます。

これらのプラグインは、Web SDKに統合されているあらゆるコンセプト概念を使用することができます。デバイス上に特定のボタンを追加するだけのものから、独自のナビゲータ・ノード、ルート、コンポーネントを持つ完全な機能セットまであります。多くのオプションがありますが、それらはすべてWeb SDKの枠内にあります。Things Cloudは、ReactやVue等などはサポートしていません。他のフレームワークを使いたい場合は、Things Cloudのコンパイル時統合をご覧ください。 現在のAngularアプリケーションをビルドするのと同じ開発者体験を提供するために、オプションを制限することにしました。開発者として、c8cycliを使うことで、新しいアプリケーションを作るのとほぼ同じ方法で最初のプラグインを始めることができます。

カスタムウィジェットプラグイン

簡単なウィジェットの作成方法、その構造がどのように見えるか、アプリケーションへの追加方法については、ダッシュボードにカスタムウィジェットを追加する > ウィジェットコンポーネントを作成するをご覧ください。 次のチュートリアルでは、マイクロ・フロントエンド・アーキテクチャーを使用してウィジェットをアプリケーションに追加する方法と、プロセスが前のプロセスとどのように異なるかを中心に説明します。

備考
以下のソリューションは、Webpack 5で導入されたアーキテクチャー機能に完全に基づいています。 機能の詳細については、Webpack:モジュールフェデレーションをご覧ください。

1. ウィジェットプラグイン(例)を初期化する

以下に示すコマンドを使用して、サンプルプラグインを作成するための複数ステップのプロセスを開始します。

c8ycli new

プラグイン名を選択します(例:widget-plugin)。

? Enter the name of the project:  (my-application) widget-plugin

サンプルアプリケーションを作成するバージョンを選択します(例:“1018.106.0”、マイクロ・フロントエンドは"10.16.0.0"以上のバージョンでサポートされています)。

? Which base version do you want to scaffold from?
  1015.0.372 (latest)
❯ 1018.106.0 (next)
  1018.0.47 
  1017.0.151 
  1016.0.233
  1014.0.359 
  other

プラグインのベースとなるアプリケーションテンプレートを選択します(例:widget-plugin)。

? Which base project do you want to scaffold from?
  administration
  application
  cockpit
  devicemanagement
  hybrid
  tutorial
❯ widget-plugin

数秒後、次のようなメッセージが表示されます。

Application created. Go into the folder "widget-plugin" and run npm install

アプリケーションフォルダに移動し、npm installを実行します。

アプリケーションフォルダは、以下の例のようになります。 このチュートリアルでは、最も重要なファイルは package.jsonREADME.md です。

app.module.spec.ts
jest.config.js
README.md
tsconfig.spec.json
app.module.ts
package.json
setup-jest.js
widget/
index.ts
polyfills.ts
tsconfig.json

これで、マイクロ・フロントエンド・アーキテクチャーを使用する最初のプラグインが作成されました。

2. カスタムウィジェット作成時のアプローチの違い

シンプルなウィジェットとマイクロ・フロントエンド・アーキテクチャに従ってビルドされたウィジェット間には、いくつかの違いがあります。

最大の違いは package.json ファイルで、isPackage, package, exports などのフィールドが配置されています。 以下のリストは、フィールドとその役割を示しています。

備考

プラグインを作成する場合、カスタムモジュールはこのアプローチの中核になります。エクスポートされたモジュールは、シェルと呼ばれるアプリケーションとプラグインをリンクするエントリポイントとして扱われます。既製の機能を含む必要があるいくつかのモジュールを作成して、エクスポートできます。

さらに、これらのモジュールは、遅延ロードモジュールのように動作します。一つの大きなパッケージとして事前に読み込まれるのではなく、オンデマンドで読み込まれる小さなパッケージのコレクションのようなものです。 各モジュールは、HOOKの概念によって追加機能を拡張することができます。詳しくは既存のアプリケーションを拡張しフックを使用するをご覧ください。例えば、プラグインはHOOK_NAVIGATOR_NODESを使用してナビゲーションメニューに別のエントリを追加することができます。詳細はナビゲーターノードをフックするをご覧ください。

また、ローカル開発サーバーの起動方法にも違いがあり、サーバーの役割については以下のステップをご覧ください。

3. ローカルサーバー、デバッグとデプロイメント

ローカルサーバー

新しいプラグインの作成プロセスを容易にするため、ローカルサーバーのコマンドに、すべてのリクエストをシェルアプリケーション「コックピット」に代理させる新しいフラグが追加されました。

npm installを実行し、ローカルサーバーを起動します。

npm start -- --shell cockpit

次のような出力が表示されます。

Shell application: cockpit
http://localhost:9000/apps/cockpit/index.html?remotes=%7B%22widget-plugin%22%3A%5B%22WidgetPluginModule%22%5D%7D

リンク先では、コックピットのログイン画面に転送されます。 ログイン後、ダッシュボードに移動しウィジェットの追加をクリックします。その際、利用可能なウィジェットのリストからモジュールフェデレーションウィジェットを選択します。

ウィジェットの編集作業の残りの部分は、通常のウィジェットの編集作業と同じです。変更内容を確認するには、ブラウザを更新してください。

デバッグ

通常のウィジェットとマイクロ・フロントエンド・アーキテクチャー用に変更されたウィジェットのpackage.jsonファイルのもう一つの違いは、remotesフィールドです。(以下の例をご覧ください。)

...
"remotes": {
  "widget-plugin": [     // contextPath
    "WidgetPluginModule" // module class name
  ]
}
...
備考
remotesフィールドは、モジュールをインポートするために使用されます。モジュールを正しくインポートするには、プラグインのコンテキストパス (package.jsoncontextPath フィールド) に続いて、モジュールのクラス名を指定します。

プラグインは、remotesというフィールドを介してそれ自体をインポートします。 エクスポートされたモジュールの正確性を検証する最初のステップとしてお勧めします。これにより、アプリケーションのデバッグが容易になります。 独自のモジュールをインポートした後、npm startを実行して、ローカルサーバーが起動するかどうかを確認します。

後でプラグインを確認するために、npm start -- --shell cockpitを使用して、さまざまなシェルアプリケーションでローカルでテストすることをお勧めします。

デプロイ

ウィジェットのアップロードは、通常のウィジェットと同じです。 以下のコマンドを順番に実行してください。

npm run build

そして、

npm run deploy

コンソールのプロンプトに従って、アプリケーションをテナントにデプロイします。

4. デプロイされたウィジェットをシェルアプリケーションに追加する

アップロードされたウィジェット プラグインをコックピットアプリケーションのダッシュボードに追加するには、以下のステップを実行します。

これで、カスタムウィジェットは、コックピットアプリケーションのバージョンで利用できるようになりました。 ウィジェットの追加のリストに、新しく追加されたウィジェットが利用可能なダッシュボードに移動します。

widget-pluginは、管理アプリケーションの中からインストールされました。これが、ウィジェットに関する通常のアプローチと新しいアプローチとの主な違いです。 マイクロ・フロントエンド・アーキテクチャーでは、アプリケーションが実行中(ランタイム)に新しい機能を追加することができますが、古いアプローチでは、アプリケーションがビルド(コンパイル時)にしか新しい機能を追加することができませんでした。

カスタム・パッケージブループリント

マイクロ・フロントエンドでは、アプリケーションが実行中(ランタイム)に新しい機能を追加することが可能であるのに対し、以前のアプローチではアプリケーションがビルド中(コンパイル時)にしか新しい機能を追加することができませんでした。

ブループリントは、プラットフォームによってホストされる複数のUI機能の組み合わせ(静的ファイル)であり、ゼロから新しいソリューションの足場として使用することができます。一方、パッケージはプラグインとブループリントの組み合わせです。ブループリントはプラグインもエクスポートできるため、それらを1つのパッケージにまとめてプラットフォームにデプロイできます。

サンプルブループリントを初期化

  1. 以下に示すコマンドを使用して、サンプルブループリントを作成するマルチステッププロセスを開始します。
c8ycli new
  1. プラグイン名を選択(例: 「package-blueprint」)
? Enter the name of the project:  (my-application) package-blueprint
  1. 作成したいサンプル・アプリケーションのバージョンを選択(例:「1016.0.233」)
? Which base version do you want to scaffold from?
  1015.0.372 (latest)
  1018.106.0 (next)
  1018.0.47
  1017.0.151
❯ 1016.0.233
  1014.0.359
  other
  1. プラグインのベースとなるアプリケーションテンプレートを選択
? Which base project do you want to scaffold from?
  cockpit
  devicemanagement
  hybrid
> package-blueprint
  tutorial
  widget-plugin
  administration

数秒後、以下のメッセージが表示されます。

Application created. Go into the folder "package-blueprint" and run npm install
  1. アプリケーションフォルダーに移動し、npm installを実行

アプリケーションフォルダーは、下図の例のようになります。 このチュートリアルで最も重要なファイルは_package.json_と_README.md_です。

app.module.spec.ts
jest.config.js
README.md
tsconfig.spec.json
app.module.ts
package.json
setup-jest.js
index.ts
polyfills.ts
tsconfig.json

これで、モジュール・フェデレーションを使用する最初のパッケージ・ブループリントが作成されました。

ステッパーのセットアップ(オプション)

HOOK_STEPPERはアプリケーションの最初のロード中にアプリケーションのカスタマイズを可能にするために追加で提供することができます。このオプションのステップでは、起動時にナビゲータを折りたたむかどうかをユーザーが選択できる小さなシングルステップの例を示します。

  1. 以下の内容で_setup-step1.component.ts_ファイルを新規作成
import { CdkStep } from "@angular/cdk/stepper";
import { Component } from "@angular/core";
import {
  AlertService,
  AppStateService,
  C8yStepper,
  SetupComponent,
} from "@c8y/ngx-components";

@Component({
  selector: "c8y-cockpit-setup-step1",
  templateUrl: "./setup-step1.component.html",
  host: { class: "d-contents" },
})
export class SetupStep1Component {
  config = {
    rootNodes: [],
    features: {
      alarms: true,
      dataExplorer: true,
      groups: true,
    },
    hideNavigator: false,
    userSpecificHomeDashboard: false,
  };
  pending = false;

  constructor(
    public stepper: C8yStepper,
    protected step: CdkStep,
    protected setup: SetupComponent,
    protected appState: AppStateService,
    protected alert: AlertService
  ) {}

  async next() {
    this.pending = true;
    try {
      const newConfig = { ...this.setup.data$.value, ...this.config };
      await this.appState.updateCurrentApplicationConfig(newConfig);
      this.setup.stepCompleted(this.stepper.selectedIndex);
      this.setup.data$.next(newConfig);
      this.stepper.next();
    } catch (ex) {
      this.alert.addServerFailure(ex);
    } finally {
      this.pending = false;
    }
  }

  back() {
    this.stepper.previous();
  }
}
  1. _setup-step1.component.html_テンプレートを作成
<form #stepForm="ngForm" name="form" class="d-contents">
  <div class="container-fluid flex-no-shrink fit-w">
    <div class="row separator-bottom">
      <div
        class="col-md-8 col-md-offset-2 col-lg-6 col-lg-offset-3 p-t-24 p-l-16 p-r-16"
      >
        <h3 translate class="text-medium l-h-base">Misc</h3>
        <p class="lead text-normal" translate>
          Miscellaneous settings for the current application.
        </p>
      </div>
    </div>
  </div>
  <div class="inner-scroll flex-grow">
    <div class="container-fluid fit-w">
      <div class="row">
        <div class="col-md-8 col-md-offset-2 col-lg-6 col-lg-offset-3">
          <c8y-misc-config [config]="config"></c8y-misc-config>
        </div>
      </div>
    </div>
  </div>
  <div class="card-footer separator d-flex j-c-center">
    <button
      class="btn btn-default"
      type="button"
      (click)="back()"
      *ngIf="index !== 0"
      translate
    >
      Previous
    </button>
    <button class="btn btn-primary" type="submit" (click)="next()" translate>
      Save and continue
    </button>
  </div>
</form>
  1. 最後に app.module.ts ファイルを拡張して、新しいステッパー・コンポーネントを含める
import { NgModule } from "@angular/core";
import { BrowserAnimationsModule } from "@angular/platform-browser/animations";
import { AssetsNavigatorModule } from "@c8y/ngx-components/assets-navigator";
import { RouterModule as ngRouterModule } from "@angular/router";
import {
  CoreModule,
  BootstrapComponent,
  RouterModule,
  SetupStep,
  HOOK_STEPPER,
  Steppers,
  gettext,
} from "@c8y/ngx-components";
import { SetupStep1Component } from "./setup-step1.component";
import { DatapointLibraryModule } from "@c8y/ngx-components/datapoint-library";
import { MiscConfigComponent } from "./misc-config.component";

@NgModule({
  declarations: [SetupStep1Component, MiscConfigComponent],
  imports: [
    BrowserAnimationsModule,
    RouterModule.forRoot(),
    ngRouterModule.forRoot([], { enableTracing: false, useHash: true }),
    CoreModule.forRoot(),
    DatapointLibraryModule.forRoot(),
    AssetsNavigatorModule,
  ],
  providers: [
    {
      provide: HOOK_STEPPER,
      useValue: [
        {
          stepperId: Steppers.SETUP,
          component: SetupStep1Component,
          label: gettext("Step 1"),
          setupId: "exampleId",
          priority: 0,
        },
      ] as SetupStep[],
      multi: true,
    },
  ],
  bootstrap: [BootstrapComponent],
})
export class AppModule {}

最初のアプリケーション起動時に、ユーザーは上記のシングルステップ・ウィザードを完了する必要があります。

カスタムアプリケーションを作成するアプローチの違い

シンプルなウィジェットとマイクロ・フロントエンドガイドラインに従ってビルドされたウィジェットには、いくつかの違いがあります。

最も大きな違いは package.json ファイルで、isPackagepackageexports (現在のブループリントアプリケーションにはない) などのフィールドが配置されています。 以下のリストにフィールドとその役割を示します。

備考
ブループリントにはプラグインを含めることもでき、後で他のアプリケーションを拡張するために使用できます。

デプロイメント

パッケージのアップロードは、通常のウィジェットの場合と同じです。 以下のコマンドを順次実行してください。

npm run build

npm run deploy

コンソールのプロンプトに従って、アプリケーションをテナントにデプロイします。

開発者のためのベストプラクティス

どのアプローチを使い、どのように開発ジャーニーを始めるかを決めるのは大変なことです。 もしあなたがパートナーや顧客であれば、マイクロ・フロントエンドのジャーニーに参加することをお勧めします。 マイクロ・フロントエンド・ジャーニーに参加することで、サイロ・ソリューションを構築する代わりにエコシステムを拡張し、利用することができます。 このセクションでは、このジャーニーのビジョンと、開発に関するベストプラクティスについて説明します。

ユーザージャーニー - ブループリントかプラグインか

正しいものを開発するためには、アプリケーションでどのようなユーザーをターゲットにしたいかを理解することがとても重要です。 マイクロ・フロントエンドのストーリーの場合、ターゲットとなるユーザーは、開発者の経験があまりないソリューションアーキテクトです。 アイデアはシンプルで、どのようなIoTソリューションでも、ユーザーはコーディングなしで数ステップでセットアップできます。 そのためのビルディング・ブロックはエコシステムによって開発されます。 手順は以下の通りです。

  1. ソリューションアーキテクトは、使用するブループリントを選択してください。これはソリューションの基礎となるアプリケーションです。
  2. ブループリントはアプリケーションの構成をガイドします。例えば、必要なマイクロサービスやプラグインのインストールがその一例です。
  3. セットアップ後、ソリューションは使用可能な状態になります。ソリューションアーキテクトは、他のユーザーとテストすることができます。追加プラグインを追加または削除するオプションで説明できる追加要件があるかもしれません。

マイクロ・フロントエンドストーリーのユーザージャーニーは、セルフサービスのアプローチです。 開発者でない人があなたのニーズに合わせることができるアプリケーションを提供するのです。 アプリケーションの使い方は未知であり、ソリューションアーキテクト次第であることを前提としています。 そのため、これらのアプリケーションは汎用的な方法で設計され、可能な限り再利用できるようにしなければなりません。 古典的なアプリケーションを開発したいのか、マイクロ・フロントエンドを開発したいのかを決めるために、次のデシジョンツリーを使ってください。

Your use case needs a distinct design and components?   --YES--> Consider a custom implementation.
                |
                NO
                |
Your use case is very specific?                         --YES--> Consider a custom implementation based on the Web SDK.
                |
                NO
                |
Your use case is an app that others can use?   --YES--> Consider a micro frontend blueprint.
                |
                NO
                |
Consider a micro frontend plugin.

デシジョンツリーは、一番上(最も労力がかかり、カスタマイズの度合いが高い)から一番下(最も労力がかからず、カスタマイズの度合いが低い)まで読むことができます。 例えば、すべてを自分で行うことを選択した場合独自のログインロジックを書く必要があり、データグリッド、ダッシュボード、アセットセレクタのような私たちの事前構築されたコンポーネントを使用することはできず開発工数は非常に高くなります。 代わりにWeb SDKを使用すれば、すぐに多くの機能を利用でき、アプリケーションをブランディングすることで、自社開発の製品のように見せることができます。 さらに、アプリケーションの設計図として顧客に提供し、顧客のニーズに応じてブランディングすることも可能です。

ただし、利用可能なブループリント (管理 > エコシステム > 拡張機能) またはデフォルトのアプリケーション (コックピット、デバイス管理、管理) のいずれかがすでにほぼあなたのケースに適合しているが、例として拡張機能がないような場合は、代わりにプラグインを実装してください。 プラグインは実装が簡単で、既存のアプリケーションのあらゆる部分を拡張することができます。さらに、それらの拡張機能を顧客に提供するつもりであれば、そうしてプラグインを共有することができます。

次のセクションでは、ブループリントとプラグインの開発者ストーリーを詳しく説明し、ベストプラクティスを提供します。

プラグインとブループリントの開発者ジャーニーの始まり

すべての開発者ストーリーは CLI ツールから始まります。 新しいアプリケーションの足場を作り、どのデモを使うか決めることができます。 例えば、プラグインの場合は widget-plugin デモを試すことができます。 このセクションではその方法について詳しくは説明しません(以前のセクションですでに説明済みです)。 その代わりに、アプリケーションをプラグインやブループリントにするものと、その違いについて説明します。

まず第一に、通常のアプリケーション、ブループリント、プラグインに大きな違いはありません。 これらはすべてアプリケーション API を介してビルド、テスト、デプロイされます。 しかし、プラグインとブループリントはマニフェストファイルにいくつかの詳細情報を持っています。

マニフェストファイルには、package.json ファイルの c8y.application プロパティに格納されているすべてのオプションが含まれています。 ビルド時に、このファイルは cumulocity.json ファイルにコンパイルされます。 cumulocity.jsonファイルを含むアーカイブをアップロードすると、その情報はアプリケーションAPIからもアクセスできるようになります。

アプリケーションがプラグインであるかどうか、そしてどのタイプであるかを示す2つの重要なプロパティがあります。 プラグインまたはブループリントに使用される追加のプロパティがあります。 以下のリストは、マイクロ・フロントエンドのマニフェストファイルに関連するプロパティです。

{
  "c8y": {
    "application": {
      "isPackage": true,
      "package": "plugin",
      "exports": [
        {
          "name": "Example widget plugin",
          "module": "WidgetPluginModule",
          "path": "./widget/widget-plugin.module.ts",
          "description": "Adds a custom widget to the shell application"
        }
      ],
      "remotes": {
        "widget-plugin": [
          "WidgetPluginModule"
        ]
      }
    }
  }
}

このプロパティは、マイクロ・フロントエンドのステップバイステップの手順に詳しく説明されています。 ただし、意味のあるプラグインやブループリントを作成するために役立つ可能性のある追加のプロパティがあります。

さらに詳細なREADME.mdファイル内に情報を追加することができ、パッケージの詳細ビューに表示されます。 これらのパッケージがリリースされた成果物です。 準備ができたら、パッケージをプラットフォームにデプロイして、既存のアプリケーションを拡張したり(プラグインで)、新しいアプリケーションをインストールしたり(ブループリントで)できます。 次のセクションでは、パッケージとは何か、パッケージには何が含まれているかについて説明します。

パッケージとその内容

ユーザーストーリーで指摘されているように、2種類のマイクロ・フロントエンドを開発することができます。ブループリントとプラグインです。 どちらもアプリケーションAPIにアップロードされ、APIによってホストされるアプリケーションです。 これらのホストされたアプリケーションは、複数のプラグイン、あるいはブループリントとプラグインの組み合わせを含むことができるので、‘packages'という新しい概念ユニットが導入されました。 パッケージでは、複数のプラグインやブループリントを1つのパッケージにバンドルし、それらのバージョンを提供することができます。最適なパッケージには以下が含まれます。

パッケージのアイデアは、ブループリント・アプリケーションで管理できる複数の所属プラグインをバンドルすることです。そのため、コックピットアプリケーションに複数のウィジェットを公開して情報(例:「無料駐車場」ウィジェット)を表示するスマートシティ管理アプリケーションが良いユースケースになります。

既存のアプリケーションをプラグインで拡張可能か

拡張方法は変わりません。コンポーネントライブラリ (ngx)で定義されているHOOK_インターフェースのどれでも使用できます。NavigatorNode、ActionBarItem、またはRouteです。

ヒント
バージョン10.17.0から、hook<<Name>>という型付きヘルパー関数が追加されました。 例えば、プロバイダー内で hookNavigator() を使うと、ノードをナビゲータにフックすることができます。

アプリケーションのデバッグ

プラグインは遅延ロードされたAngularモジュールです。 そのため、デフォルトのAngularモジュールを使い、デフォルトのデベロッパーストーリーで検証することができます。 Things Cloudはさらに2つの検証方法を提供しているので、アプリケーションの検証とデバッグには3つの方法があります。

  1. Classic: アプリケーションを実行し、モジュールを AppModule にインポートします。それから c8ycli serve を実行してアプリケーションを起動します。

  2. Lazy loading: アプリケーションを実行しますが、モジュールはインポートしません。その代わりに、リモートにモジュールをインポートします(これは基本的にセルフインポートプラグインです)。c8y.application.remotesオプションをモジュールに指定してください。

    "remotes": {
      "<<contex-path>>": [
        "<<module-name>>"
      ]
    }
    

    これには、アプリケーションがシェルとして機能し、遅延ロードで問題が発生しそうな場所を確認できるという利点があります。

  3. Shell: アプリケーションを実行し、c8cycli serve --shell cockpitを実行して任意のシェルを指定してください。これは実際のアプリケーションでテストできるという利点があります。しかし、シェルアプリケーションはすでにデプロイされているので、役に立たないエラースタックが表示されるかもしれません。

上記の方法2、Lazy loadingの使用をお勧めします。 必要であれば、方法3でアプリケーションを検証してください。 次のセクションで説明する一般的な落とし穴にぶつかる可能性があるので、できれば方法1は避けてください。

プラグイン開発で陥りがちな開発者の落とし穴

避けるべき問題がいくつかあります。

  1. ルーティング:一般的な名前のルートを避けてください。例えば/home のようなルートは使用せず、代わりに /<<my-unique-prefix>>/home を使用してください。一般的に名前が付けられたルートは、他のプラグインによって上書きされる可能性があります。開発プロセスで使用する識別子についても同じことを行います。

  2. 遅延ロード: すべてのプラグインは遅延ロードされることを覚えておいてください。これは、遅延ロードされたモジュールには遅延ロードされたモジュールのルールが適用されることを意味します。 ngx-components の CoreModuleRoutingModule では forRoot を使用しないでください。新しく導入された依存関係には forRoot を使用してください。

  3. インジェクター: 遅延ロードのアプローチでは、インジェクターが問題になることがあります。通常、プラグインごとに新しいインジェクターを用意します。ファクトリー関数を使わずにフックを使う限り、これは自動的に行われます。ファクトリー関数を使う場合は、インジェクターを用意しなければなりません。

    import { Injectable, Injector } from '@angular/core';
    import { ActionBarItem, EmptyComponent } from '@c8y/ngx-components';
    
    @Injectable()
    export class MyActionBarFactory {
      constructor(private injector: Injector) {}
    
      get() {
        const actionBarItem: ActionBarItem = {
          component: EmptyComponent,
          injector: this.injector, // (1)
          placement: 'left'
        };
        return actionBarItem;
      }
    }
    

    (1):これは、提供したコンポーネントが、あなたのプラグインでしか利用できないサービスを利用したい場合に重要です。インジェクターを定義しないと、ルートインジェクターを使用することになり、インジェクターの問題につながります。

アセットをパッケージにバンドルする方法

アセットをバンドルするのは、コピーするだけでなくパスが正しく反映されていなければなりません。例えば、画像をインポートする場合、画像へのパスは次のようになります。http://<<instance>>/apps/<<context-path>>@<<version>>/my-image.png
デプロイするたびにバージョンを変更したくないかもしれません。 そのため、bundler にイメージを処理させることをお勧めします。 これは typescript ファイルにイメージをインポートすることで行います。 bundler は常にイメージへの正しいパスを返します。 例えば、以下のようなファイル assets/assets.ts を作成します。

import previewImage from './widget-plugin-pr.png';

export const assetPaths = { previewImage };

TypescriptはPNGファイルの扱い方を知らないため、エラーを投げます。 assets/index.d.ts*ファイルにモジュールとして宣言することで、typescriptにそのようなファイルを受け入れるように伝えてください。

declare module '*.png';

アセットをプラグインかブループリントのどこかにインポートし、パスを使用して画像を表示します。

import { assetPaths } from '../assets/assets';

console.log(assetPaths.previewImage);

assetPaths.previewImageは任意のコンポーネントやフックで使用できるようになりました。 c8ycli newwidget-plugin をスキャフォールドすると、完全な例を見ることができる。 なお、この機能と例はバージョン 10.17.0 で追加されました。

ヒント
ウィジェットに使用するプレビュー画像の推奨サイズは340 x 340ピクセルです。

翻訳

標準的なカスタムアプリケーションと同じように、翻訳もすぐに動作します。 リポジトリに .po ファイルを追加し、index.ts ファイルにインポートしてください。 プラグインの翻訳は実行時にマージされるため、既存の翻訳を上書きするかもしれません。 例えば、widget-pluginc8ycli new でスキャフォールドした場合です。 制限があることに注意してください: プラグインは新しい言語を追加できません。プラグインが拡張できるのは、既存のアプリケーションで翻訳された文字列だけです。

スタイリングとブランディング

ブランディングは完全にサポートされています。 アプリケーションにはコンポーネント・ベースのスタイリングを使用することをお勧めします。 しかし、インポートすることで、カスタムのグローバル CSS スタイリングを追加することもできます。

import './example.css';

c8ycli newwidget-pluginをスキャフォールドすると、またサンプルが作成されます。

アプリケーションの互換性を確保する方法

一般的な互換性を確保する方法はありません。 すべてのメジャーなWeb SDKのバージョンには、新しいAngularのバージョンが含まれている可能性があります。 Angularはすべてのマイクロ・フロントエンドで共有されるライブラリの1つであり、そのため非推奨のメソッドを呼び出すと、マイクロ・フロントエンドとそれをインポートするアプリケーションの互換性が失われる可能性があります。 しかし、このような非互換性を回避するために2つのバージョン保護メソッドを用意しています。

  1. プラグインを追加できるのは、いわゆる「カスタム」アプリケーションだけです。カスタムアプリケーションとは、あなたがそのアプリケーションを所有しなければならないことを意味します。副次的な効果として、自動アップデートは行われません。このため、プラットフォームのアップデートによってアプリケーションが壊れることはありません。
  2. プラグインは常にバージョン管理されています。プラグインのアップデートは新しいバージョンになり、もし互換性がなくても、アップデートが壊れることはありません。

アプリケーションやプラグインのアップデートは可能です。プラグインのアップデートについては、実際のアプリケーションをクローンし、プラグインが新しいバージョンでも動作するかどうかをテストすることをお勧めします。クローンされたアプリケーションはテストアプリケーションであり、その後削除することができます。

ブループリントについては、アプリケーションのアップデートを提案する通知が届きます。これらのアップデートはテストすることもでき、いくつかのプラグインが失敗した場合はロールバックすることができます。

バージョン10.19.0からは、プラグインのどのバージョンがWeb SDKのどのバージョンと互換性があるかを正確に示すバージョンマトリックスも追加されました。

プラグインの最新バージョンを常に提供したいプラグイン開発者には、community plugin Github project をお勧めします。このcommunity plugin Github project には、プラグインの最新バージョンがまだ動作することをテスト・検証するための CI/CD ワークフローがいくつか含まれています。

リポジトリコネクトの使い方

リポジトリコネクトは、プラグインまたはブループリントをThings Cloudプラットフォームのインスタンスと同期するマイクロサービスです。 管理テナントにインストールする必要があり、複数のリポジトリを接続できます。 現在、Software AG のパブリック GitHub のみが接続されています。 複数の方法でブループリントやプラグインに参加し、共有することができます。

  1. 私たちのオープンソースプラグインに貢献してください。一覧はThings Cloud GitHub packagesにあります。公式リポジトリがあり、Things Cloudの内部R&Dチームによって管理されています。
  2. オンプレミスのインスタンスでリポジトリ接続を設定し、あなたの組織を指定します。
  3. プロダクトマネージャーに、あなたのリポジトリをパートナーリポジトリとして追加するよう依頼してください。
備考
これはすべてのThings Cloudのカスタマーとアプリケーションを共有したい場合にのみ必要です。顧客とパッケージを共有したい場合(例えばエンタープライズテナントで)、Packagesビューでパッケージをアップロードし、可用性を'shared'に設定してください。

同期のために、マイクロサービスは特定のトピックとリリース(NTT コミュニケーションズの場合は cumulocity-package)を持つすべてのリポジトリを検索します。 リリースはプラグインまたはブループリントを含む単一のzipファイルでなければなりません。 スコープと呼ばれるセキュリティーメカニズムがあり、特定の接頭辞がないアプリケーションのアップロードを禁止します。 これは、同期されたパッケージがコックピットや管理画面のようなデフォルトのアプリケーションを上書きしてしまうのを防ぐためです。 keycontextPath の前に設定した接頭辞を付けます(NTT コミュニケーションズ の場合は sag-pkg)。

リポジトリ接続経由でアップロードされるすべてのアプリケーションは community プラグインと表示され、インストール時にライセンスと保守契約がユーザーに通知されます(バージョン 10.18.0 以降)。