背景

Web エコシステムが進化するにつれて、Web エコシステムと共にプラットフォームとして私たちも進化しています。可能な限りスムーズなアップグレードパスを保ち、ほとんど変更を加えずに拡張機能が正しく動作するようにしています。

このプロセスには移行作業が含まれます。そのため、スタックとビルドプロセスが時間とともにどのように進化したかという背景知識を提供することで、なぜ一部のパーツが今日のように動作するのか、なぜ移行作業が必要であるのかを開発者がより理解できると考えています。

簡単な背景: プラグイン vs. モジュール

初期段階から、UI は常にモジュールのアーキテクチャでした。機能の単位はアプリケーションをビルドするために構成できる"プラグイン"としてグループ化されています。実際にはプラグインは単なるモジュールです。

モジュールは最新世代の Web SDK では非常に重要な機能です。ネイティブな JavaScript の import 文で任意のファイルまたはライブラリをインポートしたり、ビルド時に 1 つのファイルにバンドルしたりできます。この概念と機能強化をよりよく理解するには、全世代の Web SDK においてモジュールアーキテクチャがどのように動作しているかを理解することが重要です。

第 1 世代: プラグインのランタイムロード

第 1 世代では、各アプリケーションはプラグインをエクスポートし、プラグインをインポートしていました。エクスポートされたプラグインはプラットフォーム全体で使用できるプラグインであり、インポートされたプラグインはアプリケーションで表示される機能でした。以下のように、この方法では各アプリケーションには 2 つの機能がありました:

アプリケーションとプラグインに関する情報は、API を介してデータベースに保存されていました。各プラグインは、各アプリケーションのコンテキストで作成する必要がありました。マニフェストファイルは既に存在しますが、その情報を使用し、データを保存する API を呼び出していました。

REST API は applications/<id>/plugins でアクセス可能でしたが現在は非推奨です。

この時点では以下が存在しました:

アプリケーションコンテキスト リポジトリ
core m2m/cumulocity-ui
管理 m2m/cumulocity-ui-administration
デバイス管理 m2m/cumulocity-ui-devicemananagement
コックピット m2m/cumulocity-ui-cockpit

コアはプラグインをインポートしない、エクスポートするだけのアプリケーションでした。

モジュールの読み込みは完全に動的でした。全てのアプリケーションで使用されている基本的な index.html を提供、小さな JavaScript のレイヤーでユーザーにログインし、認証後にのみアプリケーションの情報をフェッチしてそれぞれのアプリケーションから適切なプラグインを読み込んでいました。そして最終的に AngularJS のアプリにブートストラップしていました。このため、ログイン後は以下のような構造になっていました:

<!-- Added with js -->
<script src="/apps/core/main.js"></script>
<script src="/apps/core/plugin1/main.js"></script>
<script src="/apps/administration/pluginA/main.js"></script>
<script src="/apps/devicemanagement/pluginZ/main.js"></script>

各アプリケーションにインポートされたプラグインの情報は REST API によって完全に制御されていたため、全てのアプリケーションは簡単に UI から編集できました。例えば、機能を移動、追加したり、各アプリケーションからプラグインを削除したりできました。これは各アプリケーションのインポートのリストを更新することを意味していました。

この構造により、 <app>/<plugin> の定義から URL を簡単に見つけることができました。この影響は、今日でも多くのプラグイン名に見られます。

サードパーティのクライアントアプリケーションでは、以下のような結果になる可能性がありました:

<script src="/apps/core/main.js"></script>
<script src="/apps/cockpit/dashboards/main.js"></script>
<script src="/apps/myapplication/pluginA/main.js"></script>

この方法では、プラットフォームが更新されるたびに、サードパーティアプリケーションはすべてのプラグインの最新バージョンを取得しており、時折発生する重大な変更に伴いサードパーティアプリケーションを更新することが困難でした。

第 2 世代: ビルド時間バンドルへの変更

第 2 世代の主な概念上の変更点は、アプリケーションが独立していたことです。これにより、実行時にロードされるデータとしてプラグインを使用しなくなり、スクリプトタグのリストがビルド時に index.html にレンダリングされるようになりました。

このように、プラットフォームが更新されるたびに、サードパーティアプリケーションが明示的にアップグレードされた場合にのみ影響を受けました。

プラグインは単なるモジュールであり、その名前はユニークである必要がありました。以前は意図的だったパターン `<app> / <plugin>`はユニークであるモジュール名とは無関係になりましたが、今日までのようにこの命名との互換性を維持しました。

方法は大幅に変更されましたが、マニフェストファイルは完全に互換性がありました。

最終的に、アプリケーションのアセンブル時に生成された index.html は次のようになりました:

<script src="./core/main.js"></script>
<script src="./cockpit_dashboards/main.js"></script>
<script src="./myapplication/main.js"></script>

プラグインはもはや単なるデータではありませんが、UI を使用してアプリケーションを変更できます。しかし、実際にはファイルシステムを変更する API です。ディレクトリを削除、zip をアップロードして新しいディレクトリを作成し、index.html を変更します。

今日の世代: ネイティブモジュール

