SlideShare a Scribd company logo
エネチェンジでの
STIの使い方紹介
Yuya Taki
self.inspect
[1] https://github.com/muramurasan/okuribito [2] http://poject.herokuapp.com/
➢ gemのロゴを書いたり・・・
➢ たまにQiitaの記事を書いたり・・・
➢ 新卒で某SIerにてスマートメータのヘッドエンドシ
ステムの開発に携わったあと、渋谷のベンチャー
企業にてRuby on Railsを学び、2016年10月エネ
チェンジに入社。
➢ なぜかPaypal、ベリトランス、SMBC、YDSなど決
済周り、請求周りを実装することが多い。
Name : Yuya Taki
GitHub : yuyasat
Qiita : yuyasat
[1]
若輩者ですので、何卒優しくご教授いただけ
ればと思います。
(commitはしていない)
➢ ○よ○よ風ゲームをReact.jsで実装し
たり
[2]
enechangeとは
➢ 2016年電力自由化・2017年ガス自由化に伴い、電気代やガス代を比較でき
るサービスを運営。
➢ 比較だけでなく、申し込みも受け付けている。
https://enechange.jp/
STI(Single Table Inheritance)について
➢ STI(Single Table Inheritance, 単一テーブル継承)使ってますか?
➢ ORマッピングの一つ。
○ クラステーブル継承、具象テーブル継承、単一テーブル継承
➢ 伊藤淳一さんは「あえて使わないようにしている」とのこと。
○ 6〜7年前の話なので今は状況が変わっているかも。
[1] https://qiita.com/yebihara/items/9ecb838893ad99be0561
[1]
➢ 僕は好き。
➢ エネチェンジ でも使っています。
代理店とOEM Lite
代理店21社・OEM Lite 2社
が同居するマルチテナントとなっている
➢ エネチェンジとして受付 ➢ 事業者として受付代理店 OEM Lite
※ 2018年2月1日現在
マルチテナントにおける悩み
事業者A 事業者B 事業者C
お客さま番号 必須 必須 必須
供給地点特定番号 必須 必須 必須ではないが分かれ
ば入れてもらう
事業所コード 必須 不要 不要
メールアドレス 必須 不要 必須
氏名 姓・名それぞれ
20字以内
姓・名合わせて40字以内
(姓・名は全角スペースで連結)
姓・名合わせて50字以
内
各事業者ごとに必要なバリデーションが異なる
・・・どう管理する?・・・
既存の実装
class Order < ActiveRecord::Base
include ProviderA
include ProviderB
include ProviderC
  ・
  ・
end
module ProviderA
extend ActiveSupport::Concern
included do
with_options if: :provider_a? do |o|
o.validates :email, presence: true
o.validates :bill_office_number, presence: true
・
・
end
end
end
➢ 一つのクラスで実装。
➢ 各事業者のバリデーションを書いた concernを定義し、モデルで読み込む。
(事業所コード)
(メールアドレス)
既存の実装の問題点1
class Order < ActiveRecord::Base
include ProviderA
include ProviderB
include ProviderC
  ・
  ・
end
module ProviderA
extend ActiveSupport::Concern
def phone_type_text_for_provider_a
end
end
➢ 同じようなメソッドが様々な concernに
実装されていく・・・。
➢ 実装者によってメソッド名の流儀が違
う・・・。事業者数も多くレビュアーも把
握しきれない。
module ProviderB
extend ActiveSupport::Concern
def phone_type_text_for_provider_b
end
end
module ProviderC
extend ActiveSupport::Concern
def provider_c_phone_type_text
end
end
{
'自宅' => 1,
'携帯' => 2,
'家族・親族 ' => 3,
'配偶者' => 4,
'家主・管理人 ' => 5,
'事務所' => 6,
'その他' => 9,
}
{
'事務所' => 1,
'代表電話' => 2,
'携帯電話' => 3,
'その他' => 4
}
既存の実装の問題点2
class Order < ActiveRecord::Base
include ProviderA
include ProviderB
include ProviderC
  ・
  ・
end
module ProviderA
extend ActiveSupport::Concern
LOW_VOLTAGE_EQUIPMENTS_FOR_A = {
'空調' => 1, '冷凍/冷蔵' => 2, 'モータ' => 3, 'その他' => 9
}
def low_voltage_equipment_text
LOW_VOLTAGE_EQUIPMENTS_FOR_A.key(low_voltage_equipment)
end
end
module ProviderB
extend ActiveSupport::Concern
LOW_VOLTAGE_EQUIPMENTS_FOR_B = {
'冷凍/冷蔵' => 1, 'モータ' => 2, 'コンプレッサ' => 3, 'その他
' => 9
}
def low_voltage_equipment_text
LOW_VOLTAGE_EQUIPMENTS_FOR_B.key(low_voltage_equipment)
end
end
気づいたら同名のメソッドが追加さ
れ、後からincludeされたもので上書
きされる・・!
※実際にあったが、まったく同じメソッドであったため、問題が顕在化しなかった。
既存の実装の問題点3
class Order < ActiveRecord::Base
include ProviderA
include ProviderB
include ProviderC
  ・
  ・
