Salesforce 第二世代パッケージ (2GP) のマスター:アーキテクトのための包括的ガイド

執筆者:Salesforce アーキテクト


背景と応用シナリオ

Salesforce プラットフォームにおけるアプリケーション開発と配布の歴史は、進化の連続でした。初期の変更セットやメタデータAPIによるデプロイメントから、First-Generation Packaging (1GP) (第一世代パッケージング) が登場し、AppExchange パートナー (ISV) は自身のソリューションを顧客に配布する標準的な方法を手にしました。しかし、1GP は開発組織 (Packaging Org) を「信頼できる唯一の情報源 (Single Source of Truth)」とするモデルであり、現代的な開発プラクティスとの間にいくつかの課題を抱えていました。

例えば、1GP ではソースコード管理システム (VCS) との連携が難しく、チームでの共同開発や継続的インテグレーション/継続的デプロイ (CI/CD) の導入には多くの手作業と工夫が必要でした。また、パッケージはモノリシック(一枚岩)になりがちで、機能単位での分割や依存関係の管理が複雑でした。

これらの課題を解決するために登場したのが、Second-Generation Packaging (2GP) (第二世代パッケージング) です。2GP は、Salesforce DX (SFDX) 開発モデルの中核をなす機能であり、開発のパラダイムを「組織中心」から「ソースコード中心」へと転換させました。バージョン管理システム(通常は Git)に格納されたソースコードが正となり、そこからパッケージバージョンをビルドする、現代のソフトウェア開発手法に完全に準拠したアプローチです。

主な応用シナリオ:

  • ISV パートナー: AppExchange で提供するアプリケーションを、モジュール化された複数のパッケージとして開発・管理し、顧客へのアップグレードパスをより柔軟に提供する。
  • 大企業の IT 部門: 複数の事業部や地域で利用される共通の社内アプリケーションやコンポーネントをパッケージ化し、各組織に一貫性のある形で配布・管理する。
  • コンサルティングパートナー: 複数のプロジェクトで再利用可能なコンポーネント(例えば、高度な入力検証コンポーネントや外部システム連携フレームワークなど)をパッケージとして開発し、開発効率を向上させる。
  • DevOps の推進: CI/CD パイプラインに 2GP の作成とインストールを組み込むことで、開発から本番リリースまでのプロセスを完全に自動化する。

Salesforce アーキテクトとして、2GP を理解し活用することは、スケーラブルで保守性が高く、かつ最新の開発手法に則ったソリューションアーキテクチャを設計する上で不可欠です。


原理説明

2GP の根底にあるのは「ソース駆動開発 (Source-Driven Development)」という哲学です。ここでは、その動作原理を支える主要なコンポーネントとコンセプトを解説します。

1. Dev Hub (Dev Hub 組織)

Dev Hub は、2GP の管理ハブとして機能する特別な本番組織または Developer Edition 組織です。この組織自体にパッケージのメタデータは存在しません。代わりに、Dev Hub は以下の役割を担います。

  • スクラッチ組織 (Scratch Orgs) の作成と管理。
  • 2GP パッケージとそのバージョンの所有権の追跡。
  • 名前空間 (Namespace) の登録と管理。

アーキテクトの視点では、Dev Hub は開発ライフサイクル全体を統括する司令塔と考えることができます。

2. ソースコード管理システム (VCS)

Git のような VCS が、メタデータとコードの「信頼できる唯一の情報源」となります。すべての変更は VCS 上で追跡され、ブランチ戦略(例: Git Flow)を通じて機能開発、テスト、リリースが管理されます。`sfdx-project.json` ファイルがリポジトリのルートに置かれ、プロジェクトの構造とパッケージの定義を記述します。

3. スクラッチ組織 (Scratch Orgs)

スクラッチ組織は、ソースコードから直接作成される、使い捨ての一時的な Salesforce 組織です。開発者は、クリーンな環境で特定の機能開発やテストを行うことができます。2GP のパッケージバージョンを作成する際、Salesforce は内部的にこのスクラッチ組織に似たクリーンな環境を使用して、ソースコードの妥当性検証やテストの実行を行います。

4. パッケージとパッケージバージョン

2GP では、「パッケージ」という概念とその「バージョン」が明確に分離されています。

  • パッケージ (Package): メタデータコンポーネントの論理的な集合体です。`sfdx-project.json` 内で定義され、Dev Hub に登録されます。ID は `0Ho` から始まります。
  • パッケージバージョン (Package Version): 特定の時点でのパッケージの不変なスナップショットです。Git のコミットハッシュに似ています。各バージョンは、テストカバレッジの要件を満たし、依存関係が解決された状態で作成されます。ID は `04t` から始まります。バージョンは「ベータ」として作成され、テスト後に「リリース済み (Promoted)」に昇格させることで、本番組織へのインストールが可能になります。

5. 依存関係の管理

