Slide 1

Slide 1 text

実際のところHanamiって どうなんですか? という話 2019/12/11 LegalForce Ruby Meetup #1

Slide 2

Slide 2 text

࣌෢ ༎ଠ גࣜձࣾLegalForce औక໾CTO 2014年 Πϯλʔϯͱ͍͔ͯͭ͘͠ͷWebαʔϏε։ൃΛܦݧ 2016年 DeNAʹ৽ଔೖࣾɺεϚϗΞϓϦͷ։ൃʹैࣄ 2017年 LegalForceʹࢀըɺݱ৬ 2019年 CTO of the year 2019ड৆ ౦ژେֶେֶӃ ৘ใཧ޻ֶܥݚڀՊ ଔۀ

Slide 3

Slide 3 text

https://jp.techcrunch.com/2019/11/22/cto-night

Slide 4

Slide 4 text

LegalForceとは

Slide 5

Slide 5 text

弁護⼠が作った会社です

Slide 6

Slide 6 text

⾓⽥弁護⼠ (CEO) ⼩笠原弁護⼠

Slide 7

Slide 7 text

でも、お堅くありません

Slide 8

Slide 8 text

No content

Slide 9

Slide 9 text

No content

Slide 10

Slide 10 text

No content

Slide 11

Slide 11 text

契約書レビューシステム を作っています

Slide 12

Slide 12 text

ܖ໿ॻ

Slide 13

Slide 13 text

ࢴͱϖϯͰ

Slide 14

Slide 14 text

ͻͱͭͻͱͭ੺ೖΕ

Slide 15

Slide 15 text

No content

Slide 16

Slide 16 text

2019೥ 4݄ 5݄ ໿ 180ࣾ 6݄ 7݄ 8݄ 9݄ 10݄ 導⼊顧客数 100ࣾಥഁʂ

Slide 17

Slide 17 text

技術に⼒を⼊れています

Slide 18

Slide 18 text

京都⼤学との共同研究 共同研究者兼技術顧問 森 信介 京都⼤学学術情報メディアセンター 京都⼤学情報学研究科 知能情報学専攻 教授 技術顧問 末永 幸平 京都⼤学⼤学院情報学研究科 通信情報システム専攻 准教授

Slide 19

Slide 19 text

技術顧問 まつもとゆきひろ プログラミング⾔語Ruby開発者 ⼀般財団法⼈Rubyアソシエーション理事⻑ ほか肩書多数 Matzを技術顧問として招聘

Slide 20

Slide 20 text

コミュニティへの貢献

Slide 21

Slide 21 text

https://rubykaigi.org/2019/

Slide 22

Slide 22 text

No content

Slide 23

Slide 23 text

2019年のスポンサー 3⽉ ⾔語処理学会第25回年次⼤会 (NLP2019) Rails Developer Meet Up 2019 4⽉ RubyKaigi 2019 8⽉ NLP若⼿の会 (YANS) 第14回シンポジウム 9⽉ WebDB Forum 2019 11⽉ RubyWorld Conference 2019 ICPC 2019 Asia Yokohama Regional

Slide 24

Slide 24 text

アジェンダ 話すこと • Hanamiは実際のところどうなのか • Hanamiをどんな感じで使っているのか 話さないこと • Hanamiのコンポーネントそれぞれについての詳細

Slide 25

Slide 25 text

なぜHanamiなのか

Slide 26

Slide 26 text

興味 • Clean Architectureよさそう • キレイに書けそう • フレームワーク名気に⼊った

Slide 27

Slide 27 text

話題性 • RailsやSinatraとは違う⽬新しさ • Railsに疲れてきた、飽きてきたエンジニアに訴求できそう • 技術発信すると注⽬されそう

Slide 28

Slide 28 text

で、実際のところどうなの?

Slide 29

Slide 29 text

