Salesforceを宣言的に開発していたSalesforce初心者の私が、今さらながらSalesforce DXをさわってみた

エイチームフィナジーのアドベントカレンダー6日目は、hondamが担当します！

# はじめに
弊社ではSalesforceを導入して1年ほどになりますが、普段は宣言的に画面からポチポチ修正しています。特に変更セットを使ってSandbox環境から本番環境へ反映はしておらず、システム管理者が直接本番環境を修正するケースが多かったりします。ただ、以前から誰がどう変更したかの把握や、修正のロールバック、複雑な画面修正を行いたい...みたいな話は出ていました。そこで、いまさらですがSalesforce DXでソース駆動開発できたら幸せそうだし使ってみようということです。

# Salesforce DXとは
Salesforce Developer eXperienceでSalesforce DXです(以下SFDX)。デジタルトランスフォーメーションのDXではなく、開発体験のDXです。つまりSalesforce(企業)がSalesforce(プラットフォーム)の開発・保守を、もっと気持ちよく楽しめるようにした、ということですね。そんなSFDXでは、以下5つの要素があります。

* ソース駆動開発
    * 近年のWebアプリケーション開発のように、コードリポジトリを正とし、コード、メタデータ等を共有しながら開発可能に
* Salesforce CLI
    * コマンドラインからシェルベースで各種環境に対して操作等が可能に。これにより自動化やCIサーバ連携が可能に
3. Salesforce Extensions for VS Code
    * VSCodeにてApexやVisualforce、Lightning Web Componentのコード補完やシンタックスハイライト、デバッグなどが可能に。また上記Salesforce CLIの呼び出しも可能。
4. スクラッチ組織
    * Salesforce CLIで制御可能な使い捨ての開発環境が利用可能に。ソース変更の追跡も可能。
5. 第二世代パッケージ
    * CLIでパッケージ作成して、CLIでインストール可能に。

# Salesforce DXを利用するためのセットアップ
Salesforce DXを利用するには、以下のセットアップが必要です。