def applied_at_cannnot_be_in_the_past
return unless applied_at && applied_at < Time.current
errors.add(:applied_at, “過去の日付は使用できません”)
end
end
module ProviderA
extend ActiveSupport::Concern
included do
with_options if: :provider_a? do |o|
o.validate :applied_at_cannnot_be_in_the_past
end
end
end
module ProviderB
extend ActiveSupport::Concern
included do
with_options if: :provider_b? do |o|
o.validate :applied_at_cannnot_be_in_the_past
end
end
end➢ 複数の事業者に同じバリデーションメソッドを
違う条件で用たい。
後から呼ばれた条件だけに適用されてしま
う!
※with_optionsの中にifを書くと後ろの条件しか適用されない問題と同じ。
(詳しくは[1], [2]をご覧ください)
with_options if: :condition_a? do |v|
v.validates :hoge, presence: true, if: :condition_b?
end
[1] https://github.com/rails/rails/blob/master/activemodel/lib/active_model/validations.rb#L154
[2] https://github.com/rails/rails/blob/master/activesupport/lib/active_support/callbacks.rb#L374
class Order < ActiveRecord::Base
with_options if: -> { provider_a? || provider_x? } do |o|
o.validates :family_name, length: { maximum: 20 }
o.validates :given_name, length: { maximum: 20 }
・
・
end
with_options if: -> { provider_b? || provider_y? } do |o|
o.validates :family_name, length: { maximum: 30 }
o.validates :given_name, length: { maximum: 30 }
・
・
end
end
各事業者ごとに一つのファイルで完
結
モデルにも事業者ごとのバリデー
ションが記述されコードが散乱
理想
現実
事業者によっては同じバリデーショ
ンの場合もある
今行なっている修正が、他の事業者へ影響があるかどうかすぐに分からない・・・!
既存の実装の問題点4
僕は嫌だ
こんなコード
orders
Order
orders
STI
Order
STI
OrderOfProvider::
ProviderA
STI
OrderOfProvider::
ProviderB
既存
暫く現状の
TryControllerの
まま運用
OrdersControllerを新たに作り、代理店・OEM系はこちらで運用
(tokyo-gas-oemリポジトリ参考)
※既存の代理店は
暫くこちらを使う
OrderBase
STI
OrderOfProvider::Base
新
STI
OrderOfProvider::
ProviderC
どこかのタイミングで移行・・・!
TryController
Database
Model
Controller
構成変更
各事業者毎
のクラス
新実装
class OrderOfProvider::Base < OrderBase
def phone_type_text
phone_number.start_with?(/090|080|070/) ? '携帯電話' : '自宅'
end
end
class ProviderA < OrderOfProvider::Base
POHONE_NUMBER_TYPE = {
'自宅'=> 1, '携帯' => 2, '家族・親族' => 3,
'配偶者' => 4, '家主・管理人' => 5, '事務所' => 6,
'その他' => 9,
}
def phone_type_text
# enumにしても良いかも。
POHONE_NUMBER_TYPE.key(phone_type)
end
end
➢ 親クラスによく使われる実装をしておけば、多く
の子クラスではメソッドを実装しなくて済む。
➢ 事業者クラスで違う処理にしたい場合は、事業
者クラスに実装すればよい。
○ 他の事業者を気にせずに済む。
○ 問題点2解消!
class ProviderB < OrderOfProvider::Base
POHONE_NUMBER_TYPE = {
'事務所' => 1, '代表電話' => 2,
'携帯電話' => 3, 'その他' => 4,
}
def phone_type_text
POHONE_NUMBER_TYPE.key(phone_type)
end
end
class ProviderB < OrderOfProvider::Base
end
➢ 事業者毎に同じメソッド名で実装できる。
○ 問題点1解消!
class OrderOfProvider::Base < OrderBase
def applied_at_cannnot_be_in_the_past
return unless applied_at && applied_at < Time.current
errors.add(:applied_at, “過去の日付は使用できません”)
end
end
class ProviderA < OrderOfProvider::Base
validate :applied_at_cannnot_be_in_the_past
end
class ProviderC < OrderOfProvider::Base
validate :applied_at_cannnot_be_in_the_past
def applied_at_cannnot_be_in_the_past
return unless applied_at && applied_at < Time.current
errors.add(:applied_at, “申込日は未来の日付にして下さい”)
end
end
➢ Ifの上書きを気にすることなく各事業者に同
じvalidationを適用することが可能。
○ 問題点3解消!
新実装
class ProviderB < OrderOfProvider::Base
validate :applied_at_cannnot_be_in_the_past
end
➢ 各事業者クラスに用いられているバリデー
ションが一つのファイルでわかる。
○ 問題点4解消!
まとめ
➢ STIにしたことで
○ 似たようなメソッド名の乱立が防げた。
○ 意図せぬメソッドの上書きが防げた。
○ with_optionsのifに悩まされずに済む。
○ 事業者固有のコードを同じ箇所にまとめることができた。
➢ それでも残る課題
○ (STIの問題というわけではないが)電気単独申し込み、ガス申し込みなどを一つのテーブル
で管理してしまっているが故に nullカラムが増えてしまっている。
○ 新設計に移行したいが既存の事業者のロジックをすぐには移行できず、新旧が共存してし
まっている。
最後に
➢ 100社以上電力会社を掲載・20社以上の申し込み受付に対応してきた
○ これらの知見をもとに設計を見直し、運用保守性の高いシステムにしていきたい!
➢ ビジネス速度も落とさない
○ テストを書く
➢ 電力業界のプラットフォームとなっていく
○ 開発リソースを確保しつつ、改善していくことができる環境。
➢ 電気を使うことはなくならない!
○ 生活に密着したサービスをつくる
資料
2014.12.18 Rails でシングルじゃないテーブル継承
2016.12.04 みんなRailsのSTIを誤解してないか!?
Catalog of Patterns of Enterprise Application Architecture
NULL撲滅委員会
ご静聴ありがとうございました。

