Laravel 5でサービスをフルリニューアルした話

こんにちは！@best_not_bestです。
私は現在、[dots.](http://eventdots.jp/)の開発を担当しています。このサービスは全てLaravelで動いています。今回はこのシステムの裏側を説明したいと思います。

## dots.とは？

「エンジニアのため」のイベント情報検索サービスです。
2013年10月にローンチし、2015年12月21日現在、ユーザー数は約34,000人になります。
ちなみにローンチ日は10月2日で、ドッ(10)ツ(2)を意味しています(´･ω･`)
2015年7月にシステムリニューアルを行い、その際にフレームワークをCakePHPからLaravelに変更しています。

## 使用しているもの

### ミドルウェア

- PHP 5.5
  - Laravel 5.0
  - fluent/logger
  - guzzlehttp/guzzle
  - aws/aws-sdk-php-laravel
  - watson/validating
  - dakatsuka/monolog-fluent-handler
  - predis/predis
  - illuminate/html
  - sboo/multiauth
  - abraham/twitteroauth
  - league/flysystem-aws-s3-v2
  - mockery/mockery
  - laravel/socialite
  - fabpot/goutte
  - barryvdh/laravel-cors
  - intervention/image
  - thujohn/twitter
  - cdaguerre/php-trello-api
  - thepixeldeveloper/sitemap
  ・・・等
- Nginx 1.8
- MariaDB 10.1
- Redis 3.0
・・・等

### その他ツール

- GitLab
- Jenkins
- Redmine
- Slack
- Trello
・・・等

## なぜLaravelか？

dots.のモットーは「エンジニアのためのサービスなのだから、エンジニアから見てイケてる技術を使おう」です。~~その割にはPHPのバージョン古くないですか？~~
選定当時もっともイケてたフレームワーク、それがLaravelでした。
Webサービス構築に必要な機能は一通り揃っていることに加え、直感的に書けるEloquent ORM、そしてCashier等の外部サービス連携機能もあります。上記した通り、ライブラリも豊富にあります。

## システム構成

<img width="573" alt="システム構成図" src="https://qiita-image-store.s3.amazonaws.com/0/17452/4a7c91de-d69a-d310-fefc-00723e56b5e1.png">

機能は大きく分けてバッチ/管理ツール/Webサイトの3種類あります。
ソースは共通で、管理ツールとWebサイトの出し分けは以下のようにドメインで行っています。

```php:app/Http/routes.php
switch (getenv('APP_ENV')) {
    case "production":
        $env = "";
        $https = "https";
        break;
    case "hoge":
        $env = "hoge.";
        $https = "https";
        break;
    （中略）
    default:
        $env = "local.";
        $https = "http";
        break;
}

Route::group([
    'namespace'  => 'Web',
    'domain'     => $env . 'eventdots.jp',
    function () use ($https) {
        Route::get('/', ['as' => 'web.hogehoge', 'uses' => 'HogeController@index', 'http']);
        Route::get('login', ['as' => 'web.auth.login', 'uses' => 'AuthController@login', $https]);
    }
);

Route::group([
    'namespace'  => 'Adm',
    'domain'     => "xxx.{$env}eventdots.jp",
    'https'      => true],
    function () {
        Route::get('auth', ['as' => 'xxx.auth.moge', 'uses' => 'AuthController@moge']);
    }
);
```

### バッチ

dots.上で表示されているイベントの約8割は、各イベント支援サービス様のAPIから取得しています。各サービス上で公開されるとdots.上にも公開されます。（例外あり、後述の「タグ付け」参照。）
イベント取得バッチはスケジューラから起動し、キューに格納後、順に実行されます。資料（スライド）取得バッチも同様に実行されます。

また、毎朝、前日に公開かれた新着イベントと資料の一覧をメールで配信しているのですが、それらはバッチサーバから配信されています。

### 管理ツール

残り2割のイベントはdots.主催のイベントです。イベント主催者様、またはdots.内部の運用メンバーが管理ツールから入稿します。

### Webサイト

皆さんが見ているサイトがここで動いています。
`laravel/socialite`を使用していてソーシャルログインが可能です。

## タグ付け

各イベントはタグが付けられています。例えば、[Cookpad TechConf 2016](http://eventdots.jp/event/573829)は以下のようなタグになります。
<img width="405" alt="タグ" src="https://qiita-image-store.s3.amazonaws.com/0/17452/9561cf20-8fad-57eb-9e47-3d05ef2a31df.png">

管理ツールの場合は入稿時に手動でタグが付けられますが、バッチの場合は、取得時にイベント本文を`php-mecab`で形態素解析して抽出した単語をタグとして付けています。
バッチの場合、タグが付けられないとイベントが公開されません。（データベースには「非公開」状態で格納されますので、後ほど手動でタグ付けすることは可能です。）
これらは「エンジニア向けのイベント」のみを取得するために行っているのですが、まだまだ解析の精度が甘く、今後の改善ポイントです。

## 今後の課題

- 前述したタグ付けの精度改善（ライブラリが豊富なPythonで改修予定）
- 外部のメール配信サービス導入  
  迷惑メール扱いされることがあるため、また、開封数をチェックしたいため。
- データ解析基盤の構築  
  アクセスログやデータベースのマスタデータ等を[Treasure Data](https://www.treasuredata.com/jp/)に集約し、[Tableau](http://www.tableau.com/ja-jp)を用いてデータの可視化を行いたい。

以下が改修後の構成図になります。（分かりづらいですが、Treasure Dataとデータベースも繋がっています。）
<img width="560" alt="システム構成図（改修後）" src="https://qiita-image-store.s3.amazonaws.com/0/17452/a63e36a4-3814-266d-9f8a-89a0ec28b3f8.png">

## 最後に

なんかサービス紹介になってしまいました・・・。
dots.のシステムに興味がある方、その他ご要望がある方はお気軽にご連絡ください！