SlideShare una empresa de Scribd logo
1 de 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
現在の開発サイクル

Más contenido relacionado

La actualidad más candente

Swaggerでのapi開発よもやま話
Swaggerでのapi開発よもやま話Swaggerでのapi開発よもやま話
Swaggerでのapi開発よもやま話KEISUKE KONISHI
 
ツール比較しながら語る O/RマッパーとDBマイグレーションの実際のところ
ツール比較しながら語る O/RマッパーとDBマイグレーションの実際のところツール比較しながら語る O/RマッパーとDBマイグレーションの実際のところ
ツール比較しながら語る O/RマッパーとDBマイグレーションの実際のところY Watanabe
 
JP1/AJS2オペレータ勉強会
JP1/AJS2オペレータ勉強会JP1/AJS2オペレータ勉強会
JP1/AJS2オペレータ勉強会mizuky fujitani
 
ビッグデータ処理データベースの全体像と使い分け
ビッグデータ処理データベースの全体像と使い分けビッグデータ処理データベースの全体像と使い分け
ビッグデータ処理データベースの全体像と使い分けRecruit Technologies
 
お前は PHP の歴史的な理由の数を覚えているのか
お前は PHP の歴史的な理由の数を覚えているのかお前は PHP の歴史的な理由の数を覚えているのか
お前は PHP の歴史的な理由の数を覚えているのかKousuke Ebihara
 
世界一わかりやすいClean Architecture
世界一わかりやすいClean Architecture世界一わかりやすいClean Architecture
世界一わかりやすいClean ArchitectureAtsushi Nakamura
 
DDD sample code explained in Java
DDD sample code explained in JavaDDD sample code explained in Java
DDD sample code explained in Java増田 亨
 
爆速クエリエンジン”Presto”を使いたくなる話
爆速クエリエンジン”Presto”を使いたくなる話爆速クエリエンジン”Presto”を使いたくなる話
爆速クエリエンジン”Presto”を使いたくなる話Kentaro Yoshida
 
OAuth2.0によるWeb APIの保護
OAuth2.0によるWeb APIの保護OAuth2.0によるWeb APIの保護
OAuth2.0によるWeb APIの保護Naohiro Fujie
 
REST API のコツ
REST API のコツREST API のコツ
REST API のコツpospome
 
マイクロサービスバックエンドAPIのためのRESTとgRPC
マイクロサービスバックエンドAPIのためのRESTとgRPCマイクロサービスバックエンドAPIのためのRESTとgRPC
マイクロサービスバックエンドAPIのためのRESTとgRPCdisc99_
 
Spring CloudとZipkinを利用した分散トレーシング
Spring CloudとZipkinを利用した分散トレーシングSpring CloudとZipkinを利用した分散トレーシング
Spring CloudとZipkinを利用した分散トレーシングRakuten Group, Inc.
 
テストコードの DRY と DAMP
テストコードの DRY と DAMPテストコードの DRY と DAMP
テストコードの DRY と DAMPYusuke Kagata
 
ログ+メトリック+トレースの組み合わせで構築する一元的なオブザーバビリティ
ログ+メトリック+トレースの組み合わせで構築する一元的なオブザーバビリティログ+メトリック+トレースの組み合わせで構築する一元的なオブザーバビリティ
ログ+メトリック+トレースの組み合わせで構築する一元的なオブザーバビリティElasticsearch
 
日本語テストメソッドについて
日本語テストメソッドについて日本語テストメソッドについて
日本語テストメソッドについてkumake
 
導入から 10 年、PHP の trait は滅びるべきなのか その適切な使いどころと弱点、将来について
導入から 10 年、PHP の trait は滅びるべきなのか その適切な使いどころと弱点、将来について導入から 10 年、PHP の trait は滅びるべきなのか その適切な使いどころと弱点、将来について
導入から 10 年、PHP の trait は滅びるべきなのか その適切な使いどころと弱点、将来についてshinjiigarashi
 
Python 3.9からの新定番zoneinfoを使いこなそう
Python 3.9からの新定番zoneinfoを使いこなそうPython 3.9からの新定番zoneinfoを使いこなそう
Python 3.9からの新定番zoneinfoを使いこなそうRyuji Tsutsui
 

La actualidad más candente (20)

Swaggerでのapi開発よもやま話
Swaggerでのapi開発よもやま話Swaggerでのapi開発よもやま話
Swaggerでのapi開発よもやま話
 
