SlideShare ist ein Scribd-Unternehmen logo
1 von 35
Heroku Connect
苦肉の四苦八苦
大久保英樹
自己紹介
• 名前:大久保英樹
• 職業:フリーランスプログラマ
• アカウント:@Oakbow7
• Herokuを扱う案件は8個目くらい?
• Heroku Connectを扱う案件は4個目くらい?
4U Lifecareとなでしこナース
• フォー・ユー・ライフケア株式会社
• https://4ulifecare.com/
• 医療系スタートアップ
• 活用が限定されている医療人材を活かす
• なでしこナース
• https://nadeshikonurse.jp/
• 看護師向けの人材紹介サービス
• 利用ユーザは看護師と病院・施設
なでしこナース
病院・施設看護師
運営者
管理
管理
求人に応募
採用
Heroku Salesforce
Salesforce
なでしこナース
Salesforce
Partner
Commyunity
病院・施設
Heroku
看護師
Salesfoce
運営
Heroku
Connect
今日お話しすること
• Heroku Connectおさらい
• インピーダンスミスマッチその1
• Heroku と Salesforceの双方向同期で、IDをどうするか
• インピーダンスミスマッチその2
• Salesforceのキャメルケース文化とRailsのスネークケース文化の
ギャップの解消
• 現在の開発サイクル
Heroku Connectおさらい
Heroku
Connect
Heroku
Postgres Salesfoce
Heroku Connectおさらい
• SalesforceのオブジェクトとHeroku Postgresのテーブルを双
方向に同期してくれるアドオン
• あくまでも主となるのはSalesforceオブジェクト。同期対象の
オブジェクトとそのカラムをHeroku Connectコンソールで選
択すると、オブジェクトに対応したテーブルが自動生成される。
逆方向はできない
• Heroku PostgreSQL -> Salesforceの同期時、Salesforceの
ヴァリデーションに引っかかると同期エラーが起きる。
Heroku Connectおさらい
• テーブル作成はHeroku Connectを介してしか行えない(フ
レームワークのマイグレーション機能は原則そのまま使えな
い)
• Salesforce側のオブジェクト定義に依存するので、テーブル名、
カラム名やデータ型、データ長さは変更できない
• 親と子どちらのレコードが先に同期されるか分からないので、
FK(外部参照制約)は使えない(貼ることは可能だが、FKエ
ラーで同期失敗する)
• サロゲートキーとしてIDカラムが生成されるが、Salesforce側
には同期されない
Heroku Connectおさらい
Heroku
Connect
Heroku
Postgres
Salesfoce
Rails
Railsの期待する
データベーススキーマ(理想)
Heroku Connectが作る
データベーススキーマ(現実)
Heroku Postgres
Heroku Postgres Salesforce
id
sfid ← sfid
mofid__c ⇄ mofID__c
body__c ⇄ Body__c
read__c ⇄ Read__c
name ⇄ Name
usermofid__c ⇄ UserMofId__c
user__c ⇄ User__c
conversationmofid__c ⇄ ConversationMofId__c
conversation__c ⇄ Conversation__c
createddate ⇄ CreatedDate
systemmodstamp ⇄ SystemModStamp
isdeleted ⇄ IsDeleted
conversations__r__mofid__c
user__r__mofid__c
インピーダンスミスマッチその1
• PKをうまく同期できない
• Salesforce側のPKはsfid
• Heroku Postgres側のPKはid
インピーダンスミスマッチその1
• 結合にsfidを使用した場合
• Heroku側でレコードをInsert(sfidはNULL)
• InsertしたレコードをSalesforceに同期
• Salesforceでsfidを発番
• 更新したレコードをHeroku Postgresに同期
• PKを取得するために同期が二回必要
• HerokuにとってはSalesforceは所詮外部サービスなので、PKが外部
依存なのは開発する上で非常に不利(ローカルでどう開発する?テ
ストは?CIで回せる?)
インピーダンスミスマッチその1
インピーダンスミスマッチその1解決編
• 結合に外部IDを使う(データ型は文字列型)
• Heroku側でレコードをInsertするときはPostgreSQLのシーケンスを
突っ込む
• Salesforce側でレコードをInsertするときは、トリガでsfidを突っ込
む(1トランザクションで実行するよう頑張る)
• idでもsfidでもない外部IDカラムexernal_id(ExternalId__c)を作り、
これを同期する。
• Heroku Postgres ではこのカラムをPKに指定(照合順序に注意)
• Salesforceではこのカラムを外部IDとして設定する
インピーダンスミスマッチその1解決編
インピーダンスミスマッチその1解決編
インピーダンスミスマッチその1解決編
インピーダンスミスマッチその1解決編
インピーダンスミスマッチその1解決編
インピーダンスミスマッチその1解決編
インピーダンスミスマッチその1解決編2
インピーダンスミスマッチその2
• Salesforceはキャメルケース文化なので、オブジェクトや
フィールド名は基本的にキャメルケース
• カスタムオブジェクトやカスタムフィールドは、末尾に__cが
必ずつく(キモい)
• PostgreSQLのテーブル名やカラム名は大文字小文字を区別し
ない(ので、小文字で表現される)
• Railsはスネークケース文化
• レコードの作成日時、更新日時のフィールド名(カラム名)は
それぞれ異なっている
インピーダンスミスマッチその2
Rails(理想) PostgreSQL(現実) Salesforce
updated_at systemmodstamp SystemModStamp
created_at createddate CreatedDate
work_location_address1 worklocationaddress1__c WorkLocationAddress1__c
contract_start_on contractstartdate__c ContractStartDate__c
emergency_contact_details emergencycontactdetails__c EmergencyContactDetails__c
id externalid ExternalId
opportunity_technical_skill opportunitytechnicalskill__c OpportunityTechnicalSkill__c
インピーダンスミスマッチその2解決編
Rails Model
DB View
(Updatable
View)
DB Table
インピーダンスミスマッチその2解決編
インピーダンスミスマッチその2解決編
インピーダンスミスマッチその2解決編
インピーダンスミスマッチその2解決編
インピーダンスミスマッチその2解決編
インピーダンスミスマッチその2解決編
環境 環境変数 Salesforce テーブル作成 カラム追加 カラム削除
ローカル開発 development なし される される される
ローカルテス
ト
test なし される される される
CI test なし される される される
Review App production なし される される される
ステージング production あり されない されない されない
本番 production あり されない されない されない
ミスマッチを解消
Heroku
Connect
Heroku
Postgres
Salesfoce
Rails
Railsで扱いやすいように作った
更新可能ビュー
Heroku Connectが作る
salesforceスキーマ
Heroku Postgres
現在の開発サイクル
Review App
feature branchlocal Circle CI
master branch Staging
Production
Pull Request Auto Testing
Merge
Auto Deployment
Promoting
Create Review App
現在の開発サイクル

