🎉

異なるフレームワーク間 でのアプリ マイグレーション

に公開

始めに

こんにちは。株式会社ペライチの開発部長の佐藤です。

ペライチでは、長年の機能開発を経てアプリケーション基盤がモノリシックに育ってきました。
また、 CakePHP, Backbone.js など、時を経て利用者が少なくなってきた FW をベースに開発をされていました。
サービスの拡大に向けて、このままのアーキテクチャで開発を続けることは、長期的に生産性低下につながるリスクがあると考え、マイクロサービス化、技術要素の刷新、統一を進めています。

その中でも今回は、とあるバックエンドサービスを CakePHP から Ruby on Rails へマイグレーションしたときの工夫についてお話します。

移行概要

移行した環境は下記の図のようになっており BE の CakePHP と DB を新しい環境へマイグレーションすることが今回のスコープです。
移行概要

課題感

今回移行対象となっている機能はペライチの中でも、障害を起こすと影響が大きくユーザーにも会社にも損失も大きいクリティカルな箇所で、安全に移行できる方法を考える必要がありました。
そこで、移行中に何かあった時の影響範囲を最小限にとどめるため、移行前と移行後のシステムを並行稼働し、 API を徐々に切り替えていく方式を検討しました。

解決策

新旧のサービスを並行稼働しながら移行するにあたって必要なことは以下だと考えました。

  • 新旧のサービスで API の振る舞いを変えない
  • 旧のモノリスと、新のマイクロサービスで DB を同期しながら移行する

特に DB を同期の問題については、移行期間中は新旧アプリから同じ DB, テーブルを参照することで解消しようとしました。
具体的にはこの構成で移行することにしました。
result

以下にどのような工夫をしたのか記載します。

1. Active Record の複数データベース

Ruby on Rails はバージョン 6.1 から複数のデータベースに接続する設定があるので、こちらを利用しました。

rails_guide

ApplicationRecord を継承したクラスで以下のように書けば特定のモデルで個別に指定したデータベースに接続できます。

  connects_to database: { writing: :animals, reading: :animals }

今回のケースはすでに Rails 用の DB も用意があったため、一部のモデルは別の DB (今回で言うと Cake 側の DB )を参照するように指定しました。

2. CakePHP と Rails でテーブルのタイムスタンプ形式が違う課題への対処

CakePHP と Rails では以下のようにタイムスタンプの形式が異なり、Rails で適切にタイムスタンプの値を取り扱えないことが課題でした。

cakephp: created, modified, deleted_date
Rails: created_at, update_at, discarded_at

この課題に関しては、まず、移行対象テーブルに Rails 用のタイムスタンプを追加しました
rails_guide

さらに CakePHP と Rails の callback メソッド(beforeSave や before_save など)で、双方のタイムスタンプをコピーする処理を実装しました。

CakePHP サンプルコード

  public function beforeSave(EventInterface $event, $entity, ArrayObject $options)
  {
    if ($entity->isNew()) {
      $tableSchema = $this->_table->getSchema();
      if ($tableSchema->hasColumn('created_at')) {
        $entity->set('created_at', Time::now());
      }
      if ($tableSchema->hasColumn('updated_at')) {
        $entity->set('updated_at', Time::now());
      }
    }
  }

Rails サンプルコード

  def copy_created_timestamp
    return if !respond_to?(:created) || created.present?

    self[:created] = Time.zone.now
    self[:modified] = Time.zone.now if respond_to?(:modified)
  end

結果

これらの対応をすることで、 DB を共有した形で CakePHP と Rails の並行稼働を実現できました。
これのおかげで 1API ずつ BE にリリースしつつ、 FE も 1 箇所ずつ API の向き先を変えることで一度のリリースの影響を小さくして移行を進めることができています。
result

採用情報

現在エンジニア募集しています!

▼ 採用ページ
https://recruit.peraichi.co.jp/

▼ 選考をご希望の方はこちら(募集職種一覧)
https://recruit.peraichi.co.jp/careers/?utm_source=techblog&utm_medium=referral&utm_campaign=article-01jez3tt1eha5bn4djetnqd9qv

▼ まずはカジュアル面談をご希望の方はこちら
https://casual.hp.peraichi.com/?utm_source=techblog&utm_medium=referral&utm_campaign=article-01jez3tt1eha5bn4djetnqd9qv

募集中の職種についてご興味がある方は、お気軽にお申し込みください(CTO がお会いします)

ペライチ

Discussion