TT Lab
はじめる
学ぶ 学習パス コース

CBA — Backstage認定アソシエイト

リポジトリ構造がそのままカスタマイズ力である理由

TT Labで続きを見る

一言でいうと

npx @backstage/create-appが作ってくれるのは、Backstageのコピーではなく、自分たちが所有するモノレポです。カスタマイズは、設定を1行オンにすることではなく、そのリポジトリのコードを直して、ビルドしてデプロイすることであり、そのため、どこに何があるかを知っていることが、そのままカスタマイズの能力です。

なぜ必要なのか

ポータルを導入したチームが、最初にぶつかる質問は、たいていこういうものです。

これらの質問には、共通点があります。すべて、リポジトリの構造を知っていれば答えが出て、知らなければ答えが出ません。Backstageは「設定でオン・オフする製品」ではないので、画面から見つけられる答えがありません。

どう動くのか

リポジトリはワークスペース1つ

create-appが作ったツリーは、yarnワークスペースを使うモノレポです。

package.json                루트. private: true, workspaces 에 packages/* 와 plugins/*
packages/app/               프런트엔드 애플리케이션 (React)
packages/backend/           백엔드 애플리케이션 (Node.js)
plugins/<이름>/             사내에서 만든 플러그인
app-config.yaml             설정

ルートをprivate: trueにする理由は、このパッケージ自体をデプロイしないからです。そして、workspacesにplugins/*を入れてはじめて、社内プラグインをnpmレジストリにアップロードしなくても、@internal/plugin-oncallのような名前で、アプリから持ってきて使えます。

package.jsonのbackstage.roleが鍵

各パッケージのpackage.jsonには、backstage.roleがあります。backstage-cliは、この値1つを見て、そのパッケージをどうビルドし、どうテストするかを決めます。

role 何か
frontend packages/appというただ1つのフロントエンドのアプリ
backend packages/backendというただ1つのバックエンドのアプリ
frontend-plugin ブラウザーで動くプラグイン
backend-plugin サーバーで動くプラグイン
common-library · node-library · web-library プラグインではない共通のコード

役割を間違って書くと、ビルドはできるのに、成果物がおかしくなります。バックエンドパッケージのmainが、ビルドのアーティファクト(dist/...)を指さなければならないのも、同じ理由です。

新しいバックエンドシステムは、登録するだけ

packages/backend/src/index.tsが、バックエンドのすべてです。

const backend = createBackend();
backend.add(import('@backstage/plugin-catalog-backend'));
backend.add(import('@backstage/plugin-catalog-backend-module-github'));
backend.start();

以前のバックエンドは、プラグインごとにルーターを手で作り、ロガー・設定・データベースを直接渡す必要がありました。新しいバックエンドシステムでは、登録するだけで、必要なものを注入されます。ここで、プラグインとモジュールの違いが、試験に出ます。

名前に-module-が入っていれば、それは単独では動作せず、対になるプラグインが一緒に登録されている必要があります。

フロントエンドプラグインは、3つのファイルから始まる

src/routes.ts   createRouteRef 로 라우트 참조를 만들어 내보낸다
src/plugin.ts   createPlugin 으로 플러그인을 만들고, 페이지를 확장으로 제공한다
src/index.ts    바깥에 공개할 것만 다시 내보낸다

ルート参照をあえて別のファイルに置くのには、理由があります。ページのコンポーネントがプラグインを参照し、プラグインがまたコンポーネントを参照すると、循環インポートが生じます。参照だけを入れたファイルを別に置けば、その輪が断ち切れます。

そして、ルートを持つページは、createRoutableExtensionで作り、mountPointにルート参照をかけます。コンポーネントを遅延インポートで渡すので、そのプラグインを実際に開くまでは、コードがダウンロードされません。

index.tsだけを公開の入口にするのも、規律です。アプリがプラグインの内部ファイルを直接持っていき始めると、プラグインの内部構造を、二度と変えられなくなります。

エンティティページにタブを付けるには、2か所を直す

<EntitySwitch>
  <EntitySwitch.Case if={isKind('component')}>
    <EntityLayout>
      <EntityLayout.Route path="/oncall" title="On-call">
        <OncallPage />
      </EntityLayout.Route>

EntitySwitchとisKindでkindごとに別のページを見せ、EntityLayout.Routeでタブを付けます。よくある間違いが、ここで出ます。タブだけを付けてApp.tsxのルートを登録しないと、タブは見えるのに、クリックすると空の画面が表示されます。画面にはエラーがないので、原因を探しにくいです。

開発ワークフロー

yarn install --immutableは、ロックファイルを直さないという意味です。CIでこのフラグを外すと、依存関係が静かに上がって、昨日通ったコミットが、今日は違うようにビルドされます。そのあとは、型検査、ビルド、テストの順序です。この順序が重要な理由は、型エラー1つが、ほかのすべての出力を隠してしまうからです。

現場での姿

このリポジトリにも、同じ境界があります。logos.js、i18n.js、backend/app/grader_paths.pyは生成物なので、手で直すと、次の生成のときに消えます。直すべきなのは、生成器の側です。Backstageのアプリも、まったく同じ性格の境界を持ちます。packages/app/srcは自分のコードで、node_modulesの中のプラグインは、他人のコードです。境界を曖昧にして、他人のコードを直接直し始めると、アップグレードが不可能になります。

もう1つあります。このリポジトリで、複数人が並行して直していた日に、TypeScriptの文法エラー1つのせいで、検査ツールが残りをまったく見ずに終わったことがあります。「検査ツールが何も見つけられなかった」が、実は「何も検査されなかった」だったのです。ポータルのリポジトリも、規模が大きくなると、同じことが起きます。CIで型検査をビルドより先に置いておく理由が、これです。

次のラボですること

/root/cba-app/に、create-appが作り出すリポジトリの構造を、自分で組み立てます。ワークスペースのルートとアプリ2つ、社内プラグイン2つを作り、各パッケージに役割を付けます。そのあと、新しいバックエンドシステムの方式でプラグインとモジュールを登録し、フロントエンドプラグインの3つのファイルを書き、エンティティページにタブを付けます。最後に、パッケージのインベントリを取り出し、CIゲートを立てます。Nodeの依存関係をダウンロードできない環境なので、ビルドは行わず、構造と配線だけを扱います。