保守性 • あのロジックどこにあるっけ?はRailsより少ない(気がする) • ビジネスロジックがapp間で共有できるのは便利 • Fat ControllerやFat Modelは形成されにくい • アーキテクチャへの知識は多少なりと要求される

Slide 30

Slide 30 text

パフォーマンス • Railsよりは薄いのでその分速い(書き⽅にもよる) • Railsよりメモリ消費量少なめ

Slide 31

Slide 31 text

開発速度 • 最初期の基盤構築はRailsより⼤変だった • ⼀度構築終わればあとはRailsと⼤差ないイメージ

Slide 32

Slide 32 text

カスタマイズ性 • ほぼVanilla Rubyなのでカスタマイズを⼊れやすい • ActiveSupport的な便利機能はHanami::Utilsモジュールにある • カスタマイズしすぎると理解不能になりそう。。

Slide 33

Slide 33 text

キャッチアップ • さすがにRailsと⽐べるとガイドは充実していない • ⽇本語での解説記事はほとんど無い • Railsを使いこなせる⼈であれば数週間程度で把握できる

Slide 34

Slide 34 text

実際のところ Hanami Ruby on Rails 保守性 ◎ ○ パフォーマンス ◎ ○ 開発速度 ○ ◎ カスタマイズ性 ◎ △ キャッチアップ △ ◎

Slide 35

Slide 35 text

Hanamiといえば

Slide 36

Slide 36 text

Clean Architecture

Slide 37

Slide 37 text

https://blog.cleancoder.com/uncle-bob/2012/08/13/the-clean-architecture.html

Slide 38

Slide 38 text

View Action Interactor Result Interactor Interactor Input Flow of control Actions Interactors Entities R epositories View s rack sequel ER B

Slide 39

Slide 39 text

Actionの⾃動⽣成

Slide 40

Slide 40 text

Action • ユーザーからのHTTPリクエストを扱うレイヤー • HTTP paramsからInteractorへのInputを整形する • Interactor ResultをViewへ渡せるように 整形する View Action Interactor Result Interactor Interactor Input Flow of control

Slide 41

Slide 41 text

Action class Web::Users::Create include Web::Action params do required(:name).filled(:str?, max_size?: 20) required(:gender).filled(:str?, included_in?: ["man", "woman", "other"]) required(:age).filled(:int?, gteq?: 0, lteq?: 100) end def call(params) result = UsersCreateInteractor.new.call(params) halt_by_error(result.errors) if result.failure? self.status = 201 self.body = UsersCreateResponse.create(result.output) end end

Slide 42

Slide 42 text

Action class Web::Users::Create include Web::Action params do required(:name).filled(:str?, max_size?: 20) required(:gender).filled(:str?, included_in?: ["man", "woman", "other"]) required(:age).filled(:int?, gteq?: 0, lteq?: 100) end def call(params) result = UsersCreateInteractor.new.call(params) halt_by_error(result.errors) if result.failure? self.status = 201 self.body = UsersCreateResponse.create(result.output) end end パラメータバリデーション ← Interactor呼び出し ← エラーハンドリング ← レスポンス作成

Slide 43

Slide 43 text

なんか⾃動⽣成できそう

Slide 44

Slide 44 text

OpenAPI (Swagger)

Slide 45

Slide 45 text

No content

Slide 46

Slide 46 text

OpenAPI • スキーマにバリデーションが付けられる name: type: string maxLength: 20 gender: type: string enum: ["man", "woman", "other"] age: type: number minimum: 0 maximum: 100

Slide 47

Slide 47 text

OpenAPI name: type: string maxLength: 20 gender: type: string enum: ["man", "woman", "other"] age: type: number minimum: 0 maximum: 100 required(:name) .filled(:str?, max_size?: 20) required(:gender) .filled(:str?, included_in?: ["man", "woman", "other"]) required(:age) .filled(:int?, gteq?: 0, lteq?: 100) Hanami

Slide 48