2GP の最も強力な機能の一つが、パッケージ間の明示的な依存関係定義です。`sfdx-project.json` ファイル内で、あるパッケージが別のパッケージの特定のバージョンに依存することを宣言できます。これにより、アプリケーションを複数の小さなパッケージに分割する「モジュラーアーキテクチャ」が実現可能になります。

例えば、「基盤ユーティリティパッケージ」を作成し、その上に「営業支援機能パッケージ」と「サービス支援機能パッケージ」を構築することができます。これにより、各チームは独立して開発を進めることができ、基盤ユーティリティの更新も管理された方法で行えます。

6. パッケージの祖先 (Package Ancestry)

2GP では、アップグレード可能性を保証するために「祖先」の概念が導入されています。新しいパッケージバージョンを作成する際、そのバージョンの「祖先」として以前のバージョンを指定します。これにより、Salesforce はコンポーネントの追加や変更は許可する一方で、破壊的な変更(例: Apex メソッドの削除、オブジェクトの削除)が既存のインストールに与える影響をチェックし、防ぐことができます。`sfdx-project.json` で `ancestorVersion` を指定することで、このアップグレードパスを定義します。


示例代码

2GP の操作は主に Salesforce CLI を通じて行われます。ここでは、典型的な `sfdx-project.json` の設定と、パッケージバージョンを作成・昇格させるまでの一連の CLI コマンドを、Salesforce 公式ドキュメントに基づき紹介します。

1. sfdx-project.json の設定例

このファイルはプロジェクトの心臓部です。パッケージの定義、依存関係、名前空間などをここで設定します。

{
  "packageDirectories": [
    {
      "path": "force-app", // デフォルトのパッケージディレクトリ
      "default": true,
      "package": "dreamhouse", // パッケージ名
      "versionName": "ver 0.1", // バージョン名(エイリアス)
      "versionNumber": "0.1.0.NEXT" // 次に作成されるバージョンの番号
    },
    {
      "path": "common-utils", // 別のパッケージディレクトリ
      "default": false,
      "package": "commonUtils", // 依存される側のパッケージ名
      "versionName": "ver 1.0",
      "versionNumber": "1.0.0.NEXT",
      "ancestorVersion": "1.0.0.0" // このバージョンの祖先を指定
    },
    {
      "path": "main-app", // 依存する側のパッケージディレクトリ
      "default": false,
      "package": "mainApp",
      "versionName": "ver 1.2",
      "versionNumber": "1.2.0.NEXT",
      "dependencies": [ // 依存関係の定義
        {
          "package": "commonUtils@1.0.0.0" // commonUtils パッケージの特定バージョンに依存
        }
      ]
    }
  ],
  "namespace": "my_namespace", // Dev Hub に登録した名前空間
  "sfdcLoginUrl": "https://login.salesforce.com",
  "sourceApiVersion": "58.0", // プロジェクトの API バージョン
  "packageAliases": { // パッケージID のエイリアス
    "dreamhouse": "0Ho...",
    "commonUtils": "0Ho...",
    "commonUtils@1.0.0.0": "04t...", // 特定バージョンのエイリアス
    "mainApp": "0Ho..."
  }
}

2. パッケージバージョン作成とプロモーションの CLI コマンド

以下のコマンドは、ターミナルで実行する一連の操作です。

# 1. パッケージを作成し、Dev Hub に登録します。
# このコマンドはパッケージごとに一度だけ実行します。
# --name: パッケージ名 (sfdx-project.json で定義)
# --path: パッケージのソースコードが格納されているディレクトリ
# --target-dev-hub: 認証済み Dev Hub 組織のエイリアス
sf package create --name "commonUtils" --package-type "Managed" --path "common-utils" --target-dev-hub MyDevHub

# 2. パッケージバージョンを作成します。
# このコマンドがソースコードのスナップショットをとり、Salesforce のビルド環境で検証します。
# --package: 作成するパッケージ名
# --installation-key: インストールキー(パスワード)
# --wait: コマンドが完了するまで待機する時間(分)
# --code-coverage: Apex コードカバレッジを計算し、75% 未満の場合は失敗させます。
sf package version create --package "commonUtils" --installation-key "MySuperSecretKey" --wait 20 --code-coverage --target-dev-hub MyDevHub

# 成功すると、"Successfully created the package version [08c...]. Subscriber Package Version Id: 04t..." のようなメッセージが表示されます。
# 04t から始まる ID が、インストールに使用するバージョンIDです。

# 3. 作成したベータ版パッケージをテストします。
# スクラッチ組織を作成し、そこにインストールして動作確認を行います。
sf org create scratch --definition-file config/project-scratch-def.json --alias TestOrg
sf package install --package "04t..." --target-org TestOrg --wait 10 --publish-wait 10 --installation-key "MySuperSecretKey"

# 4. テストが完了したら、パッケージバージョンをリリース済み (Promoted) に昇格させます。
# これにより、本番組織へのインストールが可能になります。
# --package: 昇格させるパッケージバージョンのID (04t...)
sf package version promote --package "04t..." --target-dev-hub MyDevHub