Weitere ähnliche Inhalte

Was ist angesagt?

エンジニアの個人ブランディングと技術組織
エンジニアの個人ブランディングと技術組織エンジニアの個人ブランディングと技術組織
エンジニアの個人ブランディングと技術組織Takafumi ONAKA
 
Azure Api Management 俺的マニュアル 2020年3月版
Azure Api Management 俺的マニュアル 2020年3月版Azure Api Management 俺的マニュアル 2020年3月版
Azure Api Management 俺的マニュアル 2020年3月版貴志 上坂
 
ウェブパフォーマンスの基礎とこれから
ウェブパフォーマンスの基礎とこれからウェブパフォーマンスの基礎とこれから
ウェブパフォーマンスの基礎とこれからHiroshi Kawada
 
Webアプリを並行開発する際のマイグレーション戦略
Webアプリを並行開発する際のマイグレーション戦略Webアプリを並行開発する際のマイグレーション戦略
Webアプリを並行開発する際のマイグレーション戦略Takayuki Shimizukawa
 
ソフトウェア開発における『知の高速道路』
ソフトウェア開発における『知の高速道路』ソフトウェア開発における『知の高速道路』
ソフトウェア開発における『知の高速道路』Yoshitaka Kawashima
 
Goで実装した UPSIDERの決済金額リミット機能
Goで実装した UPSIDERの決済金額リミット機能 Goで実装した UPSIDERの決済金額リミット機能
Goで実装した UPSIDERの決済金額リミット機能 Miki Masumoto
 
