こんにちわちわ。Underbar.phpの記事ぶりになりました@emonkakです。
本エントリでは、以前のエントリでお伝えした勤怠管理アプリケーションのシュキーンの開発について述べたいと思います。
シュキーンとは
シュキーンはAndroidで動作する勤怠管理アプリケーションです。打刻はNFCタグをAndroid端末にかざすことで行います。勤怠データはAndroid端末からサーバーに送信されるので、ネットワーク環境さえあればどこからでも確認することがきます。
開発のスタート
社内向けに使っていたシュキーンを一般公開に向けて改修をするということで、開発はスタートしました。メンバーは私を含む2名で進み、リリース直前に増員があり現在は3名体制になりました。今回リリースされたものは以前のバージョンからほとんど1から書き直すことになりました。
ドメイン駆動設計の導入
社内で以前使われていたものは、弊社以外の様々な組織で使うことが考慮されておらず、仕様書も存在しない状況だったので一度仕様から見直してメンバー間で共有する必要がありました。
そこで、ドメイン駆動設計を導入をして全面的に設計を見直すことを決めました。ドメイン駆動設計とはソフトウェアが解決する問題領域(ドメイン)からソフトウェアを設計する手法です。
開発者は設計に際して、クラスをどう分割するか、DBにどうやってデータを格納するのかなど、実装の詳細から設計しがちです。その結果、実装都合で気付かないうちに仕様を歪めてしまったり、コードから仕様が読み取れなくなってしまうことが度々あります。
ドメイン駆動設計ではドメインの専門家と開発者が共有する「ユビキタス言語」を元にシステムの概念を表わす「ドメインモデル」を構築して実装に落とし込んでいきます。ドメインモデルは純粋にシステムの概念を表すもので、DBなどの技術基盤とは分離されています。ドメイン駆動設計においては、このドメインモデルの構築こそが一番重要になってきます。
勤怠管理におけるドメインモデル
我々は勤怠管理におけるドメインモデルとは何か、どうあるべきか議論することにしました。様々な勤怠管理システムの資料を集め、実際に勤怠を管理している事務の方にヒアリングして、少しづつドメインに関する洞察を深めていきました。こうして構築していったドメインモデルを元に実装を進めながら、実装作業中に気付いた点があればドメインモデルも修正していきました。
以下は実際のAndroidクライアントにおけるパッケージです。Applicationレイヤー、Domainレイヤー、Infrastructureレイヤー、UIレイヤーの4レイヤーで構成されています。Domainレイヤーの機能の多くはサーバー側で持っているのでクライアントはシンプルな実装になっています。
jp.co.infinteloop.shukeen
├── application (アプリケーションレイヤー:ドメインモデルを使ったアプリケーションの実装を提供)
│ ├── account (ユーザーアカウントの認証と登録に関するサービス)
│ │ ├── AccountDTO
│ │ ├── AccountRegistrationFailureException
│ │ ├── AccountRegistrationService
│ │ ├── AuthenticationService
│ │ ├── Credential
│ │ └── LoginFailureException
│ ├── employee (従業員登録に関するサービス)
│ │ ├── EmployeeDTO
│ │ ├── EmployeeRegistrationFailureException
│ │ ├── EmployeeRegistrationService
│ │ └── EmployeeResponse
│ ├── nfc (NFCタグとNFCタグの所有権に関するサービス)
│ │ ├── NfcTagOwnershipDTO
│ │ ├── NfcTagOwnershipRegistrationFailureException
│ │ ├── NfcTagOwnershipRegistrationService
│ │ └── NfcTagUtils
│ ├── organization (組織登録と組織情報の同期サービス)
│ │ ├── OrganizationDTO
│ │ ├── OrganizationRegistrationFailureException
│ │ ├── OrganizationRegistrationService
│ │ ├── OrganizationSyncFailureException
│ │ └── OrganizationSyncService
│ ├── preference (クライアントアプリケーションの設定)
│ │ └── ApplicationPreferences
│ ├── timecard (打刻に関するサービス)
│ │ ├── NotRegisterdNfcTagException
│ │ ├── TimeEntryResponse
│ │ └── TimeRecoderService
│ └── web (サーバーとの通信のためのサービス)
│ ├── ErrorResponse
│ ├── ErrorResponseUtils
│ ├── NetworkErrorException
│ └── WebApiService
├── domain (ドメインレイヤー:ドメインモデルを提供)
│ ├── employee (従業員)
│ │ ├── Employee
│ │ └── EmployeeRepository
│ ├── organization (組織)
│ │ ├── AttendancePolicy (組織ごとに設定される勤怠方針)
│ │ ├── Organization
│ │ └── OrganizationRepository
│ └── timecard (打刻情報)
│ ├── TimeEntry
│ ├── TimeEntryRepository
│ └── TimeEntryType
├── infrastructure (インフラレイヤー:永続化などの技術基盤を提供)
│ ├── database
│ ├── json
│ ├── persistence
│ └── preference
└── ui (UIレイヤー:android依存のUIを提供)
├── activity
├── adapter
├── fragment
├── runner
├── sound
└── widget
ブランチ運用
開発はgithubのプライベートリポジトリを用いて進めました。
ブランチは本番環境とリリースバイナリ用のmaster
ブランチ、社内環境とデバッグバイナリ用のdevelop
ブランチの2段構成で、社内テストが済むとdevelop
がmaster
にmergeされるといった流れになります。他、機能ごとにmaster
から切ったfeatureブランチを作ってdevelop
にpull requestするといったことをしています。
Jenkinsによる自動化
プロジェクト開始時点でJenkinsを導入して可能な限りリリースサイクルを自動化して開発を進めました。
自動化の対象はAndroidクライアント、PHPサーバープログラム、公式サイトの3つです。
- gradleを使ったapkのビルド(リリースビルド・デバッグビルド)
- PHPUnitによるユニットテストとコードカバレッジ出力
- requirejs、less、coffeescript、bowerを使ったgruntによるアセットのビルド
- Dockerによるステージング環境の構築
- FabricによるPHPサーバープログラムのデプロイ
- 公式サイトのビルド(jade, less, markdown)とデブロイ
これらはすべてJenkinsによって実行され結果はSkypeに通知されます。
Skypeの通知にはSkype Desktop APIをREST APIで叩くことができるHaskell製のskype-rest-api-serverを使用してます。
この通知はサーバーリポジトリのmaster
ブランチにpushされたので、Dockerで構築されたステージング環境に自動的にデプロイされたことを示しています。Dockerのコンテナはジョブの実行時間を短くするために、デプロイごとに破棄せずに使い回しています。これは本番環境の構成(Dockerを使っていない)と合わせる意味もあります。手動でジョブを走らせればコンテナの再生成をすることもできるようにしています。
本番環境へのデプロイ
本番環境へデプロイする際はmaster
ブランチにリリース対象のブランチ(基本的にdevelop
ブランチ)をpull requestします。レビューが済んで実際にmaster
にmergeされると、ステージング環境に自動的にデプロイされます。ステージング環境での確認が済んだら本番環境へのデプロイをJenkins上から行います。デプロイ用のジョブはParameterized Trigger Pluginでデプロイ先がパラメータ化されているので、これをlocalhostから本番サーバーのものに変えて手動で実行しています。
デプロイはfabricを使ってソースをrsyncしています。デプロイ時にはcoffee-scriptなどのアセットのビルドやcomposer install
が必要となるので、予めデプロイサーバー上でビルドを完了させて、本番サーバーではビルドを実行しないようにしています。PHP製のデプロイツールとしてRocketeerがありますが、rsyncでコピーするといったことができなかったので採用を見送りました。
まとめ
シュキーンの開発にあたってドメイン駆動設計とJenkinsによるビルドやデプロイの自動化を導入しました。
これによって、開発効率と品質の向上に繋げることができました。
リリースプロセスも可能な限り自動化して素早くサービスを改善できる体制を整えることができました。
今後のアップデートとしてWeb画面のリニューアルなど、様々なサービスの改善を予定していますのでシュキーンを今後ともよろしくお願いします。