More Related Content

What's hot

S14 t3 yosuke_yamashita
S14 t3 yosuke_yamashitaS14 t3 yosuke_yamashita
S14 t3 yosuke_yamashita
Takeshi Akutsu
 
第八回 #渋谷Java 最近のjava PaaS事情
第八回 #渋谷Java 最近のjava PaaS事情第八回 #渋谷Java 最近のjava PaaS事情
第八回 #渋谷Java 最近のjava PaaS事情
Kazuhiro Serizawa
 
One Time Binding & Digest Loop
One Time Binding & Digest LoopOne Time Binding & Digest Loop
One Time Binding & Digest Loop
Kon Yuichi
 
第六回 #渋谷java Javaを書き始めて 1年半が経って思うこと
第六回 #渋谷java Javaを書き始めて 1年半が経って思うこと第六回 #渋谷java Javaを書き始めて 1年半が経って思うこと
第六回 #渋谷java Javaを書き始めて 1年半が経って思うこと
Kazuhiro Serizawa
 
海外で注目されてるJs framework “mithril”の特徴
海外で注目されてるJs framework “mithril”の特徴海外で注目されてるJs framework “mithril”の特徴
海外で注目されてるJs framework “mithril”の特徴
Shoyo Kyou
 
Clojureでガラケーサイトを作る際の細かい話
Clojureでガラケーサイトを作る際の細かい話Clojureでガラケーサイトを作る際の細かい話
Clojureでガラケーサイトを作る際の細かい話
Ikuru Kanuma
 
Riot.jsとフォームのデータバインディング
Riot.jsとフォームのデータバインディングRiot.jsとフォームのデータバインディング
Riot.jsとフォームのデータバインディング
Keisuke Imai
 
One-time Binding & $digest
One-time Binding & $digestOne-time Binding & $digest
One-time Binding & $digest
Hayashi Yuichi
 
ぼくのかんがえたさいきょうのうぇぶあぷりけーしょんふれーむわーく - YAPC Asia 2011
ぼくのかんがえたさいきょうのうぇぶあぷりけーしょんふれーむわーく - YAPC Asia 2011ぼくのかんがえたさいきょうのうぇぶあぷりけーしょんふれーむわーく - YAPC Asia 2011
ぼくのかんがえたさいきょうのうぇぶあぷりけーしょんふれーむわーく - YAPC Asia 2011Hiroh Satoh
 
High Performance Gulp
High Performance GulpHigh Performance Gulp
High Performance Gulp
Keisuke Imura
 
社内LTネタ ReactNative
社内LTネタ ReactNative社内LTネタ ReactNative
社内LTネタ ReactNative
Oguri Toru
 
milkcocoa入門@milkcocoa meetup#1
milkcocoa入門@milkcocoa meetup#1milkcocoa入門@milkcocoa meetup#1
milkcocoa入門@milkcocoa meetup#1
Syuhei Hiya
 
Node.js version16の新機能
Node.js version16の新機能Node.js version16の新機能
Node.js version16の新機能
Masaki Suzuki
 
Riot + generator で始める新しいデータバインディング
Riot + generator で始める新しいデータバインディングRiot + generator で始める新しいデータバインディング
Riot + generator で始める新しいデータバインディング
Tsutomu Kawamura
 
Service Workers Push API Hands-on
Service Workers Push API Hands-onService Workers Push API Hands-on
Service Workers Push API Hands-on
Takenori Nakagawa
 
JiraとConfluenceのTips集
JiraとConfluenceのTips集JiraとConfluenceのTips集
JiraとConfluenceのTips集
Hiroshi Ohnuki
 
View CustomizeからREST APIを使用する
View CustomizeからREST APIを使用するView CustomizeからREST APIを使用する
View CustomizeからREST APIを使用する
Asa Morino
 
jQueryを中心としたJavaScript
jQueryを中心としたJavaScriptjQueryを中心としたJavaScript
jQueryを中心としたJavaScript
hideaki honda
 
(Unityよくわかってない人のための)なんとなくわかるかもしれないAssetBundle
(Unityよくわかってない人のための)なんとなくわかるかもしれないAssetBundle(Unityよくわかってない人のための)なんとなくわかるかもしれないAssetBundle
(Unityよくわかってない人のための)なんとなくわかるかもしれないAssetBundle
Yusuke HIDESHIMA
 