Easybuggy(バグ)の召し上がり方
Easybuggy(バグ)の召し上がり方Easybuggy(バグ)の召し上がり方
Easybuggy(バグ)の召し上がり方広平 田村
 
データ分析基盤、どう作る?システム設計のポイント、教えます - Developers.IO 2019 (20191101)
データ分析基盤、どう作る?システム設計のポイント、教えます - Developers.IO 2019 (20191101)データ分析基盤、どう作る?システム設計のポイント、教えます - Developers.IO 2019 (20191101)
データ分析基盤、どう作る?システム設計のポイント、教えます - Developers.IO 2019 (20191101)Yosuke Katsuki
 
イミュータブルデータモデル(世代編)
イミュータブルデータモデル(世代編)イミュータブルデータモデル(世代編)
イミュータブルデータモデル(世代編)Yoshitaka Kawashima
 
グラフデータベース入門
グラフデータベース入門グラフデータベース入門
グラフデータベース入門Masaya Dake
 
オブジェクト指向エクササイズのススメ
オブジェクト指向エクササイズのススメオブジェクト指向エクササイズのススメ
オブジェクト指向エクササイズのススメYoji Kanno
 
モノタロウの1900万商品を検索する Elasticsearch構築運用事例(2022-10-26 第50回Elasticsearch 勉強会発表資料)
モノタロウの1900万商品を検索する Elasticsearch構築運用事例(2022-10-26 第50回Elasticsearch 勉強会発表資料)モノタロウの1900万商品を検索する Elasticsearch構築運用事例(2022-10-26 第50回Elasticsearch 勉強会発表資料)
モノタロウの1900万商品を検索する Elasticsearch構築運用事例(2022-10-26 第50回Elasticsearch 勉強会発表資料)株式会社MonotaRO Tech Team
 
営業さんまで、社員全員がSQLを使う 「越境型組織」 ができるまでの3+1のポイント | リブセンス
営業さんまで、社員全員がSQLを使う 「越境型組織」 ができるまでの3+1のポイント | リブセンス営業さんまで、社員全員がSQLを使う 「越境型組織」 ができるまでの3+1のポイント | リブセンス
営業さんまで、社員全員がSQLを使う 「越境型組織」 ができるまでの3+1のポイント | リブセンスLivesense Inc.
 
SQLアンチパターン - 開発者を待ち受ける25の落とし穴 (拡大版)
SQLアンチパターン - 開発者を待ち受ける25の落とし穴 (拡大版)SQLアンチパターン - 開発者を待ち受ける25の落とし穴 (拡大版)
SQLアンチパターン - 開発者を待ち受ける25の落とし穴 (拡大版)Takuto Wada
 
ジャストシステムJava100本ノックのご紹介
ジャストシステムJava100本ノックのご紹介ジャストシステムJava100本ノックのご紹介
ジャストシステムJava100本ノックのご紹介JustSystems Corporation
 
オンラインゲームの仕組みと工夫
オンラインゲームの仕組みと工夫オンラインゲームの仕組みと工夫
オンラインゲームの仕組みと工夫Yuta Imai
 
Apache Airflow 概要(Airflowの基礎を学ぶハンズオンワークショップ 発表資料)
Apache Airflow 概要(Airflowの基礎を学ぶハンズオンワークショップ 発表資料)Apache Airflow 概要(Airflowの基礎を学ぶハンズオンワークショップ 発表資料)
Apache Airflow 概要(Airflowの基礎を学ぶハンズオンワークショップ 発表資料)NTT DATA Technology & Innovation
 
イベント・ソーシングを知る
イベント・ソーシングを知るイベント・ソーシングを知る
イベント・ソーシングを知るShuhei Fujita
 