これらのコマンドを CI/CD スクリプトに組み込むことで、Git へのプッシュをトリガーとして、パッケージのビルド、テスト、リリースまでを自動化することが可能です。


注意事项

2GP をアーキテクチャに採用する際には、いくつかの重要な点に注意する必要があります。

1. 権限

Dev Hub 組織で 2GP を操作するユーザーには、「第二世代パッケージの作成と更新」システム権限が必要です。CI/CD プロセスで使用するインテグレーションユーザーにも、この権限と Salesforce CLI の認証設定が必須です。

2. API とガバナ制限

  • パッケージバージョン作成制限: 24時間以内に作成できるパッケージバージョンの数には制限があります(組織のエディションにより異なる)。CI/CD パイプラインを設計する際は、この制限を超えないように注意が必要です。例えば、すべてのコミットでバージョンを作成するのではなく、特定ブランチへのマージ時のみに限定するなどの工夫が考えられます。
  • スクラッチ組織の制限: Dev Hub ごとに有効なスクラッチ組織の数や、日次での作成数にも制限があります。開発チームの規模や CI/CD の実行頻度を考慮して、リソースを管理する必要があります。

3. エラーハンドリング

パッケージバージョンの作成は、様々な理由で失敗します。一般的な原因としては、Apex テストの失敗、コードカバレッジ不足、メタデータの依存関係の欠如(例: 有効化されていない機能に依存するコンポーネント)、無効なメタデータなどが挙げられます。エラー発生時には、`sf package version create list` コマンドで作成リクエストの ID を確認し、`sf package version create report --package-create-request-id ` で詳細なエラーログを取得することが重要です。

4. 破壊的変更 (Destructive Changes)

一度リリースしたパッケージバージョンに含まれるコンポーネント(Apex クラス、オブジェクト、項目など)を後のバージョンで削除することは「破壊的変更」と見なされ、原則として許可されません。これは、すでにそのコンポーネントをインストールして利用している顧客組織を保護するためです。コンポーネントを廃止したい場合は、`@deprecated` アノテーションを使用するなどして非推奨とし、将来のメジャーバージョンで削除する計画を立てる必要があります。どうしても削除が必要な場合は、新しいメジャーバージョンパッケージを作成するか、顧客に手動でのアンインストールと再インストールを依頼する必要があり、アーキテクチャ設計の初期段階でコンポーネントのライフサイクルを考慮することが極めて重要です。

5. 名前空間

管理パッケージ (Managed Package) を作成する場合、名前空間が必要です。一度パッケージが名前空間に紐づけられると、変更することはできません。名前空間は Dev Hub 組織全体でリンクされるため、どのパッケージで名前空間を使用するかは慎重に計画する必要があります。


まとめとベストプラクティス

Second-Generation Packaging (2GP) は、単なる新しいパッケージング技術ではなく、Salesforce 開発を現代の DevOps の世界に完全に統合するための戦略的なフレームワークです。アーキテクトとして 2GP を活用することで、より堅牢で、スケーラブルかつ保守性の高いソリューションを提供できます。

ベストプラクティス:

  1. モジュール性を最優先に設計する

    アプリケーションを、論理的に独立した疎結合な機能単位に分割し、それぞれを個別のパッケージとして設計します。これにより、各チームが並行して開発を進めやすくなり、一部の機能を更新する際にアプリケーション全体に影響が及ぶリスクを低減できます。

  2. Git を唯一の真実とする

    すべての開発プロセスは、Git 上のブランチ戦略から始まります。フィーチャーブランチ、開発ブランチ、リリースブランチなどを活用し、コードの変更履歴とパッケージのバージョン履歴を明確に対応付けます。

  3. CI/CD パイプラインによる完全自動化

    パッケージバージョンの作成、テスト用スクラッチ組織へのインストール、Apex テストの実行、そして成功時のベータ版プロモーションまでの一連の流れを、Jenkins, GitHub Actions, CircleCI などのツールで自動化します。これにより、手動エラーを排除し、迅速なフィードバックサイクルを実現します。

  4. 明確なバージョン管理戦略を持つ

    セマンティックバージョニング (MAJOR.MINOR.PATCH) のような規約を採用し、バージョンの変更が何を意味するのか(破壊的変更、機能追加、バグ修正)を明確にします。これは、パッケージの利用者にとって非常に重要です。

  5. 依存関係の地獄 (Dependency Hell) を避ける

    パッケージ間の依存関係ツリーは、できるだけ浅く、シンプルに保つよう努めます。複雑な依存関係は、あるパッケージの小さな変更が、他の多くのパッケージの再ビルドを要求する連鎖反応を引き起こす可能性があります。

2GP への移行は、単なるツールの変更ではなく、開発文化の変革を伴います。しかし、この変革を受け入れることで、Salesforce エコシステム全体で求められる、より高いレベルのアジリティと品質を達成することができるでしょう。

コメント