サムネイルを作る話
サムネイルを作る話サムネイルを作る話
サムネイルを作る話
Ikuru Kanuma
 

What's hot (20)

S14 t3 yosuke_yamashita
S14 t3 yosuke_yamashitaS14 t3 yosuke_yamashita
S14 t3 yosuke_yamashita
 
第八回 #渋谷Java 最近のjava PaaS事情
第八回 #渋谷Java 最近のjava PaaS事情第八回 #渋谷Java 最近のjava PaaS事情
第八回 #渋谷Java 最近のjava PaaS事情
 
One Time Binding & Digest Loop
One Time Binding & Digest LoopOne Time Binding & Digest Loop
One Time Binding & Digest Loop
 
第六回 #渋谷java Javaを書き始めて 1年半が経って思うこと
第六回 #渋谷java Javaを書き始めて 1年半が経って思うこと第六回 #渋谷java Javaを書き始めて 1年半が経って思うこと
第六回 #渋谷java Javaを書き始めて 1年半が経って思うこと
 
海外で注目されてるJs framework “mithril”の特徴
海外で注目されてるJs framework “mithril”の特徴海外で注目されてるJs framework “mithril”の特徴
海外で注目されてるJs framework “mithril”の特徴
 
Clojureでガラケーサイトを作る際の細かい話
Clojureでガラケーサイトを作る際の細かい話Clojureでガラケーサイトを作る際の細かい話
Clojureでガラケーサイトを作る際の細かい話
 
Riot.jsとフォームのデータバインディング
Riot.jsとフォームのデータバインディングRiot.jsとフォームのデータバインディング
Riot.jsとフォームのデータバインディング
 
One-time Binding & $digest
One-time Binding & $digestOne-time Binding & $digest
One-time Binding & $digest
 
ぼくのかんがえたさいきょうのうぇぶあぷりけーしょんふれーむわーく - YAPC Asia 2011
ぼくのかんがえたさいきょうのうぇぶあぷりけーしょんふれーむわーく - YAPC Asia 2011ぼくのかんがえたさいきょうのうぇぶあぷりけーしょんふれーむわーく - YAPC Asia 2011
ぼくのかんがえたさいきょうのうぇぶあぷりけーしょんふれーむわーく - YAPC Asia 2011
 
High Performance Gulp
High Performance GulpHigh Performance Gulp
High Performance Gulp
 
社内LTネタ ReactNative
社内LTネタ ReactNative社内LTネタ ReactNative
社内LTネタ ReactNative
 
milkcocoa入門@milkcocoa meetup#1
milkcocoa入門@milkcocoa meetup#1milkcocoa入門@milkcocoa meetup#1
milkcocoa入門@milkcocoa meetup#1
 
Node.js version16の新機能
Node.js version16の新機能Node.js version16の新機能
Node.js version16の新機能
 
Riot + generator で始める新しいデータバインディング
Riot + generator で始める新しいデータバインディングRiot + generator で始める新しいデータバインディング
Riot + generator で始める新しいデータバインディング
 
Service Workers Push API Hands-on
Service Workers Push API Hands-onService Workers Push API Hands-on
Service Workers Push API Hands-on
 
JiraとConfluenceのTips集
JiraとConfluenceのTips集JiraとConfluenceのTips集
JiraとConfluenceのTips集
 
View CustomizeからREST APIを使用する
View CustomizeからREST APIを使用するView CustomizeからREST APIを使用する
View CustomizeからREST APIを使用する
 
jQueryを中心としたJavaScript
jQueryを中心としたJavaScriptjQueryを中心としたJavaScript
jQueryを中心としたJavaScript
 
(Unityよくわかってない人のための)なんとなくわかるかもしれないAssetBundle
(Unityよくわかってない人のための)なんとなくわかるかもしれないAssetBundle(Unityよくわかってない人のための)なんとなくわかるかもしれないAssetBundle
(Unityよくわかってない人のための)なんとなくわかるかもしれないAssetBundle
 
サムネイルを作る話
サムネイルを作る話サムネイルを作る話
サムネイルを作る話
 

Similar to エネチェンジでのSTIの使い方紹介

2014年を振り返る 今年の技術トレンドとDockerについて
2014年を振り返る 今年の技術トレンドとDockerについて2014年を振り返る 今年の技術トレンドとDockerについて
2014年を振り返る 今年の技術トレンドとDockerについて
Masahito Zembutsu
 
ROS JAPAN Users Group Meetup 03
ROS JAPAN Users Group Meetup 03ROS JAPAN Users Group Meetup 03
ROS JAPAN Users Group Meetup 03Daiki Maekawa
 
Railsやるやる_セキュリティ小話
Railsやるやる_セキュリティ小話Railsやるやる_セキュリティ小話
Railsやるやる_セキュリティ小話
Naoki Ishibashi
 
ぼく(たち)のかんがえた最新のJS開発環境 #scripty04
 ぼく(たち)のかんがえた最新のJS開発環境 #scripty04 ぼく(たち)のかんがえた最新のJS開発環境 #scripty04
ぼく(たち)のかんがえた最新のJS開発環境 #scripty04
Yahoo!デベロッパーネットワーク
 
