Vue.jsとAWS Amplifyでプロジェクト譜のサービスを作った話

2021年1月21日に公式サービス[**キックプ譜**](https://kickpufu.com)がリリースされました！
それに伴い、β版は廃止しました。(2021.1.25追加)

# 概要
プロジェクト管理で苦労した体験から、プロジェクトを俯瞰的に可視化・管理するサービスをVue.jsとAWS Amplifyでつくりました。

プロジェクトの可視化は**プロジェクト譜**という記法で描いた図で表現します。

## 作ったサービス

- サービス名: **Pufu** (読み方：プフ) 
    - 現在β版として公開中
    - 対象ブラウザ: Chrome　※スマホは閲覧のみ
    - ~~ここからサービスサイトに飛べます。~~
    - アカウント登録は『アカウントの新規作成』から行えます。
    - サンプルエリアがあるのでアカウントを作らなくても試せます。
    - サイトイメージ

<img width="694" alt="project_sample.PNG" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/20217/2f8882e2-1e2a-5e85-cfb6-1b27d9bdc3be.png">

- サービスサイトと合わせて紹介用のサイトも作りました。
    - [Pufu紹介用サイト](https://dyson-yamashita.github.io/pufu-product-page/#/)

<img width="592" alt="product_page.PNG" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/20217/ce365cae-bd02-9b35-0f29-f8d6b837ff49.png">


# サービスを作った動機
プロジェクトを管理する傍ら炎上するプロジェクトと遭遇することもあり、
それはなぜかを自分なりに考えていくと、**「手を打つべき時に手を打てず、対応が後手に回る」**という至極普通な考えに至りました。
ただ、あのとき「ああしていれば。。」というのは、振り返って気付くのであって、その時点では気づきにくいのです。
どうすれば事前に必要な対応を気付けるのか考えているときに、**「予定通り進まないプロジェクトの進め方」**という本に出会い、
**プロジェクト譜**という手法を知りました。

プロジェクト譜はプロジェクトを俯瞰的に図示し、編集を重ねていきます。
なので、プロジェクト全体で見たときに落とし穴に気付きやすくなります。
プロジェクト譜の詳しい説明は割愛しますが、目標や状況変化に対する
記述のしやすさに興味が惹かれました。

プロジェクト譜に興味のある方は下記の書籍をご参照ください。

> [**予定通り進まないプロジェクトの進め方**](
https://www.amazon.co.jp/%E4%BA%88%E5%AE%9A%E9%80%9A%E3%82%8A%E9%80%B2%E3%81%BE%E3%81%AA%E3%81%84%E3%83%97%E3%83%AD%E3%82%B8%E3%82%A7%E3%82%AF%E3%83%88%E3%81%AE%E9%80%B2%E3%82%81%E6%96%B9-%E5%89%8D%E7%94%B0%E8%80%83%E6%AD%A9/dp/4883354377) (Amazon)
> 著者: 前田考歩、 後藤洋平
> 出版社: 宣伝会議

いざプロジェクト譜をパワポなどを使って実践していくと、
汎用ツールでは操作が多くなったり、欲しい機能がないなど不便さがありました。
[draw.io](https://www.draw.io/)のようにWebでつくれたらいいんじゃないかと調べても、
プロジェクト譜を対象としたサービスもなかったので、
**自分で作ってしまえ！**と作ったのがPufuです。

サービスの背景はここまでにして、Qiitaなので技術的な話を書きます。
※サービスデザイン的な話についてはnoteにかに書こうと思います。
→書きました[note](https://note.com/dyamashita/n/n1c283bead51e)
# システム構成
## フレームワーク類
フロントエンドは[Vue.js](https://jp.vuejs.org/index.html)で実装、UIは[BootstrapVue](https://bootstrap-vue.js.org/)を用いて、
バックエンドと一部フロントエンド(認証周り)で[AWS Amplify](https://aws.amazon.com/jp/amplify/)を利用しています。
Amplifyの裏側でAppSyncとDynamoDB、Cognitoを利用しています。

- Vue.js (ver. 3.11.0)
- BootstrapVue: UIフレームワーク
- AWS Amplify: mBaasサービス(認証・API類＆Webサイトホスティング)
  - AppSync: データアクセス(GraphQL)
  - DynamoDB: データ管理
  - Cognito: ユーザー管理

## 全体構成イメージ
<img width="600" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/20217/142d2928-fc8b-7954-1366-7fd893c85292.png">

# 開発環境
AWSのWebIDE Cloud9を利用しています。去年、Cloud9が日本リージョンに対応して重宝しています。

- IDE: [AWS Cloud9](https://aws.amazon.com/jp/cloud9/)
- バージョン管理: [AWS CodeCommit](https://aws.amazon.com/jp/codecommit/)
    - CodeCommitにプッシュするとAmplifyのホスティングサービスでビルドとデプロイが走ります
    - AmplifyはCodeCommit以外にもGitHubとの連携も対応しています

## デプロイのイメージ
![pufu_architecture-Page-2.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/20217/46d95f03-f3d6-2356-90fb-25650edf971b.png)

1. Amplify push: バックエンド側の環境構築  
amplifyコマンドで実行し、CloudformationでAPI(GraphQL)やAuth(Cognito)のリソースを作成します。
2. Git Push: Cloud9で編集したコードをCodeCommitにプッシュ
3. Notify: プッシュをAmplify側が検知
4. Build & Deploy: CodeCommit側のソースを取得してビルド、デプロイ

```
$ amplify push
Scanning for plugins...
Plugin scan successful

Current Environment: dev

| Category | Resource name | Operation | Provider plugin   |
| -------- | ------------- | --------- | ----------------- |
| Auth     | abcdefghijkl  | Update    | awscloudformation |
| Api      | abcdefghijkl  | Update    | awscloudformation |
```

# 実装
## データアクセス
- AppSyncのGraphQLを利用
- スキーマを定義ファイルを編集します
- amplify pushするとクエリの定義ファイルが作成
- `@auth`や`@key`などのディレクティブで認証や検索時のインデックスを制御

```javascript:schema.graphql
type Blog
  @model
  @auth(rules: [{ allow: owner }])
  @key(name: "ByOwner", fields: ["owner"], queryField: "listBlogByOwner")
{
  id: ID!
  name: String!
  owner: String!
}
```
https://aws-amplify.github.io/docs/cli-toolchain/graphql?sdk=js

## ユーザー管理
- ログイン、サインアップはamplifyのコンポーネントを利用
- サインアップ時のダイアログ表示は、signUpのイベントをキャッチしてダイアログを表示

https://aws-amplify.github.io/docs/js/vue

## プロジェクト譜エディタ
- SVGで実装
- 各アイコンをコンポーネントで実装
  - コンポーネント管理できるVue.jsのメリットを活かせた感じ
- 要素の移動など、開始イベントは子要素が発生し、終了イベントは親要素で発生する場合がある
    - 子要素で処理を実装すると終了イベントを子要素自身でキャッチできない
    - 親要素で終了イベントの処理を実装すると複雑化しすぎる
    - 要素移動などはDelegateパータンで実装 

Delegateパターン実装イメージ
![pufu_architecture-Page-3.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/20217/c0aa5fac-1f12-e930-072f-ed54ea9c4d2b.png)

## テスト環境
### ユニットテスト
- ユニットテストではjestを利用
- amplifyやstoreの挙動はモックを作成しました

```
jest.spyOn(API, 'graphql').mockReturnValue(
  Promise.resolve({
    data: {
      getProject: {id: 'todo1', name: 'todo_name_1'},
      createProject: {id: '123'}
    }
  })
)
```

### e2eテスト
- e2eテストはjest + [puppeteer](https://github.com/puppeteer/puppeteer)を利用  
nightwatchやcypressを試しましたが、cloud9上で動かさず、最終的にpupetterで構築しました。pupetterで画面キャプチャを取れるので、cloud9上でもテスト時の画面結果を確認できます。

# 感想
Amplifyは使い方が分かると実装しやすい。凝ったことをしようとすると、カスタマイズが多くなるので、開発スピードをとるなら、素のまま使う割り切りが必要。

VueとSVGの組み合わせは相性がいい。jQueryで実装するとかなり大変だったはず。Reactと相性はよさそう。(Reactは全然詳しくないですが。。。)
SVGのエディタの処理はかなり複雑化になるので、どう実装するといいのか悩みます。
今回はできてないですが、Atomic Designで初めから細分化しておくとすっきりしそう。


サービスを作るのはやはり大変。Pufuは仕事の合間、勉強しながらで作ったので6か月かかりました。最初はやる気があっても中だるみするのでモチベーションの維持は難しいですね。

VueのCompositionAPIやTypeScriptも興味あるので、余裕があるときに導入検討します。あとテストはもっと充実させたい。

あと、Pufuが本当によりよいプロジェクト運営に役に立てるのか検証していければと思います。