Kubernetesでの性能解析 ~なんとなく遅いからの脱却~(Kubernetes Meetup Tokyo #33 発表資料)
Kubernetesでの性能解析 ~なんとなく遅いからの脱却~(Kubernetes Meetup Tokyo #33 発表資料)Kubernetesでの性能解析 ~なんとなく遅いからの脱却~(Kubernetes Meetup Tokyo #33 発表資料)
Kubernetesでの性能解析 ~なんとなく遅いからの脱却~(Kubernetes Meetup Tokyo #33 発表資料)NTT DATA Technology & Innovation
 

Was ist angesagt? (20)

エンジニアの個人ブランディングと技術組織
エンジニアの個人ブランディングと技術組織エンジニアの個人ブランディングと技術組織
エンジニアの個人ブランディングと技術組織
 
Azure Api Management 俺的マニュアル 2020年3月版
Azure Api Management 俺的マニュアル 2020年3月版Azure Api Management 俺的マニュアル 2020年3月版
Azure Api Management 俺的マニュアル 2020年3月版
 
ウェブパフォーマンスの基礎とこれから
ウェブパフォーマンスの基礎とこれからウェブパフォーマンスの基礎とこれから
ウェブパフォーマンスの基礎とこれから
 
Webアプリを並行開発する際のマイグレーション戦略
Webアプリを並行開発する際のマイグレーション戦略Webアプリを並行開発する際のマイグレーション戦略
Webアプリを並行開発する際のマイグレーション戦略
 
ソフトウェア開発における『知の高速道路』
ソフトウェア開発における『知の高速道路』ソフトウェア開発における『知の高速道路』
ソフトウェア開発における『知の高速道路』
 
TLS, HTTP/2演習
TLS, HTTP/2演習TLS, HTTP/2演習
TLS, HTTP/2演習
 
Goで実装した UPSIDERの決済金額リミット機能
Goで実装した UPSIDERの決済金額リミット機能 Goで実装した UPSIDERの決済金額リミット機能
Goで実装した UPSIDERの決済金額リミット機能
 
Easybuggy(バグ)の召し上がり方
Easybuggy(バグ)の召し上がり方Easybuggy(バグ)の召し上がり方
Easybuggy(バグ)の召し上がり方
 
データ分析基盤、どう作る?システム設計のポイント、教えます - Developers.IO 2019 (20191101)
データ分析基盤、どう作る?システム設計のポイント、教えます - Developers.IO 2019 (20191101)データ分析基盤、どう作る?システム設計のポイント、教えます - Developers.IO 2019 (20191101)
データ分析基盤、どう作る?システム設計のポイント、教えます - Developers.IO 2019 (20191101)
 
イミュータブルデータモデル(世代編)
イミュータブルデータモデル(世代編)イミュータブルデータモデル(世代編)
イミュータブルデータモデル(世代編)
 
グラフデータベース入門
グラフデータベース入門グラフデータベース入門
グラフデータベース入門
 
オブジェクト指向エクササイズのススメ
オブジェクト指向エクササイズのススメオブジェクト指向エクササイズのススメ
オブジェクト指向エクササイズのススメ
 
モノタロウの1900万商品を検索する Elasticsearch構築運用事例(2022-10-26 第50回Elasticsearch 勉強会発表資料)
モノタロウの1900万商品を検索する Elasticsearch構築運用事例(2022-10-26 第50回Elasticsearch 勉強会発表資料)モノタロウの1900万商品を検索する Elasticsearch構築運用事例(2022-10-26 第50回Elasticsearch 勉強会発表資料)
モノタロウの1900万商品を検索する Elasticsearch構築運用事例(2022-10-26 第50回Elasticsearch 勉強会発表資料)
 
営業さんまで、社員全員がSQLを使う 「越境型組織」 ができるまでの3+1のポイント | リブセンス
営業さんまで、社員全員がSQLを使う 「越境型組織」 ができるまでの3+1のポイント | リブセンス営業さんまで、社員全員がSQLを使う 「越境型組織」 ができるまでの3+1のポイント | リブセンス
営業さんまで、社員全員がSQLを使う 「越境型組織」 ができるまでの3+1のポイント | リブセンス
 
SQLアンチパターン - 開発者を待ち受ける25の落とし穴 (拡大版)
SQLアンチパターン - 開発者を待ち受ける25の落とし穴 (拡大版)SQLアンチパターン - 開発者を待ち受ける25の落とし穴 (拡大版)
SQLアンチパターン - 開発者を待ち受ける25の落とし穴 (拡大版)
 
ジャストシステムJava100本ノックのご紹介
ジャストシステムJava100本ノックのご紹介ジャストシステムJava100本ノックのご紹介
ジャストシステムJava100本ノックのご紹介
 
オンラインゲームの仕組みと工夫
オンラインゲームの仕組みと工夫オンラインゲームの仕組みと工夫
オンラインゲームの仕組みと工夫
 
Apache Airflow 概要(Airflowの基礎を学ぶハンズオンワークショップ 発表資料)
Apache Airflow 概要(Airflowの基礎を学ぶハンズオンワークショップ 発表資料)Apache Airflow 概要(Airflowの基礎を学ぶハンズオンワークショップ 発表資料)
Apache Airflow 概要(Airflowの基礎を学ぶハンズオンワークショップ 発表資料)
 
イベント・ソーシングを知る
イベント・ソーシングを知るイベント・ソーシングを知る
イベント・ソーシングを知る
 
Kubernetesでの性能解析 ~なんとなく遅いからの脱却~(Kubernetes Meetup Tokyo #33 発表資料)
Kubernetesでの性能解析 ~なんとなく遅いからの脱却~(Kubernetes Meetup Tokyo #33 発表資料)Kubernetesでの性能解析 ~なんとなく遅いからの脱却~(Kubernetes Meetup Tokyo #33 発表資料)
Kubernetesでの性能解析 ~なんとなく遅いからの脱却~(Kubernetes Meetup Tokyo #33 発表資料)
 

Mehr von Hideki Ohkubo

Rubyist のフィリピン留学
Rubyist のフィリピン留学Rubyist のフィリピン留学
Rubyist のフィリピン留学Hideki Ohkubo
 
10年後になくなる仕事 / Jobs which will disappear 10 years later
10年後になくなる仕事 / Jobs which will disappear 10 years later 10年後になくなる仕事 / Jobs which will disappear 10 years later
10年後になくなる仕事 / Jobs which will disappear 10 years later Hideki Ohkubo
 
Job-Hub 自由をつかめ(大人の事情ver)
Job-Hub 自由をつかめ(大人の事情ver)Job-Hub 自由をつかめ(大人の事情ver)
Job-Hub 自由をつかめ(大人の事情ver)Hideki Ohkubo
 
Heroku meetup#11(フル)
Heroku meetup#11(フル)Heroku meetup#11(フル)
Heroku meetup#11(フル)Hideki Ohkubo
 
Heroku meetup#11(lt)
Heroku meetup#11(lt)Heroku meetup#11(lt)
Heroku meetup#11(lt)Hideki Ohkubo
 
おいしいherokuの使い方
おいしいherokuの使い方おいしいherokuの使い方
おいしいherokuの使い方Hideki Ohkubo
 
Cacooではじめるスタートアップ
CacooではじめるスタートアップCacooではじめるスタートアップ
CacooではじめるスタートアップHideki Ohkubo
 

Mehr von Hideki Ohkubo (8)

Rubyist のフィリピン留学
Rubyist のフィリピン留学Rubyist のフィリピン留学
Rubyist のフィリピン留学
 
10年後になくなる仕事 / Jobs which will disappear 10 years later
10年後になくなる仕事 / Jobs which will disappear 10 years later 10年後になくなる仕事 / Jobs which will disappear 10 years later
10年後になくなる仕事 / Jobs which will disappear 10 years later
 
Job-Hub 自由をつかめ(大人の事情ver)
Job-Hub 自由をつかめ(大人の事情ver)Job-Hub 自由をつかめ(大人の事情ver)
Job-Hub 自由をつかめ(大人の事情ver)
 
Heroku meetup#11(フル)
Heroku meetup#11(フル)Heroku meetup#11(フル)
Heroku meetup#11(フル)
 
Heroku meetup#11(lt)
Heroku meetup#11(lt)Heroku meetup#11(lt)
Heroku meetup#11(lt)
 
SendGrid on Job-Hub
SendGrid on Job-HubSendGrid on Job-Hub
SendGrid on Job-Hub
 
おいしいherokuの使い方
おいしいherokuの使い方おいしいherokuの使い方
おいしいherokuの使い方
 
Cacooではじめるスタートアップ
CacooではじめるスタートアップCacooではじめるスタートアップ
Cacooではじめるスタートアップ
 

Heroku connect 苦肉の四苦八苦

Hinweis der Redaktion

  1. 今回紹介する事例は、「なでしこナース」という看護師向け人材紹介サービスで実際に採用している Heroku Connectの活用方法です。
  2. なでしこナースは人材紹介サービスなので、求職者である看護師、求人者である病院・施設、サービスを運営する4ULの3つのプレイヤーが存在します。 図にするとこのような形になります。 プレイヤーごとにシステムが異なる構造で、看護師はHeroku、病院・施設はSalesforce の Partner Community、運営者はSalesforceという三層構造です。 こういうシステム構成にすることで、SalesfoceとHerokuの強みを両方活かし、サービス開発を素早く行えるようにしています。
  3. Heroku と Salesforce のハイブリット構成では当然データベース同期が必要になるので、Heroku Connectを使用しています。
  4. SalesforceのオブジェクトとHeroku Postgresのテーブルを双方向に同期してくれるアドオン あくまでも主となるのはSalesforceオブジェクト。同期対象のオブジェクトとそのカラムをHeroku Connectコンソールで選択すると、オブジェクトに対応したテーブルが自動生成される。逆方向はできない Heroku PostgreSQL -> Salesforceの同期時、Salesforceのヴァリデーションに引っかかると同期エラーが起きる。 テーブル作成はHeroku Connectを介してしか行えない(フレームワークのマイグレーション機能は原則そのまま使えない) Salesforce側のオブジェクト定義に依存するので、テーブル名、カラム名やデータ型、データ長さは変更できない 親と子どちらのレコードが先に同期されるか分からないので、FK(外部参照制約)は使えない(貼ることは可能だが、FKエラーで同期失敗する) サロゲートキーとしてIDカラムが生成されるが、Salesforce側には同期されない
  5. 結構色々制約があります。 概ね、より自由度の高いRDBMSであるPostgreSQL が Salesforceのデータの持ち方に合わせている感じです。 ユニークキーはHeroku Connectのダッシュボード上では貼れませんが、テーブルそのものに貼ることはできます。 複合インデックスも複合ユニークキーも貼れます。 ただしユニークキーはSalesforce側に制約として存在しない場合、Salesforce -> Heroku Postgres 同期時に エラーが発生するので注意が必要です。 特にHeroku Connect使い始めは勝手がわからず同期エラーを発生させがちなので、ログはしっかり見るようにしましょう。 Heroku Connect ダッシュボード上にログは表示されていますが、Herokuのログに流すこともできます。 なでしこナースではHeroku ConnectのログはPapertrailに流しており、エラーログが発生したらSlackに通知するようにしています。
  6. Heroku Connectはデータの同期をよしなにやってくれるものの、自由度の高いRDBMS側がSalesforceに合わせる形になっているため、フレームワークが期待するスキーマから大きく異なっています。 RailsのようなモダンなWebフレームワークは、フレームワークの流儀に従うことで高い生産性を実現しているので、このギャップが大きいと開発が非常に大変になってしまいます。 ここに理想と現実の壁が存在しているわけですが、ここではこの壁をどうやって乗り越えたかをお話します。
  7. Heroku Connectのダッシュボード画面では、このように同期対象のカラムを指定し、必要があればインデックスを追加することができます。 カラム名やデータ型、データ長さは変更できません。
  8. これは一例ですが、Heroku Postgres と Salesforceのカラムの対応はこんな感じです。 createddate、systemmodstamp、isdeletedシステムカラムで、常に同期されます。 Nameもシステムカラムですが、同期するかどうかは任意です。同期が推奨されています。 このほか、Heroku Connectのコンソールには表示されないものの、sfid、_hc_err、_hc_lastopというシステムカラムが自動的に追加されます。
  9. Heroku側が参照のみだったり、そもそも親子関係のない単独のテーブルの場合は問題は起こりません。 しかしHerokuとSalesforce両建てのハイブリッドシステムを構築する場合、当然Heroku側での書き込みも行うし、単独で存在するテーブルなんてほとんどありません。 つまり、PKとFKをどうするか?と言う問題が常に発生するわけです。 Salesforce側のPKであるsfidはSalesforceで発番されるので、Heroku側でInsertが発生した場合に発番できません。 Heroku側のPKであるIDはそもそもSalesforce側に同期されないので、親子関係を表現できません。 では、親子関係を維持するためにどうすれば良いか?と言う問題が出てきます。 厳密には図のようにSalesforce側のsfidを利用すれば親子関係を維持できますが、発番が常にSalesforce側となるため、 Heroku側でPKを使用できるまでに同期を二回繰り返す必要があり、現実的ではありません。 また、Webサービスという観点からみれば、Heroku視点では外部サービスとなるSalesforceに依存する構成は、 色々と弊害がありそうです。
  10. このインピーダンスミスマッチの解決策は、外部IDを使用する、というやり方です。
  11. Postgresのシーケンスを突っ込むのが手っ取り早いですが、より推奨されているやり方は Posgres の uuid_generate_v4() や gen_random_uuid() を使うやり方です。
  12. Conversation__c - Message__cのリレーションの例では、Conversation__cの外部ID(ここではmofID)の設定を行います。
  13. Conversation__cで外部IDの設定を行なっていると、Heroku Connect のカラム定義で、Conversations__r__mofID__c というリレーション解決専用の特別なカラムが表示されるようになります。 ここにConversation__cのPKを登録してやると、Salesforce側でも適切に親子関係のリレーションが行われるようになります。
  14. Railsのマイグレーション定義です。 create_tableはHeroku Connectが動いている環境では不要ですが、Salesforceの存在しないローカル、CI、ReviewAppのために定義しています。 IDカラムからPK定義をドロップ mofid__cにPK定義を追加 mofid__cのためにシーケンスオブジェクトを新規に追加 mofiid__cのデフォルト値に、シーケンスの発番処理を設定
  15. Schema.rbはこんな感じになります。 Idカラムは使用しなくなるのですが、Heroku Connectが自動生成するシステムカラムなので残しています。
  16. Sfdc側の結合の解決に使用される conversations__r__mofid__c に、conversation_idをセットする処理をコールバックで追加します。
  17. Heroku側でもSalesforce側でも、external_idカラムをPKとして使用し、このカラムで結合できるようになりました。 それぞれの処理系で発番を行えるため、同期を行わなくても各々の世界でデータの結合を閉じることができます。 これはつまり、Heroku側でメッセージ送信を行なった場合、同期を行わなくてもHerokuの世界ではメッセージ送信を完了させることができることを意味します。 これの何が重要かというと、Heroku側から見て外部サービスであるSalesforceに依存せずにWebアプリケーションを独立・完結させられることを意味します。 こうすることで、背後にあるSalesforceがなくてもHeroku側単独で動作するため、ローカルでの開発及びテスト、CIでのテスト、Review Appでの動作が全て正常に行えるるようになります。
  18. 今年夏頃までは conversations__r__mofid__c のような結合解決用システムカラムを使うのが一番良いやり方だったんですが、最近upsertフィールドというものが追加されており、 こちらの利用が現在は推奨されているようです。 Upsertフィールドが設定されていない場合、Heroku Connectコンソールのマッピング編集画面では常にUpsertフィールドを設定するよう促すアラートがで続けます。 Upsertフィールドになる条件を満たすフィールドが存在するようになると、このアラートの内容が切り替わり、候補となるフィールド名を提示するようになります。 ここではmofID__cがupsertフィールドの候補として提示されています。 画面右下の「Upsert Field」のプルダウンメニューもUpsertフィールド候補ができて初めて表示されるようになります。 ここでmofID__cを選択してSaveすれば、あとはSalesforceがリレーションを解決してくれるでしょう。 残念ながら時間がなくてUpsertフィールドはまだ試せていません。 Conversations__r__mofid__cのようなリレーション解決用の特殊カラムは同期が遅いというアラートがUpsert機能が追加されてから出るようになったので、 今後はこちらを使用するのが一般的になりそうです。 そのうち追試してみたいと考えています。
  19. Heroku Connect で生成されるカラム名は変更できないため、真ん中のようなカラム名になります。 短いものであればまだ良いですが、長いカラム名だと切れ目がどこなのか非常にわかりづらいため、コードを書く、読む面でも非常に扱いづらくなります。 created_atやupdated_atなど、変更は一応できるものの規約で名称の決まっているものもあります。
  20. ここでPostgreSQLの機能である、Updatable View(更新可能ビュー)を使います。 これはPostgreSQL独特の機能で、単一テーブルを参照するビューであれば、Updateを実行できるというものです。 このビューを利用してテーブル名やカラム名をRailsの規約に則ったものに変換しています。 理想と現実のギャップ(インピーダンスミスマッチ)を、更新可能ビューで解消しているわけです。
  21. 繰り返しになりますが、Railsのマイグレーションファイルはこんな感じです。 テーブルの作成、PKの変更、インデックスの作成を行なっています。
  22. DBビューのRailsのマイグレーションファイルはこんな感じです。 普通にDDLを叩いているだけですね。 Mofid__cをidとしているのが肝で、こうすることでSalesforce側で設定している外部IDを、Rails側でもIDとして使用できるようになっています。
  23. 再掲ですが、Railsのモデル定義はこれ。 self.primary_key はいらない気がしてきますが、これがないとなぜかうまく動きません。 先に説明した通りFK(外部参照制約)を物理的に貼ることができないので、フレームワーク側だけでも外部結合を 強制するように、関連の定義を行なっています。 これはRails4なので、belongs_toにrequired: trueを設定することで、関連を強制できます。 Rails5ではデフォルトでこの挙動になるので、required: trueは書かなくても大丈夫です。 この程度の定義で、DBビューであるmessagesビューに対し、CRUD操作を普通に行えます。 つまり、Rails側の開発者にとっては普通のRailsアプリと同様の開発を行えることを意味します。
  24. テーブルの新規追加ではなく、カラムの追加が発生することもあります。 この場合のマイグレーションファイルはこんな感じ。 ここではread__cという既読未読判別用のカラムを新たにメッセージオブジェクトに追加しています。 マイグレーションファイルでは対象カラムが存在するかどうかを条件式にできるので、 すでにread__cカラムがあるかを調べ、なかったら追加しています。
  25. カラム削除の場合のマイグレーションファイルはこんな感じ。 カラム削除の場合、DROP VIEW を先に実行しないとPosrgreSQLがエラーを吐きます。 ビューから参照されているカラムは消せないからです。
  26. このように二つのインピーダンスミスマッチを解消することで、HerokuとSalesforceが密に連携しながらも、 それぞれが独立した環境で動き、Ruby on RailsとSalesforceそれぞれの流儀で開発を行うことができています。 Web開発では疎結合が汎用性や柔軟性の上でメリットが有ることが多いですが、この事例でも同様です。
  27. このように、ふたつのインピーダンスミスマッチを効果的に解消することで、WebアプリケーションではSalesforceがバックエンドに存在することをあまり意識することなく、 従来の方法論に則ったWeb開発を行うことができます。
  28. 現在の開発フローはこんな感じです。 Githubを中心にCIを組み合わせて自動テストを行い、Heroku Pipelineを利用しているあたりは、モダンなHeroku開発そのままだと思います。 Review Appはビジネスサイド(プロダクトオーナー)に実際に動きを見せるために使っているので、サービスが動くのに必要なダミーデータをseed_fuで登録しています。 Salesforceと連動して動く機能はステージング環境までデプロイしないと確認できませんが、 Heroku側のアプリケーションのみでほぼ完結する機能も多いので、 Review Appでほとんどの動作確認を済ますことができています。 なお、ここでCircleCIの代わりにHerokuCIを使うとよりHeroku Pipelineを使いこなしてる感があっていいのですが、現在は使えません。 このなでしこナースプロジェクトではDB Viewを使っている関係上、データベースの構築には rake db:migrate か rake db:migrate:reset する必要があるのですが、 HerokuCIは rake db:schema:load 決め打ちになっているからです。 このコマンドは schema.rbの情報を元にデータベーススキーマ定義を行うので、DB Viewを作ってくれません。 Schema.rb を使わずに structure.sql に切り替えるのも一つの手ですが、Railsのプレーンな、一般的な構成を崩したくなくて、 CircleCIを使っています。
  29. Review Appを利用するためにはapp.jsonを書く必要がありますが、現在こんな感じです。 Postdeployが重要で、rake db:migrate でデータベーススキーマの構築、db:seed_fu でマスタデータやダミーデータの投入を行なっています。 RAILS_ENVをステージングにしているのは、万が一にも本番環境で動作しないようproductionのseed_fuを作成していないからです。 別にseed_fuである必要はないので、通常のseedなどを使っても良いと思います。