古き良きRailsプロジェクトに wepbackとvue.jsを導入した話
古き良きRailsプロジェクトに wepbackとvue.jsを導入した話古き良きRailsプロジェクトに wepbackとvue.jsを導入した話
古き良きRailsプロジェクトに wepbackとvue.jsを導入した話
Yuya Taki
 
少しずつ手厚くして不具合や仕様漏れを防ぐために
少しずつ手厚くして不具合や仕様漏れを防ぐために少しずつ手厚くして不具合や仕様漏れを防ぐために
少しずつ手厚くして不具合や仕様漏れを防ぐために
Fumiya Sakai
 
あなたの安心を高速に守る Container-based CI
あなたの安心を高速に守る Container-based CIあなたの安心を高速に守る Container-based CI
あなたの安心を高速に守る Container-based CI
Wataru MIYAGUNI
 
Ruby向け帳票ソリューション「ThinReports」の開発で知るOSSの威力
Ruby向け帳票ソリューション「ThinReports」の開発で知るOSSの威力Ruby向け帳票ソリューション「ThinReports」の開発で知るOSSの威力
Ruby向け帳票ソリューション「ThinReports」の開発で知るOSSの威力
ThinReports
 
Openstack ceph 20171115 vtj
Openstack ceph 20171115 vtjOpenstack ceph 20171115 vtj
Openstack ceph 20171115 vtj
Takehiro Kudou
 
リーンスタートアップと顧客開発とアジャイル開発を一気通貫するッ #devlove #devkan
リーンスタートアップと顧客開発とアジャイル開発を一気通貫するッ #devlove #devkanリーンスタートアップと顧客開発とアジャイル開発を一気通貫するッ #devlove #devkan
リーンスタートアップと顧客開発とアジャイル開発を一気通貫するッ #devlove #devkan
Itsuki Kuroda
 
属人化したフロントエンドのJavaScriptを、 ‘新規機能開発を止めずに’改善するために行った取り組みについて。 及びその経過報告。
属人化したフロントエンドのJavaScriptを、‘新規機能開発を止めずに’改善するために行った取り組みについて。及びその経過報告。属人化したフロントエンドのJavaScriptを、‘新規機能開発を止めずに’改善するために行った取り組みについて。及びその経過報告。
属人化したフロントエンドのJavaScriptを、 ‘新規機能開発を止めずに’改善するために行った取り組みについて。 及びその経過報告。
Jun Suzuki
 
ゼロからのプログラミングRails講座 Codeanywhere版
ゼロからのプログラミングRails講座 Codeanywhere版ゼロからのプログラミングRails講座 Codeanywhere版
ゼロからのプログラミングRails講座 Codeanywhere版
DIVE INTO CODE Corp.
 
2014-02-20_Tokyo-GAS#5-社内活性化推進にGASを大活用した話
2014-02-20_Tokyo-GAS#5-社内活性化推進にGASを大活用した話2014-02-20_Tokyo-GAS#5-社内活性化推進にGASを大活用した話
2014-02-20_Tokyo-GAS#5-社内活性化推進にGASを大活用した話
Humangas
 
VSCodeで始めるAzure Static Web Apps開発
VSCodeで始めるAzure Static Web Apps開発VSCodeで始めるAzure Static Web Apps開発
VSCodeで始めるAzure Static Web Apps開発
Yuta Matsumura
 
Zマイスターとの新たな価値探求 Rational
Zマイスターとの新たな価値探求 RationalZマイスターとの新たな価値探求 Rational
Zマイスターとの新たな価値探求 Rational
IBMソリューション
 
20120118 titanium
20120118 titanium20120118 titanium
20120118 titanium
Hiroshi Oyamada
 
プロダクトに 1 から Vue.js を導入した話
プロダクトに 1 から Vue.js を導入した話プロダクトに 1 から Vue.js を導入した話
プロダクトに 1 から Vue.js を導入した話
Shohei Okada
 
[Okta x Jamf合同新年会] Okta Workflowsによるノーコード業務改善 〜Jamf APIを使ってMac端末情報を自動収集してみよう〜
[Okta x Jamf合同新年会] Okta Workflowsによるノーコード業務改善 〜Jamf APIを使ってMac端末情報を自動収集してみよう〜[Okta x Jamf合同新年会] Okta Workflowsによるノーコード業務改善 〜Jamf APIを使ってMac端末情報を自動収集してみよう〜
[Okta x Jamf合同新年会] Okta Workflowsによるノーコード業務改善 〜Jamf APIを使ってMac端末情報を自動収集してみよう〜
Ryo Sasaki
 
RxDataSourceをNSDiffableDataSourceへ置き換える際のTips集紹介
RxDataSourceをNSDiffableDataSourceへ置き換える際のTips集紹介RxDataSourceをNSDiffableDataSourceへ置き換える際のTips集紹介
RxDataSourceをNSDiffableDataSourceへ置き換える際のTips集紹介
Fumiya Sakai
 

Similar to エネチェンジでのSTIの使い方紹介 (20)

2014年を振り返る 今年の技術トレンドとDockerについて
2014年を振り返る 今年の技術トレンドとDockerについて2014年を振り返る 今年の技術トレンドとDockerについて
2014年を振り返る 今年の技術トレンドとDockerについて
 
