レガシーシステムから新システムにデータ移行をした話

:::note info
この記事は「[LITALICO Advent Calendar 2022](https://qiita.com/advent-calendar/2022/litalico)」のカレンダー3の16日目の記事です。
:::

# 何の記事？
これは、レガシーシステムに蓄積されたデータをフルスクラッチで再構築した新システムに移行した時の手法や注意事項、苦労話などを書いた記事です。

# 記事の見出し
- データ移行の背景
- データ移行の旧システムと新システム
- データ移行で行ったプロセス
- はまりポイントと苦労話
- 考察

# データ移行の背景
自社の事業所各拠点がそれぞれに業務を遂行する為に使っている業務基幹システムが老朽化したので、データ設計からシステム構成にかけて完全にフルスクラッチで作り替えました。
業務フローの変更を伴う大きな変更で、データベース構成や、アプリケーション単位、できる事、システムが想定する業務フロー、システムアーキテクチャ、インフラ構成に至るまで全く異なる物に生まれ変わり、この新システムに対してこれまで長きに渡って運営して蓄積してきた旧システムのデータの移行をする必要がありました。

# データ移行の旧システムと新システム
旧システムと新システムではUXや提供するソリューションが全く異なっていました。そのため蓄積されるデータ構造も異なっていて移行計画は難航を極めました。

## 新旧システムのイメージ

旧システム
```
モノリシックに作られた旧来の伝統的なシステムでデータベース構成もシンプルに作られている。
```

新システム
```
マイクロサービスを採用してそれぞれのドメイン毎にDBが分けられており、スキーマ構成も正規化が進んで業務に合わせた設計がされている。
```


旧システムは３つのサービス種類から構成されており、`サービス毎に３つ別々のシステムが用意されていました`。
データの管理はそれぞれに行われ相互にユニークキーの重複などもある状態でした。
３つの内２つはほぼ同じ構成のシステムでしたが、残り１つのサービスは簡易的なスプレッドシートとGASで管理されており、手入力データが行われる事により新システム側で想定しない形のデータを多数含んでおり、データ構造も異なりました。

| 新システム(マイクロサービス) | 旧システム(モノリシックだけど3つある) |
| --- | --- |
|![Monosnap Qiitaデータ移行記事用資料 - Google スライド 2022-11-28 20-07-54.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1576/b0de0b73-f814-f7a3-5a7c-c469f85df3a3.png)|![Monosnap Qiitaデータ移行記事用資料 - Google スライド 2022-11-28 20-07-24.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1576/ea6180f6-6bea-e951-e3da-405abaabaf08.png) |


## 旧システムから新システムへのデータの流れ
旧システムでは正規化レベルが低かったため１つのテーブルとして扱っていた情報が５つくらいにのテーブルに分かれたりしました。また、旧システムはサービス種類毎に異なる３つのシステムがあり、齟齬なく情報のユニーク性を保ちながらデータを移行しました。

![Monosnap Qiitaデータ移行記事用資料 - Google スライド 2022-11-28 20-47-25.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1576/53e8f1f2-a85e-1a4e-eec7-cd99919151e3.png)

データ移行は1月に第１弾、2月に第２弾、4月に第３弾という具合に事業所の各拠点毎に３段階に分けて行われました。
1月以降は新システムを利用する事業所と旧システムを利用する事業所が混在する状態となり、4月以降にすべての事業所が完全移行されました。
![Monosnap Qiitaデータ移行記事用資料 - Google スライド 2022-11-30 20-36-43.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1576/53e1ac30-719f-294e-bf8c-a8a2d161b664.png)


# データ移行で行ったプロセス
以下のようなプロセスを経てデータ移行を完成させました。

- 何を移行して何を移行しないか決める
- ステークホルダーを見極める
- 移行スケジュールの計画
- 新システムと旧システムのデータ構造の理解と意味を照らし合わせるマッピングを行った
- 移行の大方針を決める
- 移行データのカテゴライズ
- カテゴリ毎の移行方針を決める
- リハーサルとリリース手順

## 何を移行して何を移行しないか決める
まず、大量に存在する各種データの中から何を移行し、何を移行しないかを決めました。

具体的には
- どの台帳情報を移行するのか
- 運営情報、請求情報は移行するのか
- 段階的移行においてどの事業所をどの段階で移行するのか
- いつまで遡って情報を登録するのか
- 旧システムでどんどん登録されていく情報をいつまで移行対象として受け入れるか
- 複数に散らばるシステムからの移行において同じ利用者の名寄せを行うのか

などを検討していきました。

### 移行対象は未来に向けた情報のみとした

このシステムは毎月の運営と請求のために利用されるシステムなので、過去の情報を扱うよりも未来に向けた利用がメインとなります。
そのため、過去の実績等は移行しない事として、過去の実績に対する見直しが必要な場合には旧システムを使う事としました。
![Monosnap Qiitaデータ移行記事用資料 - Google スライド 2022-11-30 20-51-56.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1576/4a6c8157-e8c7-f98d-05a4-14e6d1115c47.png)
※ 移行後も旧システムは数年間稼働させておく

### 旧システムにどんどん登録される情報の扱い
1月運用開始データは12月末に移行する事に決まりました。データを受け取ってから複雑な工程を経て移行データに変換されるので、手順とチェックの時間を考慮して、
- 事業所データは10月中の物（それ移行増える予定がなかった）
- 関係機関データは12月第１週まで
- 利用者データは12月第２週まで

などとデータの種類特性毎にルールを決めました。
よりスタティックな情報は早い段階で締め切り、日々運用で増減する生データは柔軟な対応を許容できる体制を整えました。
<font color="blue">不確定要素が多いので、決められる事は意識的に早い段階で確定させていく</font>様にしました。

### 名寄せについて
初めはシステム毎にバラバラに登録された情報の複雑性と移行困難さや、同一人物を間違いなく特定する方法が最終的には人力になってしまう事から<font color="blue">名寄せはしない</font>との結論に至っていました。
しかし後に<font color="red">システム特性をよく理解していく内に名寄せがマスト要件である事が発覚</font>し、名寄せのルールなどを決めていきました。

同一人物の特定方法などもルール化しました
- 同じ住所で同じカナ姓名（漢字姓名だと登録者によって揺らぎが大きくなる）
- 兄弟児登録された児童それぞれの親
- 入力文字の揺らぎを吸収するために絞り込みをした上で目視で決めました
- 最終的には事業所拠点による確認をとって情報を担保


## ステークホルダーを見極める
### データの取得元は旧システムからだけではない
システムリプレイスが行われると言っても、必ずしも旧システムを元データとする必要はありません。
旧システムは日々運営されている生物のデータをリアルタイムで変更されていくので、取得元として常に最善というわけではありませんでした。
自社の事業所拠点や職員の情報などは結果的に社内の各部署のキーパーソンと連携しながら移行データを取得しました。

### ほぼスタティックなデータ
例えば、事業所のデータですが、自社事業所はそれほど頻繁に変動する物でもなく、ある時点で当年度の事業所新規開設の目処が決まればそれをマスタデータとして扱う事ができそうです。
こういったスタティックな情報は無理になま物であるシステムから取得する方法を考えるより、こちらで決まった情報を元にこちらの都合良いスキーマに適用させてデータを作ってしまう方が安全で確実でした。

その他市町村の情報もわざわざ旧システムから取得する必要はありませんでした。こういったスタティックな情報は早い段階で目処を付けてある程度早い段階で事前に用意するように努めました。

### 旧システムよりも情報が正確なデータ
在籍職員のデータ等に関しては旧システムに時間差で登録された情報よりもそのマスタ情報となる情報システム部が保有する社員一覧の情報の方が早くて正確だとわかりました。ただし、職員にもロールが様々あり、拠点職員として登録する場合、本社本部職員の場合、内部監査室の場合、マーケの職員の場合など登録されるべき情報が異なっていたので、それぞれの部署のキーパーソンと連携してこちらが指定するフォーマットに情報を埋めてもらう形で移行元データを入手するプロセスを取りました。
![Monosnap Qiitaデータ移行記事用資料 - Google スライド 2022-11-30 21-00-30.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1576/aa76d9ed-1d3b-f124-5f89-f05209c492f9.png)


## 移行スケジュールの計画
監査の観点から、実績のないシステムをいきなりすべての事業所で採用する事が許容されず、トライアル拠点として最初に１０事業所程度、その後追加で20事業所、最後に残り全部の事業所といった具合で３段階のデータ移行をする事になりました。
各移行時期の運用開始日から遡ってデータ移行の日程を決めました。
さらにそこから遡っていつまでに登録されたデータを受け入れ対象として許容するのかを決めました。

### 運営開始日から逆算してマイルストンを決める
最初の1月運営開始対象の移行スケジュールの場合以下の様に逆算していきました。
![Monosnap Sandbox - Google スプレッドシート 2022-12-14 15-50-39.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1576/f06a3eee-75a6-dd6e-a9b6-217f7ff292bf.png)




- いつデータを受領するか？
- いつまでに旧システムに登録された情報を受け入れるか

等を決めていきました。


## 新システムと旧システムのデータ構造の理解と意味を照らし合わせるマッピングを行った
**以下はイメージ**
| 旧システム保護者テーブル | データ移行元 | 備考 |
| --- | --- | --- |
| ID | - | 自動採番 |
| ユニークキー | 事前に採番 | |
| 法人ID | XXX | 固定値 |
| 姓 | 旧システム請求情報.保護者氏名<br>全角スペース区切りなので、前半部分を姓とみなす | 自動変換できない場合は人力でチューニングする |
| 名 | 旧システム請求情報.保護者氏名<br>全角スペース区切りなので、後半部分を名とみなす | 自動変換できない場合は人力でチューニングする |
| ... | ... | ... |

- 移行が必要な新システムの全テーブルを洗い出しました
- 個々のテーブルの１項目ずつ具体的に旧システムのどの項目をどのように加工して設定するかをマッピングしました


## 移行の大方針を決める
テーブル毎に細かい流れは異なるが、大まかな大方針をまず決めておく。
![Monosnap Sandbox - Google スライド 2022-11-28 23-01-42.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1576/a73bde04-95b7-85e6-ed64-b8f791f83052.png)
- 旧システムからCSVダウンロード
- 新システム側でCSVを受領
- 中間データを何段階か変換
- SQLを作成
- リハーサル用のデータ移行環境のDBに実際に何度も投入
- データ移行環境ではシステムと実際に接続し画面からも確認
- 上記の仕組みに問題無い事を事前に確認しておく
- 本番適用は、スケジュールにそってギリギリに受領した生データの加工から全プロセスを経て本番環境に投入される


## 移行データのカテゴライズ
大方針は決めつつも、移行するデータをいくつものカテゴリに分け、それぞれに手順を決めていきました。

### 移行データのカテゴリ
1. 設定系データ
1. 台帳系データ
1. 事業所系データ
1. 職員系データ
1. 利用者系データ

データを上記の様なカテゴリにわけました。

移行の順序は、どのデータがどのデータに依存するかを確認しつつ、最終的に<font color="blue">どのデータにも依存しない情報</font>を最初に移行する事として、上記の順番で移行する事になりました。

![Monosnap Qiitaデータ移行記事用資料 - Google スライド 2022-11-30 21-16-24.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1576/811c102d-7cb0-323a-50fd-0182875b22a8.png)

上に行くほど、依存度が低く、流動性も低いデータになります。設定情報は決めようと思えば1ヶ月前でも決めてしまえるので事前にほとんどの準備も終えておく様にしました。設定情報のIDなどは他のテーブルが利用したりするため、早い段階で決めてしまい不確定要素を減らしておきたい意図もありました。

下に行くほど流動性が高く、ユーザーからの要望として、ギリギリに旧システムに登録した情報も移行対象として欲しいと要求がありました。他のカテゴリデータへの依存度も高く、上のカテゴリが確定していないと決まらない項目が多々ありますので、手順としては、自動変換スクリプトなどを作っておき、後から上のカテゴリのデータが変わってしまってもすぐに変換作成できるように再現性を高めておきました。


## カテゴリ毎の移行方針を決める
上で決めたカテゴリ事に細かく段取りを決めておきます。

### ポイント
- どこからデータを取得するか
- どの部署からもらうか
- どのようなフォーマットでもらうか
- 受領したデータの正確性に応じて手作業でのサニタイズの可能性も考慮にいれておく（時間的制約になりうる為）
- テーブル毎に具体的な変換スクリプトの必要有無を確認しておく
- パスワードのような情報はどのように扱うか
- 複数のシステムが元データになる利用者系データはそれぞれの情報が元システム同士でぶつからないような考慮をいれておく


## リハーサルとリリース手順
初回移行の運営開始が1月だったので12月から本格的にリリースのスタンバイ体制に入りました。
あらかじめ決めたスケジュールに従って月初に職員系のデータを締め切り、本番用SQLの準備を済ませてしまいました。
利用者系のデータは12月2週目ごろに締め切りそこから旧システムでのCSV抽出を行いました。

旧システムではスクリプトを使って抽出しますが、結構そのままでは新システム側で使えない状態のデータがあるので、不備があれば目視で修正をかけます。

受領したCSVを中間データに変換してそこでも細かい変換を何段階かかけます。

名寄せのため名寄せマップを作成します。
名寄せマップとは、どの利用者がどの利用者と同じ人物か？をシステムIDで特定しておいた表になります。
データをギリギリに受領してマップ作成スタートになるので、あらかじめこの時間が確保できているかがキーになります。
この時は時間的余裕が与えられず結局徹夜気味で作業となってしまいました。

一度名寄せマップができてしまえば、事前に入念に作成したSQLを実行するだけですのでマップをつくるところまでが勝負でした。
名寄せSQLは、関連する１０余りのテーブルの情報をリレーション等の整合性を保って変換をかけてくれるようにスタンバイしていました。

名寄せまでおわったらデータ移行環境に実際にリハーサルとしてデータ投入し動作確認等チェックを行い、本番データ移行の日を迎えました。



# はまりポイントと苦労話
## 正規化されていないデータ

データの取得元が複数部署にわたりました。そのため、同じフォーマットに入れてもらった受領データは<font color="red">部署毎に異なる状態で入力されていました。</font>

### 具体的な事例
- NOT NULL項目にデータが設定されていない
- 空と思ったら半角スペースが入っている
- 半角スペース区切りと全角スペース区切りが混ざっている
- 全角アルファベットで入っている
- 半角数字項目に全角数字が入っている
- 全角カナ文字項目に半角カナが入っている
- 文字化けしている
- 同じデータが２レコード入っている
- そもそも間違った情報が入っている（整合性がとれず確認する所から発覚します）
- 想定外の組み合わせデータが存在する

データの厳密性を要求される場合には特に、他部署との意思疎通の難しさを痛感しました。

### ありえない組み合わせのデータへの対策
この新システムは厳密なデータの整合性が要求されており、例えば
```
項目XがTRUEの場合に項目Yに値が入っていてはいけない
```
や
```
〇〇開始日が××日よりも後ではいけない
```
など数えればきりが無いほど多数の制約があり、これらの制約を１つでも破るとシステムが動かないので事前に細心のチェックが要求されました。

残念ながら受領するデータはほぼ100%何らか想定外の組み合わせや不整合のあるデータが含まれていました。

対策としては、たくさんの関数を用意して受領データの相関関係チェックを行い、それを１つでもパスしなかったらデータ送り元に問い合わせて元データの改善を要求するというプロセスを確立する事で何度も修正とリトライができるような体制を整えました。


## 段階的移行（相互に稼働中のシステムの移行）
段階的移行もかなり苦しい物がありました。
1階目のデータ移行に関してはまっさらなシステムにデータ投入する形になるので、ある程度予測がつきましたが、2階目以降は以下のような難しさがありました。

- 新システムで既に登録した利用者が第２弾データ移行対象に登場する
- 新システム移行済みの利用者が旧システム上で名前変更された
- 旧システムで使っている利用者が新システム上で名前変更された
- 新システムに移行済み児童と旧システムでこれから移行する児童が兄弟。その場合保護者を名寄せしないといけない。
- 新システム移行済み職員が部署移動したので情報の紐付け直しがしたい

旧システムでも新しいデータが登録され新システムでも新しいデータが登録され、それらがさらに名寄せ対象だったりとカオスでした。

これに対しては、ある程度最初にルールを決める事で対応しました。
例えば、第１弾データ移行で投入した利用者データが第２弾投入対象にも存在する場合などは必ず先に新システムに投入した側を正とする。とか、名寄せ対象をマップさえあれば、複数の重複があっても必ず１つに収束するようなかなり複雑なSQLを事前に用意しておくなど。


## ３つの旧システムからのデータ移行
旧システムは３つあるが移行先の新システムは１つで、同じ管理番号を使っていたり、それぞれに同一人物が存在したりと気をつける点がたくさんありました。
また、３つの内１つ目はスプレッドシートとGASで管理されたシステムで、項目名も違いますし、そもそも新システムで必要とするデータが存在しなかったりしました。
構造の違う３つのシステムを１箇所にデータのユニーク性を担保しながら登録するのでかなり整合性チェックを入念に行いました。

## 名寄せ
#### 想定外だった事
段取りをまとめて方針が決まってきた所で序盤に不要と言われていた<font color="red">名寄せが必要</font>とわかりました。
システムの特性と利用の単位を考えると確かに名寄せは必要だと後になってわかりました。
元々新システムについても調査と並行して進めていた事もあり知らない事が多い中作業していたのでこの辺りの詰めの甘さが祟りました。


## 中間データにスプレッドシートを使った
今回の移行で特殊な事としては、中間データの加工をDBではなくスプレッドシートを使って行いました。
DBを使うメリットとしては、ある程度型指定をして厳密な情報を受け入れて、スクリプトやSQLで再現性の高いプロセスを作れる事にありますが、DBを使っていたら細かい動きに機敏に順応する事ができなかったと思っています。

`移行の相当前に完全な形の移行データがもらえるのなら話は別ですが` 、実際には運用中のシステムから移行当月までギリギリまで粘って最新のデータを取り入れたいと言われたりします。しかも仕様調査と並行してデータ移行プロセスを整えたりするので、途中段階ではわからない事がとても多く、時間的制約もついて回るので、「スクリプトを書いてもすぐに想定外の事が起きて作ったスクリプトの修正に何時間もかけたと思ったらまた想定外のデータを受領する」みたいなスパイラルに陥る可能性がありました。

今回はデータの種類も多く、30種類くらいのテーブルに移行が行われました。1テーブルの項目数も多く、個々の情報に対して受領元は３つのシステムがあり、項目事に正規化されていないデータが入ってくるという状況で、機敏に対応するためにスプレッドシート上で関数による変換を行い最終的にSQLに落とすという事をしました。
SQLになった情報はデータ移行環境でちゃんとリハーサルを繰り返し最終的に本物のスキーマに入る事は何度も確認を繰り返していますので、本番移行前にその辺りの確認は十分取れていました。

### スプレッドシートを使ったデータ移行までの流れ
1. 旧システムからCSV抽出
1. CSVを新システムの受け入れスプレッドシートに取り込み
1. ３システムからの情報をマージして新システムのスキーマに変換した別のスプレッドシートに変換
1. 新システムのスキーマで一度SQLを作成しDBに投入（データ移行環境で実施）
1. 新システムのDBに投入されたデータに対し名寄せSQLを適用
1. 必要なデータをInsert文にしてSQLを取得
1. 本番環境に名寄せされたSQLでInsert実行


# 考察
最終的にはデータが手に入る時期とそこから移行までの日程の調整、不確定要素の多さが特に心配な部分でした。
システムを使用して業務を運用する事業部からは、旧システムでの運用が12月ギリギリまで行われるのでデータ移行対象もギリギリまで受け入れてほしいと要請がありましたが、逆算すると日程的にはかなり厳しいと感じました。
そのため、ギリギリに受け入れてそこからサニタイズや加工、名寄せ、リハーサルまで行うのを可能にするために、事前にプロセスとしての安全性と再現性を高める事を意識しました。
ただ、どうしても名寄せプロセスだけは手作業で名寄せマップを作る工程が生まれ、ここだけは人海戦術で愚直にやるしかなく、他に良い方法はなかったかと考えるも答えはいまだ出ていません。

データ移行は元のシステムと先のシステムの特性にもよりますし、稼働したままやるのか止めてやるのか、など状況により対応方法は変わるかと思いますが、最終的にはシステムの利用者への運用負荷を増やさない形でスムーズに移行できるのが目的だと思います。

移行にあたっては、システム的に確実に一意になるものとそうでないもの、情報量が元々足りないもの等があり、どこまで救ってやるのかを決めるのも難しいポイントになります。「受領したデータが壊れていたんだから仕方がない」とか「受領したデータの通りにいれた。漏れてるものはしらない。」とかそういった割り切りの状況もありうるかとは思いますが、今回は名寄せへの対応や文字化けデータの目視救済、表記揺れに対してもギリギリまで正しい状態を作って移行を終えられたと思っています。