* [VSCode](https://code.visualstudio.com/) のインストール
* VSCodeへ[Salesforce Extenstion Pack](https://marketplace.visualstudio.com/items?itemName=salesforce.salesforcedx-vscode) のインストール
* [Salesforce CLI](https://developer.salesforce.com/tools/sfdxcli) のインストール
* スクラッチ組織を使うためにDev Hub機能の有効化
    * [設定] > [クイック検索]でDev Hub > Dev Hubを有効化

# Salesforce CLIで出来ること
Salesforce CLIではSalesforce組織やスクラッチ組織、ローカル環境に対して様々なことが可能です。

* Salesforce組織を認証する
* Salesforce組織をブラウザで開く
* Salesforce DX プロジェクトを作成する
* Salesforce組織からソース形式でメタデータを取得する
* Salesforce組織へソースをデプロイする
* スクラッチ組織を作成する
* スクラッチ組織を削除する
* スクラッチ組織からソースをプルする
* スクラッチ組織へソースをプッシュする
* LWCやAuraコンポーネント、Apexクラス・トリガー等のスキャフォールド
* LWCやApexのテスト実行
* レコードに対するCRUD
* SOQLクエリーの実行
* パッケージの作成、インストール、アンインストール
* ...etc

# Salesforce DX プロジェクトに関して
SFDXでは、新しいプロジェクト構造が導入されており、固有のプロジェクト構造とソース形式があります。これらのソースをリポジトリで管理しながら、スクラッチ組織や本番環境へデプロイすることになります。

Salesforce DX プロジェクトは以下のようなコマンドで作成できます。

```sh
// マニフェストファイル付きのプロジェクト名なSalesforce DX プロジェクトを作成する
$ sfdx force:project:create -n <プロジェクト名> -x
```

プロジェクト構造は以下のようになります。

```sh
.
├── README.md
├── config
│   └── project-scratch-def.json     // スクラッチ組織定義ファイル
├── force-app
│   └── main
│       └── default
│           ├── applications         // アプリケーション
│           ├── aura                 // Aura コンポーネント
│           ├── classes              // Apex クラス
│           ├── contentassets        // アセットファイル
│           ├── flexipages           // Lightning ページ
│           ├── layouts              // ページレイアウト
│           ├── lwc                  // Lightning Web コンポーネント
│           ├── objects              // オブジェクト
│           ├── permissionsets       // 権限セット
│           ├── staticresources      // 静的リソース
│           ├── tabs                 // タブ
│           └── triggers             // Apex トリガ
├── jest.config.js
├── manifest
│   └── package.xml                  // マニフェストファイル
├── package.json
├── scripts
│   ├── apex
│   │   └── hello.apex
│   └── soql
│       └── account.soql
└── sfdx-project.json                 // プロジェクト設定ファイル
```

以下はプロジェクト設定ファイルの例です。設定ファイルの詳細は[こちら](https://developer.salesforce.com/docs/atlas.ja-jp.sfdx_dev.meta/sfdx_dev/sfdx_dev_ws_config.htm)をご覧ください。

```json
{
  "packageDirectories": [
    {
      "path": "force-app",
      "default": true
    }
  ],
  "name": "MyProject",
  "namespace": "",
  "sfdcLoginUrl": "https://login.salesforce.com",
  "sourceApiVersion": "52.0"
}
```

マニフェストファイルには取得またはリリースする対象のメタデータを指定します。
以下は標準のLeadオブジェクトを対象とするマニフェストファイルの例です。

```xml
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<Package xmlns="http://soap.sforce.com/2006/04/metadata">
    <types>
        <members>Lead</members>
        <name>CustomObject</name>
    </types>
    <version>52.0</version>
</Package>
```

マニフェストファイルに設定したメタデータを取得するには以下のようなコマンドを利用します。

```
// マニフェストファイルに定義されているメタデータコンポーネントを取得する
$ sfdx force:source:retrieve -u <ターゲットユーザ名> -x <マニフェストファイル>
FULL NAME                  TYPE          PROJECT PATH
─────────────────────────  ────────────  ──────────────────────────────────────────────────────────────────────────────
Lead                       CustomObject  force-app/main/default/objects/Lead/Lead.object-meta.xml
...
```

コマンドの例では標準オブジェクトのLeadのメタデータを取得していますので、メタデータファイルは`force-app/main/default/objects`以下に作成されているのがわかります。

# スクラッチ組織に関して

* 有効期限は最大30日
* CLIで作成時に 1 ～ 30 日の期間を選択可能
* デフォルトでは7日に設定されている
* スクラッチ組織の有効期限が切れると、復元できなくなる
* Salesforceのエディションにより、作成可能数が違う

スクラッチ組織の割り当て済み数と残数を確認する

```sh
$ sfdx force:limits:api:display -u <DevHubユーザ名 or エイリアス>
Name                                         Remaining  Max
───────────────────────────────────────────  ─────────  ─────────
// 現在使用中のスクラッチ組織
ActiveScratchOrgs                            5          5
// 1日に作成可能なスクラッチ組織
DailyScratchOrgs                             10         10
```

スクラッチ組織は以下のようなコマンドで作成できます。

```sh
// スクラッチ組織を作成する例
$ sfdx force:org:create -f <スクラッチ組織定義ファイル> -a <エイリアス>
Successfully created scratch org: XXXXXXXXX, username: test-XXXXXXX@example.com
```

どのようなスクラッチ組織を作成するかは、スクラッチ組織定義ファイルである程度設定可能です。
ここでは割愛しますが、詳細は[こちら](https://developer.salesforce.com/docs/atlas.ja-jp.sfdx_dev.meta/sfdx_dev/sfdx_dev_scratch_orgs_def_file.htm)をご覧ください。

では作成したスクラッチ組織にソースを反映してみましょう。
スクラッチ組織へのソースの反映は以下のようなコマンドで実行できます。

```sh
$ sfdx force:source:push -u <ターゲットユーザ名>
*** Deploying with SOAP ***
Job ID | 0Af1m00000OoGnGCAV
SOURCE PROGRESS | ████████████████████████████████████████ | 39/39 Components
=== Pushed Source
STATE  FULL NAME                  TYPE          PROJECT PATH
─────  ─────────────────────────  ────────────  ──────────────────────────────────────────────────────────────────────────────
Add    Lead                       CustomObject  force-app/main/default/objects/Lead/Lead.object-meta.xml
...
```

さきほど例で`force:source:retrieve`した標準のLeadオブジェクトのメタデータがスクラッチ組織にpushされているのがわかります。

スクラッチ組織ではコマンドによる差分の確認が可能です。
例えば、以下のようなコマンドでLWCを作成したとします。

```sh
// mycomponentというLWCを作成
$ sfdx force:lightning:component:create -n mycomponent --type=lwc
target dir = /<Path>/force-app/main/default/lwc
   create mycomponent/mycomponent.js
   create mycomponent/mycomponent.html
   create mycomponent/mycomponent.js-meta.xml
```

ここで先ほどのスクラッチ組織との差分を見てみましょう。
以下のコマンドでスクラッチ組織とローカル環境の差分が確認できます。LWCが追加されているのがわかりますね。

```
$ sfdx force:source:status -u <ターゲットユーザ名>
=== Source Status
STATE      FULL NAME                     TYPE                      PROJECT PATH
─────────  ────────────────────────────  ────────────────────────  ──────────────────────────────────────────────────────────────
Local Add  mycomponent/mycomponent.html  LightningComponentBundle  force-app/main/default/lwc/mycomponent/mycomponent.html
Local Add  mycomponent/mycomponent.js    LightningComponentBundle  force-app/main/default/lwc/mycomponent/mycomponent.js
Local Add  mycomponent/mycomponent.js    LightningComponentBundle  force-app/main/default/lwc/mycomponent/mycomponent.js-meta.xml
```

ではこのLWCをスクラッチ組織にpushしてみて、再度差分を確認してみましょう。
以下のコマンドを実施すると、差分がなくなったのがわかりますね。

```sh
$ sfdx force:source:push -u <ターゲットユーザ名>
*** Deploying with SOAP ***
Job ID | 0Af1m00000OoRkrCAF
SOURCE PROGRESS | ████████████████████████████████████████ | 1/1 Components
=== Pushed Source
STATE  FULL NAME                     TYPE                      PROJECT PATH
─────  ────────────────────────────  ────────────────────────  ──────────────────────────────────────────────────────────────
Add    mycomponent/mycomponent.html  LightningComponentBundle  force-app/main/default/lwc/mycomponent/mycomponent.html
Add    mycomponent/mycomponent.js    LightningComponentBundle  force-app/main/default/lwc/mycomponent/mycomponent.js
Add    mycomponent/mycomponent.js    LightningComponentBundle  force-app/main/default/lwc/mycomponent/mycomponent.js-meta.xml

$ sfdx force:source:status -u <ターゲットユーザ名>
=== Source Status
No results found
```

当然のようにローカル環境で変更をすると、スクラッチ組織と差分が生まれ、その差異はコマンドで確認することが可能です。では逆にスクラッチ組織を直接修正するとどうなるでしょう？試しに標準Leadオブジェクトにオブジェクトマネージャからカスタム項目を追加してみます。

![スクリーンショット 2021-12-03 11.11.19.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/4305/eaf8c870-caac-59b0-4d56-70a569e9d568.png)

あらためて、スクラッチ組織とローカル環境の差分を確認すると...
さきほど画面から追加したカスタム項目が追加されていることがわかりますね！

```sh
$ sfdx force:source:status -u <ターゲットユーザ名>
=== Source Status
STATE       FULL NAME                     TYPE         PROJECT PATH
──────────  ────────────────────────────  ───────────  ────────────
Remote Add  Lead.Field1__c                CustomField
```

ではマニフェストファイルに記載のないデータが修正されるとどうなるでしょう？
同じように画面から今度は標準Accountオブジェクトにカスタム項目を追加しました。

```sh
// スクラッチ組織とローカル環境の差分を確認
$ sfdx force:source:status -u <ターゲットユーザ名>
=== Source Status
STATE       FULL NAME                           TYPE         PROJECT PATH
──────────  ──────────────────────────────────  ───────────  ────────────
Remote Add  Account.Field1__c                   CustomField

// カスタム項目が追加されていたので、pull
$ sfdx force:source:pull -u <ターゲットユーザ名>
=== Pulled Source
STATE  FULL NAME                               TYPE         PROJECT PATH
─────  ──────────────────────────────────────  ───────────  ─────────────────────────────────────────────────────────────────────────────────────
Add    Account.Field1__c                       CustomField  force-app/main/default/objects/Account/fields/Field1__c.field-meta.xml
```

ローカル環境でマニフェストファイルに記載のない標準Contactオブジェクトにカスタム項目を追加してみます。

```sh
// Contactオブジェクト用のディレクトリを作って、先ほどのカスタム項目をコピーします
$ pwd /<Path>/force-app/main/default/objects
$ mkdir -p Contact/fields
$ cp Account/fields/Field1__c.field-meta.xml Contact/fields/

// 差分の確認
$ sfdx force:source:status -u <ターゲットユーザ名>
=== Source Status
STATE      FULL NAME          TYPE         PROJECT PATH
─────────  ─────────────────  ───────────  ──────────────────────────────────────────────────────────────────────
Local Add  Contact.Field1__c  CustomField  force-app/main/default/objects/Contact/fields/Field1__c.field-meta.xml

// Push
$ sfdx force:source:push -u <ターゲットユーザ名>
*** Deploying with SOAP ***
Job ID | 0Af1m00000OoRqWCAV
SOURCE PROGRESS | ████████████████████████████████████████ | 1/1 Components
=== Pushed Source
STATE  FULL NAME          TYPE         PROJECT PATH
─────  ─────────────────  ───────────  ──────────────────────────────────────────────────────────────────────
Add    Contact.Field1__c  CustomField  force-app/main/default/objects/Contact/fields/Field1__c.field-meta.xml

// 画面で確認
$ sfdx force:org:open -u <ターゲットユーザ名>
```

追加されているのがわかりますね！

![スクリーンショット_2021-12-03_11_39_32.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/4305/05eed597-5332-ef8b-dab2-278f9f60980a.png)

最後に、本番環境に反映してみましょう。
SFDXによるデプロイはマニフェストファイルを指定する、メタデータを指定する、ソースパスを指定するなど様々ありますが、今回は標準オブジェクトを変更したのでソースパス指定でデプロイしてみます。
スクラッチ組織で変更したオブジェクトのカスタム項目がデプロイされてますね！

```sh
// オブジェクトを指定してターゲットにデプロイ
$ sfdx force:source:deploy -u <ターゲットユーザ名> -p ./force-app/main/default/objects
*** Deploying with SOAP API ***
Deploy ID: 0Af5h000007GD7QCAW
SOURCE PROGRESS | ██████████████████████████████████████░░ | 41/43 Components

=== Deployed Source
FULL NAME                  TYPE          PROJECT PATH
─────────────────────────  ────────────  ──────────────────────────────────────────────────────────────────────────────
Account.Field1__c          CustomField   force-app/main/default/objects/Account/fields/Field1__c.field-meta.xml
Contact.Field1__c          CustomField   force-app/main/default/objects/Contact/fields/Field1__c.field-meta.xml
Lead.Field1__c             CustomField   force-app/main/default/objects/Lead/fields/Field1__c.field-meta.xml
...
```

基本はこの流れスクラッチ組織で開発、本番環境へデプロイの繰り返しになりますが、折角なのでCIサーバ連携してみましょう。

# CIサーバ連携
この記事ではSalesforce DXプロジェクトの作成からはじめましたが、本来であればGithub等の何かしらのコードリポジトリでコードを管理し、使用する場合にcloneしていると思います。

Salesforce及びCLIは`OAuth 2.0 JWT ベアラー認証フロー`に対応しているため、Salesforceのログイン画面での認証を利用せずに、組織に対してSFDXコマンドを利用することが可能です。

そのため、CIサーバなどからコードリポジトリで管理しているSalesforce DXプロジェクトを各種環境へデプロイすることが出来ます。

例えばCircleCIの場合、以下のセットアップでCIサーバから各種環境へCLI実行が可能となります。

* [非公開鍵と自己署名デジタル証明書の作成する](https://developer.salesforce.com/docs/atlas.ja-jp.sfdx_dev.meta/sfdx_dev/sfdx_dev_auth_key_and_cert.htm)
* [Dev Hub 組織用の接続アプリケーションの作成し、JWT 認証を有効にする](https://developer.salesforce.com/docs/atlas.ja-jp.sfdx_dev.meta/sfdx_dev/sfdx_dev_auth_connected_app.htm)
* CircleCI にSalesorce DXプロジェクトを追加する
* 必要な環境変数 `SFDX_JWT_KEY` と `SFDX_CONSUMER_KEY` を追加する
* `.circleci/config.yml`を設定する

https://circleci.com/developer/ja/orbs/orb/circleci/salesforce-sfdx

| CircleCIでデプロイしている様子 |
|---|
|![スクリーンショット 2021-12-03 15.24.33.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/4305/45ad6f9e-9450-41bd-1fdf-d3d952b811ba.png) |

# まとめ
ということでCIサーバ連携までやってみましたが、近年のWebアプリ開発よろしく以下の図ような流れでSalesforceの開発をしていくんだろうなと思いました。

![スクリーンショット 2021-12-04 12.15.37.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/4305/3e1d1499-8820-ec25-11ea-b366afe84811.png)


実際、まだSFDXを使って開発していないので、世の中のSalesforceを利用している方々が日々どう運用しているかってのは正直わかりません。完全にコードのみで運用しているのか、今まで通りに宣言的に開発することもあるのか、半々くらいなのかなど。とはいえ冒頭に書いた悩みなどはクリアできそうなので、積極的に使っていければと思っています。

この記事が、まだSalesforce DXを使ったことのないSalesforce開発者の助けになれば幸いです。

# 参考情報
* [Salesforce アプリケーションの継続的インテグレーション \- CircleCI](https://circleci.com/ja/blog/continuous-integration-for-salesforce-applications/)