ROS JAPAN Users Group Meetup 03
ROS JAPAN Users Group Meetup 03ROS JAPAN Users Group Meetup 03
ROS JAPAN Users Group Meetup 03
 
Railsやるやる_セキュリティ小話
Railsやるやる_セキュリティ小話Railsやるやる_セキュリティ小話
Railsやるやる_セキュリティ小話
 
ぼく(たち)のかんがえた最新のJS開発環境 #scripty04
 ぼく(たち)のかんがえた最新のJS開発環境 #scripty04 ぼく(たち)のかんがえた最新のJS開発環境 #scripty04
ぼく(たち)のかんがえた最新のJS開発環境 #scripty04
 
古き良きRailsプロジェクトに wepbackとvue.jsを導入した話
古き良きRailsプロジェクトに wepbackとvue.jsを導入した話古き良きRailsプロジェクトに wepbackとvue.jsを導入した話
古き良きRailsプロジェクトに wepbackとvue.jsを導入した話
 
少しずつ手厚くして不具合や仕様漏れを防ぐために
少しずつ手厚くして不具合や仕様漏れを防ぐために少しずつ手厚くして不具合や仕様漏れを防ぐために
少しずつ手厚くして不具合や仕様漏れを防ぐために
 
あなたの安心を高速に守る Container-based CI
あなたの安心を高速に守る Container-based CIあなたの安心を高速に守る Container-based CI
あなたの安心を高速に守る Container-based CI
 
Ruby向け帳票ソリューション「ThinReports」の開発で知るOSSの威力
Ruby向け帳票ソリューション「ThinReports」の開発で知るOSSの威力Ruby向け帳票ソリューション「ThinReports」の開発で知るOSSの威力
Ruby向け帳票ソリューション「ThinReports」の開発で知るOSSの威力
 
Openstack ceph 20171115 vtj
Openstack ceph 20171115 vtjOpenstack ceph 20171115 vtj
Openstack ceph 20171115 vtj
 
リーンスタートアップと顧客開発とアジャイル開発を一気通貫するッ #devlove #devkan
リーンスタートアップと顧客開発とアジャイル開発を一気通貫するッ #devlove #devkanリーンスタートアップと顧客開発とアジャイル開発を一気通貫するッ #devlove #devkan
リーンスタートアップと顧客開発とアジャイル開発を一気通貫するッ #devlove #devkan
 
属人化したフロントエンドのJavaScriptを、 ‘新規機能開発を止めずに’改善するために行った取り組みについて。 及びその経過報告。
属人化したフロントエンドのJavaScriptを、‘新規機能開発を止めずに’改善するために行った取り組みについて。及びその経過報告。属人化したフロントエンドのJavaScriptを、‘新規機能開発を止めずに’改善するために行った取り組みについて。及びその経過報告。
属人化したフロントエンドのJavaScriptを、 ‘新規機能開発を止めずに’改善するために行った取り組みについて。 及びその経過報告。
 
ゼロからのプログラミングRails講座 Codeanywhere版
ゼロからのプログラミングRails講座 Codeanywhere版ゼロからのプログラミングRails講座 Codeanywhere版
ゼロからのプログラミングRails講座 Codeanywhere版
 
Webteko 20090925
Webteko 20090925Webteko 20090925
Webteko 20090925
 
2014-02-20_Tokyo-GAS#5-社内活性化推進にGASを大活用した話
2014-02-20_Tokyo-GAS#5-社内活性化推進にGASを大活用した話2014-02-20_Tokyo-GAS#5-社内活性化推進にGASを大活用した話
2014-02-20_Tokyo-GAS#5-社内活性化推進にGASを大活用した話
 
VSCodeで始めるAzure Static Web Apps開発
VSCodeで始めるAzure Static Web Apps開発VSCodeで始めるAzure Static Web Apps開発
VSCodeで始めるAzure Static Web Apps開発
 
Zマイスターとの新たな価値探求 Rational
Zマイスターとの新たな価値探求 RationalZマイスターとの新たな価値探求 Rational
Zマイスターとの新たな価値探求 Rational
 
20120118 titanium
20120118 titanium20120118 titanium
20120118 titanium
 
プロダクトに 1 から Vue.js を導入した話
プロダクトに 1 から Vue.js を導入した話プロダクトに 1 から Vue.js を導入した話
プロダクトに 1 から Vue.js を導入した話
 
[Okta x Jamf合同新年会] Okta Workflowsによるノーコード業務改善 〜Jamf APIを使ってMac端末情報を自動収集してみよう〜
[Okta x Jamf合同新年会] Okta Workflowsによるノーコード業務改善 〜Jamf APIを使ってMac端末情報を自動収集してみよう〜[Okta x Jamf合同新年会] Okta Workflowsによるノーコード業務改善 〜Jamf APIを使ってMac端末情報を自動収集してみよう〜
[Okta x Jamf合同新年会] Okta Workflowsによるノーコード業務改善 〜Jamf APIを使ってMac端末情報を自動収集してみよう〜
 
