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

ORM - Object-relational mapping

ORM - Object-relational mapping

2023-03-19 YAPC::Kyoto 2023

Takafumi ONAKA

March 19, 2023
Tweet

More Decks by Takafumi ONAKA

Other Decks in Technology

Transcript

  1. 自己紹介 • 大仲 能史 a.k.a. onk • 芸歴19年 今年大厄 •

    株式会社はてな チーフエンジニア • Perl歴: 高校〜大学 + はてなの 5 年 ◦ はてな入社以来、Perl プロダクトを担当 2
  2. Rowへのマッピング • DBのテーブルの1行を1オブジェクトにする ◦ fetchrow_arrayref, fetchrow_hashrefして得られた 値をRowクラスのインスタンスにする • インピーダンスミスマッチの解消 ◦

    関連や、データ制約、トランザクション等をRowでも 表現していく ◦ 変換処理を持つ場合も ▪ datetime のカラムは DateTime obj になるとか 11
  3. TableDataGateway 14 # Read できる my $row1 = Gateway->single(id =>

    1); # Create できる my $row2 = Gateway->create(id => 2, name => 'Jiro'); # Update できる my Gateway->update(name => 'Sabu', { id => 2 }); • CRUDを司る、テーブルと紐付いたクラス
  4. RowDataGateway 16 my $row = Row->new(id => 2, name =>

    'Taro'); # Create できる $row->create; # Update できる $row->update(name => 'Sabu'); • Row自身がCRUD操作を知っている
  5. ORM概論 > PoEAAの4つのパターン 21 パターン名 永続化層とModelが ドメインロジック置き場 TableDataGateway 別クラス トランザクションスクリプト

    RowDataGateway 同じクラス トランザクションスクリプト ActiveRecord 同じクラス ドメインモデル DataMapper 別クラス ドメインモデル
  6. ORMのCPANモジュール • DBIx::Class ◦ たぶんデファクト ◦ カヤックさん方面でよく聞く • Teng ◦

    YAPCで無限に聞いていたので馴染みがある • Aniki ◦ DeNAさん、モバファクさん方面で聞いていた 24
  7. はてなでよくあるアーキテクチャ 26 package Sample::Service::User; sub change_profile { my ($class, $id,

    $name) = @_; my $user = $class->find_by_id(db => $db, id => $id); return unless $user; return unless $class->validate_name($name); $class->update_user( db => $db, user => $user, params => { name => $name }, ); }
  8. はてなでよくあるアーキテクチャ 27 package Sample::Service::User; # Read sub find_by_id { my

    ($class, $db, $id) = @_; return $db->select_row_as(q[ SELECT * FROM user WHERE id = :id ], { id => $id }, 'Sample::Model::User' ); }
  9. はてなでよくあるアーキテクチャ 28 package Sample::Service::User; # Update sub update_user { my

    ($class, $db, $user, $params) = @_; # 変更がなかったら更新しない return if ( $user->name eq $params->{name} && $user->type eq $params->{type} ); $db->query(q[ UPDATE user SET name = :name, type = :type WHERE id = :id ], { name => $params->{name}, type => $params->{type}, id => $user->id, }); }
  10. Tengの場合の差分 32 package Sample::Service::User; sub change_profile { my ($class, $id,

    $name) = @_; my $user = $class->find_by_id(db => $db, id => $id); return unless $user return unless $class->validate_name($name); $class->update_user( db => $db, user => $user, params => { name => $name }, ); }
  11. Tengの場合の差分 33 package Sample::Service::User; # Read sub find_by_id { my

    ($class, $db, $id) = @_; - return $db->select_row_as(q[ - SELECT * FROM user - WHERE id = :id - ], - { id => $id }, - 'Sample::Model::User' - ); + return $teng->single('user', { id => $id }); }
  12. Tengの場合の差分 34 package Sample::Service::User; # Update sub update_user { my

    ($class, $db, $user, $params) = @_; # 変更がなかったら更新しない return if ( $user->name eq $params->{name} && $user->type eq $params->{type} ); - $db->query(q[ - UPDATE user - SET name = :name - SET type = :type - WHERE id = :id - ], { - name => $params->{name}, - type => $params->{type}, - id => $user->id, - }); + $teng->update('user', $params, { id => $user->id }); }
  13. Greppabilityの確保 38 package Sample::Service::User; # Read sub find_by_id { my

    ($class, $db, $id) = @_; - return $teng->single('user', { id => $id }); + return Sample::Repository::User->single({ id => $id }); }
  14. AbstractRowの導入 • すべてのRowの親クラス • いくつか共通のメソッドを置いた ◦ new_from_hash ▪ テスト時とかに便利 ◦

    bless_class_accessor_lite ▪ 今までのCALのRowクラスとの相互運用のため ◦ will_changes ▪ いわゆるdirty(is_changed) ▪ これから$paramsに変更するとdirtyになるか?を返す 41
  15. プラグインで拡張していく • RepositoryとRowの親クラスができた • どんどん拡張できる ◦ search_in_period ▪ WHERE start_at

    <= :now AND :now < end_at ▪ $row->in_period #=> Bool ◦ insert_or_update ▪ なければinsert、あればupdateを正しくやる 42
  16. まとめ 50 • ORM導入でアーキテクチャが分かりやすく ◦ 重厚なServiceを2つの役割に分割した ▪ TableDataGatewayとしてのTeng ▪ トランザクションスクリプトを書くService

    • 導入の痛みは少ない ◦ 単純に移行するなら逐次変換でいける ◦ ORMっぽいコードに変換するならもう一手間加える
  17. まとめ 51 • ORMっぽいコードとは ◦ 共通化による加速 ▪ 同じことを同じように行う ▪ 拡張ポイントが分かりやすく、プラグインを書く圧がある

    ◦ ORMに任せるとコード量が激減する ▪ 普通のことは既に実装されている ▪ 普通じゃない=複雑なところだけコードを書いていく