新しいアプローチは、最新の Web 開発の完全なスタックを有効にし、プロセスから可能な限り特異性を排除することに焦点を当てています。これを可能にするために、既存の AngularJS のフレームワークを Angular と呼ばれる次世代のものに更新することにしました。レガシーブラウザの使用を引き続き有効にし、npm を介してサードパーティライブラリからのインポートを許可するには、モジュールをバンドルする必要があります。バンドラーとして、Angular で動作する webpack を使用しています。

大きな変更点の 1 つは、これまでのモジュール(またはプラグイン)が開発中およびビルド後に常に表示されるため、スクリプトを追加または削除することで簡単に構成できることです。現在は全てをバンドルしているため、モジュールは開発中にのみまとめることができます。このため、アプリケーションは現在 UI から編集できませんが、同時に開発者は標準化された ES2016 モジュールアーキテクチャを使用できるようになります。標準的なアプローチに従っており、複雑なモジュールアーキテクチャを必要としないため、開発プロセスが簡素化されます。この時点で、プラグインは単なるモジュールです。名前は変更せず、何らかの分離された機能を含むサブパッケージを参照します。

これは、スタックをモジュール化するのに最適な機会でした。焦点は依然として Angular ですが、フレームワークへのロックインを回避するツールを提供しています。これにより、npm に複数のパッケージが公開され、スタンドアロンで使用できます。例えば、開発者は@c8y/client に実装された API 抽象化レイヤーを使用して、React アプリケーションを開発できます。スタックは、Angular に排他的にバインドされなくなりました。

npm への公開

以前はサーバーに tgz を公開していましたが、現在では以下を npm に公開しています:

ブランディングはアプリケーションの一部ではなくなりました

今までは、他のモジュールと同様にブランディングは常にアプリケーションの一部と見なされていました。ブランディングを変更する必要がある場合、アプリケーションの定義を変更しなければなりませんでした。これにはターゲット定義を使用していました。現在では、これはアプリケーションの独自のエントリーポイントに反映されるビルド時のオプションとなっています。アプリケーションビルド時はアプリケーションの 1 つのエントリーポイントと、ブランディング用のエントリーポイントが存在します。これにより、ブランディングを再デプロイすることなくアプリケーションを更新することが可能になります。

移行

新しいツールを使用するには、ビルドツールを切り替える必要があります。以前は、npm パッケージ “cumulocity-node-tools” で c8yコマンドを公開していました。競合を避けるためにコマンド名を c8ycli に変更し、"@c8y/cli” として npm に公開しました。

以下の表はどのバージョンでどのツールをサポートしているかを示しています:

9.16.x 9.22.x 9.25.x 10.4.0.x
cumulocity-node-tools
@c8y/cli
AngularJS
Angular
Hybrid (Angular & AngularJS)

この表は次のように理解できます:

参考: バージョン 9.25.x から Things Cloud 自身はハイブリッドアプリケーションとして実行されています。

非推奨のカスタムマニフェスト

現在、開発者はすべての最新の JavaScript を自由に使用できるため、通常の ESM を使用して依存関係をインポートおよびエクスポートするだけです。マニフェストファイルは引き続き機能しますが、必要ありません。マニフェストファイルはエントリポイントとして引き続きサポートされているため、通常の package.json ファイルと同様にこれらのファイルを使用できます。

cumulocity.json マニフェストは実際にはモジュール記述子であるため、@c8y/cli(webpack をモジュールバンドラーとして使用)を使用すると、カスタムローダーとリゾルバーを使用して、アプリケーションとプラグインマニフェストが他のモジュールとして webpack に解決およびロードされます。

例えば、組み込み済みの AngularJS プラグインは現在以下のように含まれています:

import "@c8y/ng1-modules/dashboard2/cumulocity.json";
import "@c8y/ng1-modules/dashboardUI/cumulocity.json";
import "@c8y/ng1-modules/groupsHierarchy/cumulocity.json";
import "@c8y/ng1-modules/measurements/cumulocity.json";
import "@c8y/ng1-modules/map/cumulocity.json";

そして任意の JavaScript のファイルへのインポートも可能です:

import "./plugins/mywidget/cumulocity.json";

@c8y/cli で既存のアプリケーションを実行する

こちらの diff で確認されているように、既存のプロジェクトに新しいツールを含めるための変更は非常に簡潔です。

@c8y/cli では、アプリケーションのエントリーポイントは、cumulocity.json アプリケーションマニフェストまたはプレーンな JavaScript ファイルにすることができます。

npx c8ycli serve ./cumulocity.json
npx c8ycli serve ./src/main.js

ターゲットファイルを使用する

ターゲットファイルは非推奨ですが、アプリケーションのエントリーポイントが cumulocity.json マニフェストである場合は引き続き使用可能です。

特定のターゲットでコックピットアプリケーションを実行するには以下を実行します:

npx c8ycli serve node_modules/@c8y/ng1-modules/apps/cockpit/cumulocity.json --env.target=mytarget.json

アプリケーションへの変更はターゲットファイルから読み取られますが、アプリケーションを実行またはビルドする定義は、引数として CLI に渡す必要があります。

ターゲットファイルの代替

ターゲットファイルの代替としては、開発者は アプリケーションオプション を使用してください。

インポートされたプラグインのリストを変更する方法はありません。推奨されるアプローチは、必要なモジュールを明示的にインポートすることです。