Upgrade to Pro — share decks privately, control downloads, hide ads and more …

Realworld Domain Model on Rails

Realworld Domain Model on Rails

railsdm 2018の発表資料

Tomohiro Hashidate

March 25, 2018
Tweet

More Decks by Tomohiro Hashidate

Other Decks in Technology

Transcript

  1. self.inspect @joker1007 Repro inc. CTO ( 要は色々やる人) Ruby/Rails fluentd/embulk RDB

    Docker/ECS (Fargate ェ……) Bigquery/EMR/Hive/Presto 最近、kafka を触り始めました
  2. We are hiring Rails ごりごり書きたい人 データ量の多いサービスに興味がある人 DB パフォーマンスに敏感な人 Bigquery やPresto

    等の分散クエリエンジンを触りたい人 コード化されたインフラの快適度を更に上げたい人 色々と仕事あります!お声がけください!
  3. この話のテーマ 継続してアプリケーションを改善し続けるために、より良い設計 を考えるというのは大事なことだと思う。 その中でも、DDD という考え方は今日かなりの影響力を持ってい るし、有用だと思う。 Rails はWeb の開発効率に特化したフレームワークであり、現実的 にはDDD

    と相性が悪い点が多々ある。 一方で、Rails でかなり複雑なドメインを表現しなければならない ケースも増えてきている。 Rails において、現実的でかつ複雑なアプリに耐えうるモデルの設 計や表現方法を考えたい。
  4. DDD の概念とのマッピング 直接の対応関係にある訳ではないが、Rails が基本的に持っている 要素の中で近いと考えられるもの。 エンティティ ­> Model (AR or

    not AR) 値オブジェクト ­> Model (not AR) リポジトリ ­> ActiveRecord アプリケーションサービス ­> Controller ドメインサービス ­> Model (Service Class)
  5. 開発初期 開発初期はActiveRecord をそのまま利用するケースが多いと思 う。 AR のままでも集約境界とルートがコントロールできるなら問題な い。 c l a

    s s O r d e r < A c t i v e R e c o r d : : B a s e h a s _ m a n y : o r d e r _ d e t a i l s e n d c l a s s O r d e r D e t a i l < A c t i v e R e c o r d : : B a s e b e l o n g s _ t o : o r d e r e n d # O r d e r D e t a i l はO r d e r からしか触らないし生成されない
  6. Form オブジェクトのvalidation について validation をAR と二重でやるかについては好みがある。 私はAR に移譲できるものはAR に任せて、errors を収集する方が良

    いと思う。 G i f t F o r m = S t r u c t . n e w ( : b u d g e t , : d e t a i l s , k e y w o r d _ i n i t : t r u e i n c l u d e A c t i v e M o d e l : : V a l i d a t i o n s d e f v a l i d ? v a l i d i t y = @ g i f t _ d e t a i l s . a l l ? ( & : v a l i d ? ) @ g i f t _ d e t a i l s . e a c h _ w i t h _ i n d e x d o | g i f t _ d e t a i l , i d x | g i f t _ d e t a i l . e r r o r s . e a c h d o | a t t r , m e s s a g e | s e l f . e r r o r s . a d d ( " g i f t _ d e t a i l [ # { i d x } ] " , m e s s a g e : m e s s a g e n d e n d e n d e n d
  7. 単純なコレクションクラスの例 c l a s s R e t e

    n t i o n C o l l e c t i o n i n c l u d e E n u m e r a b l e d e f i n i t i a l i z e ( b a s e _ c o n v e r s i o n , c o n v e r s i o n s ) @ b a s e _ c o n v e r s i o n = b a s e _ c o n v e r s i o n @ r e c o r d s = c o n v e r s i o n s . e a c h _ w i t h _ o b j e c t ( { } ) d o | r , h | h [ r . i d ] = r e n d e n d d e f e a c h ( & b l o c k ) ; @ r e c o r d s . e a c h ( & b l o c k ) ; e n d d e f e a c h _ w i t h _ r e t e n t i o n _ r a t e ( & b l o c k ) e a c h d o | _ i d , r | y i e l d r , r . v a l u e . f d i v ( @ b a s e _ c o n v e r s i o n . v a l u e ) e n d e n d e n d
  8. よくあるテーブル定義の例 c r e a t e _ t a

    b l e : u s e r s d o | t | t . s t r i n g : e m a i l , n u l l : f a l s e t . s t r i n g : n a m e t . s t r i n g : e n c r y p t e d _ p a s s w o r d t . s t r i n g : c o n f i r m a t i o n _ t o k e n t . d a t e t i m e : c o n f i r m e d _ a t t . d a t e t i m e : c o n f i r m a t i o n _ s e n t _ a t t . s t r i n g : i n v i t a t i o n _ t o k e n t . d a t e t i m e : i n v i t a t i o n _ c r e a t e d _ a t t . d a t e t i m e : i n v i t a t i o n _ s e n t _ a t t . d a t e t i m e : i n v i t a t i o n _ a c c e p t e d _ a t e n d
  9. 駄目な理由 NULLABLE カラムの嵐になる ( あったり無かったり) 状態によって整合性を管理しなければならない範囲が変化す る 登録完了時には必須だが、confirm 待ちや招待の承認前は 不要

    他オブジェクトとの関連が、必須になったり初期化でき なかったりする 自身の状態とどういう遷移を経てきたかを管理する必要がある それはそのまま、分岐の氾濫や整合性違反に繋がる
  10. User の例 c r e a t e _ t

    a b l e : u s e r s d o | t | t . s t r i n g : e m a i l , n u l l : f a l s e t . s t r i n g : n a m e , n u l l : f a l s e t . s t r i n g : e n c r y p t e d _ p a s s w o r d , n u l l : f a l s e e n d c l a s s U s e r < A c t i v e R e c o r d : : B a s e v a l i d a t e s : e m a i l , p r e s e n c e : t r u e v a l i d a t e s : n a m e , p r e s e n c e : t r u e v a l i d a t e s : e n c r y p t e d _ p a s s w o r d , p r e s e n c e : t r u e e n d
  11. UserRegistration の例 c r e a t e _ t

    a b l e : u s e r s d o | t | t . s t r i n g : e m a i l , n u l l : f a l s e t . s t r i n g : c o n f i r m a t i o n _ t o k e n , n u l l : f a l s e t . d a t e t i m e : c o n f i r m e d _ a t t . d a t e t i m e : c o n f i r m a t i o n _ s e n t _ a t , n u l l : f a l s e e n d c l a s s U s e r R e g i s t r a t i o n < A c t i v e R e c o r d : : B a s e v a l i d a t e s : e m a i l , p r e s e n c e : t r u e v a l i d a t e s : c o n f i r m a t i o n _ t o k e n , p r e s e n c e : t r u e e n d Form オブジェクトがUserRegistration の整合性を確認 c o n f i r m e d _ a t の記録とUser の永続化を行う
  12. UserInvitation の例 c r e a t e _ t

    a b l e : u s e r s d o | t | t . s t r i n g : e m a i l , n u l l : f a l s e t . s t r i n g : i n v i t a t i o n _ t o k e n , n u l l : f a l s e t . d a t e t i m e : i n v i t a t i o n _ c r e a t e d _ a t , n u l l : f a l s e t . d a t e t i m e : i n v i t a t i o n _ s e n t _ a t , n u l l : f a l s e t . d a t e t i m e : i n v i t a t i o n _ a c c e p t e d _ a t e n d UserRegistration と同様Form オブジェクトが整合性を確認 i n v i t a t i o n _ a c c e p t e d _ a t の記録とUser の永続化を行う この時Form オブジェクトはUserRegistration の処理とは異なる
  13. どう変わったか Registration, Invitation はそれぞれ自分の中で2 値の状態だけ管 理すれば良い User はNOT NULL カラムonly

    になり、バリデーションが単純化 する 通知のコールバック等を定義する場所が明確になる User を利用する際に、登録に関するカラムやメソッドが邪魔 にならない
  14. イベントの発行 c l a s s O r d e

    r P l a c e d < R a i l s E v e n t S t o r e : : E v e n t e n d s t r e a m _ n a m e = " o r d e r _ 1 " e v e n t = O r d e r P l a c e d . n e w ( d a t a : { o r d e r _ i d : 1 , o r d e r _ d a t a : " s a m p l e " , f e s t i v a l _ i d : " b 2 d 5 0 6 f d ­ 4 0 9 d ­ 4 e c 7 ­ b 0 2 f ­ c 6 d 2 2 9 5 c 7 e d d " } ) # p u b l i s h i n g a n e v e n t f o r a s p e c i f i c s t r e a m e v e n t _ s t o r e . p u b l i s h _ e v e n t ( e v e n t , s t r e a m _ n a m e : s t r e a m _ n a m e )
  15. イベントの購読 c l a s s I n v o

    i c e R e a d M o d e l d e f c a l l ( e v e n t ) # P r o c e s s a n e v e n t h e r e . e n d e n d s u b s c r i b e r = I n v o i c e R e a d M o d e l . n e w e v e n t _ s t o r e . s u b s c r i b e ( s u b s c r i b e r , t o : [ I n v o i c e C r e a t e d , I n v o i c e
  16. AR のテーブル構造 c r e a t e _ t

    a b l e ( : e v e n t _ s t o r e _ e v e n t s _ i n _ s t r e a m s , f o r c e : f a l s e ) t . s t r i n g : s t r e a m , n u l l : f a l s e t . i n t e g e r : p o s i t i o n , n u l l : t r u e t . r e f e r e n c e s : e v e n t , n u l l : f a l s e , t y p e : : s t r i n g t . d a t e t i m e : c r e a t e d _ a t , n u l l : f a l s e e n d a d d _ i n d e x : e v e n t _ s t o r e _ e v e n t s _ i n _ s t r e a m s , [ : s t r e a m , : p o s i t i o n a d d _ i n d e x : e v e n t _ s t o r e _ e v e n t s _ i n _ s t r e a m s , [ : c r e a t e d _ a t ] a d d _ i n d e x : e v e n t _ s t o r e _ e v e n t s _ i n _ s t r e a m s , [ : s t r e a m , : e v e n t _ i d c r e a t e _ t a b l e ( : e v e n t _ s t o r e _ e v e n t s , i d : f a l s e , f o r c e : f a l s e ) t . s t r i n g : i d , l i m i t : 3 6 , p r i m a r y _ k e y : t r u e , n u l l : f a l s e t . s t r i n g : e v e n t _ t y p e , n u l l : f a l s e t . t e x t : m e t a d a t a t . t e x t : d a t a , n u l l : f a l s e t . d a t e t i m e : c r e a t e d _ a t , n u l l : f a l s e e n d