RxDataSourceをNSDiffableDataSourceへ置き換える際のTips集紹介
RxDataSourceをNSDiffableDataSourceへ置き換える際のTips集紹介RxDataSourceをNSDiffableDataSourceへ置き換える際のTips集紹介
RxDataSourceをNSDiffableDataSourceへ置き換える際のTips集紹介
 

エネチェンジでのSTIの使い方紹介

  • 2. self.inspect [1] https://github.com/muramurasan/okuribito [2] http://poject.herokuapp.com/ ➢ gemのロゴを書いたり・・・ ➢ たまにQiitaの記事を書いたり・・・ ➢ 新卒で某SIerにてスマートメータのヘッドエンドシ ステムの開発に携わったあと、渋谷のベンチャー 企業にてRuby on Railsを学び、2016年10月エネ チェンジに入社。 ➢ なぜかPaypal、ベリトランス、SMBC、YDSなど決 済周り、請求周りを実装することが多い。 Name : Yuya Taki GitHub : yuyasat Qiita : yuyasat [1] 若輩者ですので、何卒優しくご教授いただけ ればと思います。 (commitはしていない) ➢ ○よ○よ風ゲームをReact.jsで実装し たり [2]
  • 4. STI(Single Table Inheritance)について ➢ STI(Single Table Inheritance, 単一テーブル継承)使ってますか? ➢ ORマッピングの一つ。 ○ クラステーブル継承、具象テーブル継承、単一テーブル継承 ➢ 伊藤淳一さんは「あえて使わないようにしている」とのこと。 ○ 6〜7年前の話なので今は状況が変わっているかも。 [1] https://qiita.com/yebihara/items/9ecb838893ad99be0561 [1] ➢ 僕は好き。 ➢ エネチェンジ でも使っています。
  • 5. 代理店とOEM Lite 代理店21社・OEM Lite 2社 が同居するマルチテナントとなっている ➢ エネチェンジとして受付 ➢ 事業者として受付代理店 OEM Lite ※ 2018年2月1日現在
  • 6. マルチテナントにおける悩み 事業者A 事業者B 事業者C お客さま番号 必須 必須 必須 供給地点特定番号 必須 必須 必須ではないが分かれ ば入れてもらう 事業所コード 必須 不要 不要 メールアドレス 必須 不要 必須 氏名 姓・名それぞれ 20字以内 姓・名合わせて40字以内 (姓・名は全角スペースで連結) 姓・名合わせて50字以 内 各事業者ごとに必要なバリデーションが異なる ・・・どう管理する?・・・
  • 7. 既存の実装 class Order < ActiveRecord::Base include ProviderA include ProviderB include ProviderC   ・   ・ end module ProviderA extend ActiveSupport::Concern included do with_options if: :provider_a? do |o| o.validates :email, presence: true o.validates :bill_office_number, presence: true ・ ・ end end end ➢ 一つのクラスで実装。 ➢ 各事業者のバリデーションを書いた concernを定義し、モデルで読み込む。 (事業所コード) (メールアドレス)
  • 8. 既存の実装の問題点1 class Order < ActiveRecord::Base include ProviderA include ProviderB include ProviderC   ・   ・ end module ProviderA extend ActiveSupport::Concern def phone_type_text_for_provider_a end end ➢ 同じようなメソッドが様々な concernに 実装されていく・・・。 ➢ 実装者によってメソッド名の流儀が違 う・・・。事業者数も多くレビュアーも把 握しきれない。 module ProviderB extend ActiveSupport::Concern def phone_type_text_for_provider_b end end module ProviderC extend ActiveSupport::Concern def provider_c_phone_type_text end end { '自宅' => 1, '携帯' => 2, '家族・親族 ' => 3, '配偶者' => 4, '家主・管理人 ' => 5, '事務所' => 6, 'その他' => 9, } { '事務所' => 1, '代表電話' => 2, '携帯電話' => 3, 'その他' => 4 }
  • 9. 既存の実装の問題点2 class Order < ActiveRecord::Base include ProviderA include ProviderB include ProviderC   ・   ・ end module ProviderA extend ActiveSupport::Concern LOW_VOLTAGE_EQUIPMENTS_FOR_A = { '空調' => 1, '冷凍/冷蔵' => 2, 'モータ' => 3, 'その他' => 9 } def low_voltage_equipment_text LOW_VOLTAGE_EQUIPMENTS_FOR_A.key(low_voltage_equipment) end end module ProviderB extend ActiveSupport::Concern LOW_VOLTAGE_EQUIPMENTS_FOR_B = { '冷凍/冷蔵' => 1, 'モータ' => 2, 'コンプレッサ' => 3, 'その他 ' => 9 } def low_voltage_equipment_text LOW_VOLTAGE_EQUIPMENTS_FOR_B.key(low_voltage_equipment) end end 気づいたら同名のメソッドが追加さ れ、後からincludeされたもので上書 きされる・・! ※実際にあったが、まったく同じメソッドであったため、問題が顕在化しなかった。
  • 10. 既存の実装の問題点3 class Order < ActiveRecord::Base include ProviderA include ProviderB include ProviderC   ・   ・ def applied_at_cannnot_be_in_the_past return unless applied_at && applied_at < Time.current errors.add(:applied_at, “過去の日付は使用できません”) end end module ProviderA extend ActiveSupport::Concern included do with_options if: :provider_a? do |o| o.validate :applied_at_cannnot_be_in_the_past end end end module ProviderB extend ActiveSupport::Concern included do with_options if: :provider_b? do |o| o.validate :applied_at_cannnot_be_in_the_past end end end➢ 複数の事業者に同じバリデーションメソッドを 違う条件で用たい。 後から呼ばれた条件だけに適用されてしま う! ※with_optionsの中にifを書くと後ろの条件しか適用されない問題と同じ。 (詳しくは[1], [2]をご覧ください) with_options if: :condition_a? do |v| v.validates :hoge, presence: true, if: :condition_b? end [1] https://github.com/rails/rails/blob/master/activemodel/lib/active_model/validations.rb#L154 [2] https://github.com/rails/rails/blob/master/activesupport/lib/active_support/callbacks.rb#L374
  • 11. class Order < ActiveRecord::Base with_options if: -> { provider_a? || provider_x? } do |o| o.validates :family_name, length: { maximum: 20 } o.validates :given_name, length: { maximum: 20 } ・ ・ end with_options if: -> { provider_b? || provider_y? } do |o| o.validates :family_name, length: { maximum: 30 } o.validates :given_name, length: { maximum: 30 } ・ ・ end end 各事業者ごとに一つのファイルで完 結 モデルにも事業者ごとのバリデー ションが記述されコードが散乱 理想 現実 事業者によっては同じバリデーショ ンの場合もある 今行なっている修正が、他の事業者へ影響があるかどうかすぐに分からない・・・! 既存の実装の問題点4
  • 14. 新実装 class OrderOfProvider::Base < OrderBase def phone_type_text phone_number.start_with?(/090|080|070/) ? '携帯電話' : '自宅' end end class ProviderA < OrderOfProvider::Base POHONE_NUMBER_TYPE = { '自宅'=> 1, '携帯' => 2, '家族・親族' => 3, '配偶者' => 4, '家主・管理人' => 5, '事務所' => 6, 'その他' => 9, } def phone_type_text # enumにしても良いかも。 POHONE_NUMBER_TYPE.key(phone_type) end end ➢ 親クラスによく使われる実装をしておけば、多く の子クラスではメソッドを実装しなくて済む。 ➢ 事業者クラスで違う処理にしたい場合は、事業 者クラスに実装すればよい。 ○ 他の事業者を気にせずに済む。 ○ 問題点2解消! class ProviderB < OrderOfProvider::Base POHONE_NUMBER_TYPE = { '事務所' => 1, '代表電話' => 2, '携帯電話' => 3, 'その他' => 4, } def phone_type_text POHONE_NUMBER_TYPE.key(phone_type) end end class ProviderB < OrderOfProvider::Base end ➢ 事業者毎に同じメソッド名で実装できる。 ○ 問題点1解消!
  • 15. class OrderOfProvider::Base < OrderBase def applied_at_cannnot_be_in_the_past return unless applied_at && applied_at < Time.current errors.add(:applied_at, “過去の日付は使用できません”) end end class ProviderA < OrderOfProvider::Base validate :applied_at_cannnot_be_in_the_past end class ProviderC < OrderOfProvider::Base validate :applied_at_cannnot_be_in_the_past def applied_at_cannnot_be_in_the_past return unless applied_at && applied_at < Time.current errors.add(:applied_at, “申込日は未来の日付にして下さい”) end end ➢ Ifの上書きを気にすることなく各事業者に同 じvalidationを適用することが可能。 ○ 問題点3解消! 新実装 class ProviderB < OrderOfProvider::Base validate :applied_at_cannnot_be_in_the_past end ➢ 各事業者クラスに用いられているバリデー ションが一つのファイルでわかる。 ○ 問題点4解消!
  • 16. まとめ ➢ STIにしたことで ○ 似たようなメソッド名の乱立が防げた。 ○ 意図せぬメソッドの上書きが防げた。 ○ with_optionsのifに悩まされずに済む。 ○ 事業者固有のコードを同じ箇所にまとめることができた。 ➢ それでも残る課題 ○ (STIの問題というわけではないが)電気単独申し込み、ガス申し込みなどを一つのテーブル で管理してしまっているが故に nullカラムが増えてしまっている。 ○ 新設計に移行したいが既存の事業者のロジックをすぐには移行できず、新旧が共存してし まっている。
  • 17. 最後に ➢ 100社以上電力会社を掲載・20社以上の申し込み受付に対応してきた ○ これらの知見をもとに設計を見直し、運用保守性の高いシステムにしていきたい! ➢ ビジネス速度も落とさない ○ テストを書く ➢ 電力業界のプラットフォームとなっていく ○ 開発リソースを確保しつつ、改善していくことができる環境。 ➢ 電気を使うことはなくならない! ○ 生活に密着したサービスをつくる
  • 18. 資料 2014.12.18 Rails でシングルじゃないテーブル継承 2016.12.04 みんなRailsのSTIを誤解してないか!? Catalog of Patterns of Enterprise Application Architecture NULL撲滅委員会