Slide 48 text

Actionの⾃動⽣成運⽤を 検討してます。が

Slide 49

Slide 49 text

まだ達成できてません。。 誰か助けて!

Slide 50

Slide 50 text

DIコンテナの利⽤

Slide 51

Slide 51 text

依存性逆転の原則 (DIP) • モジュールの依存関係は抽象だけを参照すべきである • 抽象は具象に依存せず、具象が抽象に依存すべきである

Slide 52

Slide 52 text

https://blog.cleancoder.com/uncle-bob/2012/08/13/the-clean-architecture.html

Slide 53

Slide 53 text

Hanamiでよくあるコード user_repo = UserRepository.new book_repo = BookRepository.new payment_repo = PaymentRepository.new interactor = HogeInteractor.new( user_repo, book_repo, payment_repo ) interactor.call(params)

Slide 54

Slide 54 text

DIコンテナ導⼊後 class HogeInteractor include Hanami::Interactor extend DIContainer include modules( :user_repository, :book_repository, :payment_repository ) def call(params); end end HogeInteractor.new.call(params)

Slide 55

Slide 55 text

DIコンテナ導⼊後 • include modules部分を⾒るだけで依存モジュールが明確に • SomeClass.newのコードが減って可読性アップ

Slide 56

Slide 56 text

マルチデータベース問題

Slide 57

Slide 57 text

Repositoryについて

Slide 58

Slide 58 text

Repository • データの永続化ロジックを実装したレイヤー MySQL PostgreSQL MongoDB UserRepository #where(name:) #find(id:)

Slide 59

Slide 59 text

Repository ActiveRecord (Rails) users = User.where(age: 20).order(created_at: :desc) Hanami users = UserRepository.new.where(age: 20).order { created_at.desc } ほぼ⼀緒では…?

Slide 60

Slide 60 text

Repository ActiveRecord (Rails) users = User.where(age: 20).order(created_at: :desc) Hanami users = UserRepository.new.where(age: 20).order { created_at.desc } ⾮推奨

Slide 61

Slide 61 text

Private Queries クエリの発⾏ロジックをRepository内でカプセル化する class UserRepository < Hanami::Repository def most_recent_by_age(age) users.where(age: age).order { created_at.desc } end end UserRepository.new.most_recent_by_age(25)

Slide 62

Slide 62 text

Private Queries クエリの発⾏ロジックをRepository内でカプセル化する ↓ • Repositoryの内部実装を知らずに使える • どこでメソッドが使われているか探しやすい • 可読性が⾼い • テストしやすい

Slide 63

Slide 63 text

LegalForceはマルチテナンシー • 1社につき1つのMySQLデータベース+共有データベース Amazon Aurora 共有 テナント1 テナント2 テナント3

Slide 64

Slide 64 text

Hanami::ModelはマルチDB⾮対応 • Railsはバージョン6からマルチDBが正式対応されたので、 今マルチDBを検討するならRailsのほうがつらくないかも

Slide 65

Slide 65 text

どうやってるのか • DBスイッチロジックを⾃前で書いてます DB Container DB Container request Interactor Repository MySQL

Slide 66

Slide 66 text

マイグレーション問題 1テナント1データベース、ということは

Slide 67

Slide 67 text

No content

Slide 68

Slide 68 text

マイグレーションヤバい!

Slide 69

Slide 69 text

おや、どこかで⾒覚えが…?

Slide 70

Slide 70 text

https://tech.smarthr.jp/entry/2018/04/06/100000

Slide 71

Slide 71 text

Citusへの移⾏も検討中です 誰か助けて!

Slide 72

Slide 72 text

こんな感じで ぼちぼちやってます

Slide 73

Slide 73 text

⼀定のHanami運⽤知⾒は たまってきたものの

Slide 74

Slide 74 text

開発・改善するところ まだまだあります…!

Slide 75

Slide 75 text

No content