ツール比較しながら語る O/RマッパーとDBマイグレーションの実際のところ
ツール比較しながら語る O/RマッパーとDBマイグレーションの実際のところツール比較しながら語る O/RマッパーとDBマイグレーションの実際のところ
ツール比較しながら語る O/RマッパーとDBマイグレーションの実際のところ
 
分散トレーシング技術について(Open tracingやjaeger)
分散トレーシング技術について(Open tracingやjaeger)分散トレーシング技術について(Open tracingやjaeger)
分散トレーシング技術について(Open tracingやjaeger)
 
JP1/AJS2オペレータ勉強会
JP1/AJS2オペレータ勉強会JP1/AJS2オペレータ勉強会
JP1/AJS2オペレータ勉強会
 
ビッグデータ処理データベースの全体像と使い分け
ビッグデータ処理データベースの全体像と使い分けビッグデータ処理データベースの全体像と使い分け
ビッグデータ処理データベースの全体像と使い分け
 
Apache OpenWhiskで実現するプライベートFaaS環境 #tjdev
Apache OpenWhiskで実現するプライベートFaaS環境 #tjdevApache OpenWhiskで実現するプライベートFaaS環境 #tjdev
Apache OpenWhiskで実現するプライベートFaaS環境 #tjdev
 
お前は PHP の歴史的な理由の数を覚えているのか
お前は PHP の歴史的な理由の数を覚えているのかお前は PHP の歴史的な理由の数を覚えているのか
お前は PHP の歴史的な理由の数を覚えているのか
 
世界一わかりやすいClean Architecture
世界一わかりやすいClean Architecture世界一わかりやすいClean Architecture
世界一わかりやすいClean Architecture
 
SREチームとしてSREしてみた話
SREチームとしてSREしてみた話SREチームとしてSREしてみた話
SREチームとしてSREしてみた話
 
DDD sample code explained in Java
DDD sample code explained in JavaDDD sample code explained in Java
DDD sample code explained in Java
 
爆速クエリエンジン”Presto”を使いたくなる話
爆速クエリエンジン”Presto”を使いたくなる話爆速クエリエンジン”Presto”を使いたくなる話
爆速クエリエンジン”Presto”を使いたくなる話
 
OAuth2.0によるWeb APIの保護
OAuth2.0によるWeb APIの保護OAuth2.0によるWeb APIの保護
OAuth2.0によるWeb APIの保護
 
REST API のコツ
REST API のコツREST API のコツ
REST API のコツ
 
マイクロサービスバックエンドAPIのためのRESTとgRPC
マイクロサービスバックエンドAPIのためのRESTとgRPCマイクロサービスバックエンドAPIのためのRESTとgRPC
マイクロサービスバックエンドAPIのためのRESTとgRPC
 
Spring CloudとZipkinを利用した分散トレーシング
Spring CloudとZipkinを利用した分散トレーシングSpring CloudとZipkinを利用した分散トレーシング
Spring CloudとZipkinを利用した分散トレーシング
 
テストコードの DRY と DAMP
テストコードの DRY と DAMPテストコードの DRY と DAMP
テストコードの DRY と DAMP
 
ログ+メトリック+トレースの組み合わせで構築する一元的なオブザーバビリティ
ログ+メトリック+トレースの組み合わせで構築する一元的なオブザーバビリティログ+メトリック+トレースの組み合わせで構築する一元的なオブザーバビリティ
ログ+メトリック+トレースの組み合わせで構築する一元的なオブザーバビリティ
 
日本語テストメソッドについて
日本語テストメソッドについて日本語テストメソッドについて
日本語テストメソッドについて
 
導入から 10 年、PHP の trait は滅びるべきなのか その適切な使いどころと弱点、将来について
導入から 10 年、PHP の trait は滅びるべきなのか その適切な使いどころと弱点、将来について導入から 10 年、PHP の trait は滅びるべきなのか その適切な使いどころと弱点、将来について
導入から 10 年、PHP の trait は滅びるべきなのか その適切な使いどころと弱点、将来について
 
Python 3.9からの新定番zoneinfoを使いこなそう
Python 3.9からの新定番zoneinfoを使いこなそうPython 3.9からの新定番zoneinfoを使いこなそう
Python 3.9からの新定番zoneinfoを使いこなそう
 

Más de 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
 

Más de 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 苦肉の四苦八苦

Notas del editor

  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などを使っても良いと思います。