なぜ「CSVファイルをファイルサーバに置く」という方法を、プロジェクト間のファイル連携で選択してはいけないのか

#はじめに
　システム開発において、プロジェクト間のデータ受け渡しにどのような方式を選択するかは開発上の重要事項である。なぜかというとソースコードの広範囲に影響があり、かつシステム環境構築にも関係するからである。ここで下手な方法を選んでしまうとシステムの要件を満たすことも難しくなるだろう。
　これには様々な方法が考えられるが、ほとんどの場合において「CSVファイルをファイルサーバに置く」という方法は適切でない。特別なプロジェクトでこれが適切であるという場合もあるかもしれないが。
　この理由をCSVファイルという形式が持つ問題と、ファイルサーバを利用するという問題2つの観点から説明する。

#想定

　複数のチームでシステム開発を行なうことを想定する。　
　「CSVファイルをファイルサーバに置く」とは以下のような流れとする。

1. プロジェクトAでCSVファイルをローカルフォルダに作成する。
2. 作成したCSVファイルをFTP等でファイルサーバに置く

![無題.png](https://qiita-image-store.s3.amazonaws.com/0/135472/f0a19b96-5a19-5c7b-fda6-9917130e6eec.png)

###CSVとは
　カンマ区切りのテキストデータ

```
yamada,20,156,65
tanaka,19,145,54
hamada,21,178,87
```

# CSVの問題点

　CSVはファイル連携に適した形式と言えない、以下のような問題が発生するからである。

## 階層構造を持つデータをうまく扱えない

　CSVファイルで表現できるデータ構造は限られている。二次元配列のデータはそのまま扱えるが、それ以外のデータ構造の場合、CSV形式で表せるような形式への変換が必要である。例えば以下のようなXmlファイルのデータ構造はそのままではCSVファイルに落とし込むことはできない。

```xml:CSVファイルで表すのが難しいデータの例
<?xml version="1.0" encoding="utf-8"?>
<Project>

  <PropertyGroup>
    <ProjectName>Hoge Project</ProjectName>
    <SubProject>
      <SubProjectName>Huga</SubProjectName>
      <SubProjectName>Piyo</SubProjectName>
    </SubProject>
  </PropertyGroup>

  <ItemGroup>
    <Item>ITEM1</Item>
    <Item>ITEM2</Item>
    <Item>ITEM3</Item>
    <Item>ITEM4</Item>
  </ItemGroup>
  
</Project>
```

　このためデータ連携を行なうプログラムでは、プログラム内部のデータクラスをCSV用のデータクラスに変換するクラスが増える。それに伴いCSV用データの項目が正しく変換されているかを確認するというテスト項目が増える。

##構造の変更に弱い
　データ構造が変更された場合に、プログラムに与える影響が大きい。マップ用のクラスを改修する必要があるため。
　扱うデータが二次元配列で項目が増えただけの場合でも、順序を間違えると正しく出力されないためテストが必要となる。

##プロジェクト間でCSVの形式のすり合わせが必要

　実はCSVはデータをカンマで区切るだけでない。特殊文字のエスケープも考える必要がある。既存のCSV向けパーサを用いればあまり問題になる箇所ではないが、以下のような観点が見過ごされていた場合、たまにシステムが止まる。

* カンマが項目中にある場合のエスケープ
* ダブルクォートが項目中にある場合のエスケープ
* 項目中に改行がある場合のエスケープ
* 項目以外にスペースがある場合のトリム
* 改行のみの行をエラーとするかどうか

　データ連携を行なうという目的に対して、懸念事項が増えることとなる。
　CSVの形式は標準化されているがあまり言及されることはないような気がする。[CSV標準化文書 rfc4180.txt](http://www.ietf.org/rfc/rfc4180.txt, "rfc4180.txt")

# ファイルサーバに配置するという点について

　以下の点に留意しなければいけない。

##同期実行は行えない
　ファイル受け渡しをプロジェクト間で同期実行することは行えない。ジョブスケジューラを用いてファイル出力とファイル読み込みそれぞれのプログラムを制御する必要がある。

## バックアップが必要
　連携ファイルをファイルサーバに置いただけでは、データ連携が成功したかどうかが分からない。そのため失敗したときのために連携ファイルをバックアップしておく必要がある。処理はクライアントのプログラムに記述することになる。

## セキュリティに注意する
　連携ファイルは一度ローカルフォルダに作成されるため、処理が中断された際にオペレーターにのぞき見られる可能性がある。オペレーターがのぞき見てよいファイルであるか、改ざんが行われないかを考慮してプログラムを作る。

## 環境構築をどうするか
　どのように環境を構築するかを前もって決めておいたほうが良い。バックアップ先、アップロード先を環境ごとに手動で設定するとヒューマンエラーが発生するので。



#　代替案

　「CSVファイルをファイルサーバに置く」という方法を使わずにファイル連携をする例を示す。
　連携ファイルのインタフェースはJSON、XML、YAMLといったプログラム上でシリアライズが簡潔に行えるものを利用する。

## プロジェクトごとにWebサーバを立てる

![無題.png](https://qiita-image-store.s3.amazonaws.com/0/135472/3d578cb5-0694-6aa0-be82-69c90ed5d98e.png)

　プロジェクトAのクライアントはプロジェクトBのWebサーバに対してデータ送信を行なう。プロジェクトBではデータを受け取り、処理を行った後、成功したかどうかのレスポンスを送信する。
　プロジェクトAの処理とプロジェクトBの処理が同期実行される。

## MOM(Message-oriented middleware)を使う

 　[メッセージ指向ミドルウェア-Wikipedia](https://ja.wikipedia.org/wiki/%E3%83%A1%E3%83%83%E3%82%BB%E3%83%BC%E3%82%B8%E6%8C%87%E5%90%91%E3%83%9F%E3%83%89%E3%83%AB%E3%82%A6%E3%82%A7%E3%82%A2, "Message-oriented middleware")
　[RabbitMQ](https://www.rabbitmq.com/, "rabbitmq")が多言語対応しており使いやすい。

![無題.png](https://qiita-image-store.s3.amazonaws.com/0/135472/f16ff2b3-c02b-b0b5-3229-a342c39b12cd.png)

　プロジェクトAのクライアントはMOMサーバに対して、データ送信のリクエストを送り、成功したかどうかをレスポンスとして受け取る。MOMサーバでは連携データをキューに格納する。プロジェクトBのクライアントはMOMサーバにデータ受信のリクエストを送り、連携データを受け取る。
　データ連携は非同期実行される。
　どのようなデータ連携を行ったかの記録はMOMサーバに集約され、クライアント側でバックアップを取らなくてすむようになる。

#さいごに

　プロジェクト間の連携データの形式にはCSVを選ばないようにしましょう。