メールアドレスを会員IDにしたときの退会処理

ウェブサービスにおいて、会員IDをメールアドレスにするというのはわりとよくあることだと思います。このQiitaにしてもそうですが、ユーザ自身にニックネームを付けさせる場合は重複を排除するために、先に取った者勝ちになってしまうという問題点があります。その点、メールアドレスならそれ自体が世界的にユニークであることが保証されていますので、重複を心配する必要はありません。スキーマ定義としてはこんな感じでしょうか。

```SQL
create table accounts (
  email varchar(255) unique,
  password varchar(255),
  ...
);
```

このユーザがサービスから退会した際、潔いサービスならアカウントレコードを削除すれば済みます。しかし実際には退会済みユーザの情報も残しておかないといけない場合も多々あり、結果的に悪名高い「削除フラグ」を作らないとしょうがない場合もあります。削除フラグが立っている場合はサービスを利用できないなどの制限処理を実装します。スキーマ定義はこんな感じですかね。

```SQL
create table accounts (
  email varchar(255) unique,
  password varchar(255),
  locked boolean default false,
  ...
);
```

これで退会処理の実装も出来ましたが、退会したユーザが気が変わって再度登録しにきた場合はどうしましょうか。emailカラムにはunique制約を付けていますので、レコードを追加して再登録ということはできません。というところで、どういう対処法があるかなというのがこの記事のテーマです。

## 削除フラグをクリアする

新規登録ではなくアカウント復活として処理する方法ですね。これは実装も単純ですが、過去のデータが全部復元されることが気になります。それをありがたいというユーザも居るでしょうが、退会したのにデータが残っていることを気持ち悪いというユーザもいそうです。というか、自分としては気持ち悪いなと思ってしまいます。

退会してもデータを残すことを利用規約とかでちゃんとうたっておけば法的にはリスクは無いですが、ユーザの感じる印象はいかんともしがたいところです。印象だけではなく、過去のごちゃごちゃしたデータを一旦クリアするために退会と再登録をしたいという場合もありますので、それに対応できないということになります。

どうしてもクリアしたかったら別のメールアドレスを用意するしかありません。無料でメールアドレスを発行するサービスもありますが、メールアドレスの発行と管理の手間をユーザに押しつけるという問題は残ります。

## unique制約を外す

emailカラムに対するunique制約を外して、多重に登録可能にする方法です。退会は引き続き削除フラグで管理します。これなら再登録時にもデータは新規になりますし、退会時のデータもデータベースには残っていますので要求仕様は満たせます。スキーマ定義はさきほどと同じで実際のデータはこんな感じですね。

```
> select email, locked from accounts;
+-------------------+--------+
| email             | locked |
+-------------------+--------+
| user1@example.com |      0 |
| user1@example.com |      1 |
| user1@example.com |      1 |
+-------------------+--------+
```

ただ、unique制約に頼っていた多重登録排除の仕組みをアプリ側で実装しないといけなくなります。

## emailカラムを別の値に書き換える

これは過去にとあるプロジェクトで私が実際に使った方法です。退会時にemailカラムの内容をメールアドレスから「メールアドレス+エポック秒」に書き換えるようにしたんです。エポック秒にしたのは、再退会の時にもuniqueであるためです。具体的にはデータはこんな感じですね。

```
> select email from accounts;
+-----------------------------+
| email                       |
+-----------------------------+
| user1@example.com 123456789 | // 退会済み
| user1@example.com           | // 有効
+-----------------------------+
```

これでunique制約は外すことなく、再登録時には過去データを閲覧されることもなく、退会処理を実装することが出来ました。ただ、問題は「emailというカラムにメールアドレスではないデータが格納されている」という気持ち悪さですね。というか、メールアドレス以外のデータが格納されるのはそういうカラムだと定義しておけばいいだけの問題なんですが、メールアドレスとアカウントの有効/無効という二つの情報を一つのカラムに格納しているというのが正規化の面から非常に問題ですね。

この時のプロジェクトは私は途中参加で、再登録も当初仕様に入ってなかったのでアドホックな対応をそもそも求められてしまったからなんですけどね。とか苦しい言い訳をしたりとか。

結局、どうするのが一番いいんですかねぇ。[unique制約を外す](http://qiita.com/saoyagi2/items/f730554c95dd7c3223c2#unique%E5%88%B6%E7%B4%84%E3%82%92%E5%A4%96%E3%81%99)がマシかなぁという気が今のところしていますが。

とあるウェブサービスを利用しようとして「過去に退会した履歴があるため再登録出来ません」と言われてしまったので[^1]、そういう場合はどう設計するのがよかったんだろうというのを、過去の体験と合わせて思い出したので記事化してみました。結論がなくてすいません。

[^1]: ちなみにそのウェブサービス。問い合わせたところ「再登録できるようにしました」と返事がありまして、その後は本当に登録できるようになっていました。多分ですが、運用担当者がクエリ叩いてデータを直接書き換えてたんではないかなぁという気がします。いわゆる「運用で対処」って奴ですね。開発者としては、出来ればその方法は採りたくないので、あらかじめいい方法を考えておきたいところです。

(2017/8/23追記)

さらに別案を少し。

## 退会させない

そもそも退会させないってのもあり得ます。本当に退会は出来ないといけないのか。そこから考え直すのも一つの方法でしょうね。もちろん、退会したユーザのデータを残しておかないといけないのか、というのもしっかり考えておきたいところです。

## 別テーブルに待避する

退会者テーブルを別に用意して、データをそちらに移動させる方法です。

```SQL
create table retired_accounts (
  email varchar(255),
  password varchar(255),
  locked boolean default false,
  ...
);
```

という感じでしょうか。accountsの方は現時点で有効なアカウントのみが格納されることになります。スキーマの意味的には悪くはないのですが、accountsとリレーションを張っている他のテーブルのデータをどうするかという問題はあります。

## アカウント以外のデータをどうするか

例えばQiitaで退会すれば、そのユーザが投稿した記事は全部消えると思います。実際にデータベースから消えるかどうかはともかく、一般には不可視になるでしょう。

それはいいとして、ではコメントとかいいねとかはどうなるでしょう。ユーザが次々に退会すると、contiributionがどんどん減っていくのでしょうか。それは記事を書いてる身としてはモチベーションの面でちょっと辛い。

記事に付けられたコメントはどうでしょう。退会者自身としてはコメントも退会と同時に消えてほしいかもしれませんが、コメントに返信があった場合には返信だけが残って話の流れが見えなくなってしまいます。

とかとか、退会処理って考えることが実際には多いので、出来ればやりたくないなぁと言う気もしてきました。