Spring Boot + Angular で、Webアプリケーションをつくってみた

## デモ

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/405852/f550a609-95e1-47e6-77d7-fbf60b15451d.png)

https://y-chip.github.io/andres/#/

GitHub PagesとHerokuでホスティングしています。

Herokuは無料プランのため、初回の画面表示に時間がかかります。

## ソースコード

https://github.com/y-chip/andres

## アーキテクチャ

![architecture.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/405852/2ab509bc-0a49-596c-afa3-b88c769125c2.png)

## アーキテクチャの構成要素

### front

シングルページアプリケーションのクライアントです。

| 構成要素     | 説明                                                                                                          |
|------------|---------------------------------------------------------------------------------------------------------------|
| TypeScript | Microsoftが開発した、プログラミング言語です。<br>JavaScriptに「静的型付け」と「クラスベースのオブジェクト指向」をもたらします。 |
| Angular    | Googleが開発した、フレームワークです。<br>フルスタックであり、フロントエンドに必要な機能が揃っています。                      |
| PrimeNG    | トルコのPrimeTek Informaticsが開発した、UIコンポーネントライブラリです。<br>高機能なUIコンポーネントが揃っています。         |

### back

WebAPIを提供するサーバーです。

| 構成要素                  | 説明                                                                                                      |
|-------------------------|-----------------------------------------------------------------------------------------------------------|
| Java                    | 「静的型付け」で「クラスベースのオブジェクト指向」のプログラミング言語です。                                            |
| Spring&#160;Boot        | 支持率の高い、フレームワークです。                                                                               |
| H2&#160;Database        | 軽量なデータベースです。<br>手軽に利用するために、サーバーに組み込んでいます。                                          |
| MyBatis                 | データベースを操作する、フレームワークです。<br>後述する、MyBatis Generatorで自動生成しています。                       |
| springdoc&#8209;openapi | Spring Bootをもとに、OpenAPIを自動生成するライブラリです。<br>後述する、OpenAPI Generatorのために、自動生成しています。 |

### back-dao-generator

データベースを操作するための、プログラムを自動生成します。

| 構成要素                 | 説明                                     |
|------------------------|------------------------------------------|
| MyBatis&#160;Generator | MyBatisのプログラムを、自動生成するツールです。 |

### front-api-generator

WebAPIを呼び出すための、プログラムを自動生成します。

| 構成要素                 | 説明                                                                                                       |
|------------------------|------------------------------------------------------------------------------------------------------------|
| OpenAPI&#160;Generator | WebAPIを呼び出すプログラムを、自動生成するツールです。<br>さまざまなプログラム言語／フレームワークにむけて、自動生成できます。 |

## 特徴

### プログラムの自動生成

#### データベースを操作するためのプログラム

つぎのプログラムを自動生成しています。

- [SampleMapper.xml](https://github.com/y-chip/andres/blob/master/back/src/gen/resources/mapper/SampleMapper.xml)
- [SampleMapper.java](https://github.com/y-chip/andres/blob/master/back/src/gen/java/com/yamamoto/yuta/andres/infrastructure/repository/mapper/SampleMapper.java)
- [Sample.java](https://github.com/y-chip/andres/blob/master/back/src/gen/java/com/yamamoto/yuta/andres/infrastructure/repository/entity/Sample.java)
- [SampleExample.java](https://github.com/y-chip/andres/blob/master/back/src/gen/java/com/yamamoto/yuta/andres/infrastructure/repository/entity/SampleExample.java)

これらを使って、つぎのようにデータベースを操作できます。

```java:SampleRepository.java
  // プライマリキーで、SELECTする。
  public Optional<Sample> selectByPrimaryKey(Integer id) {
    return Optional.ofNullable(sampleMapper.selectByPrimaryKey(id));
  }

  // プライマリキーで、UPDATEする。
  public void updateByPrimaryKey(Sample sample) {
    sampleMapper.updateByPrimaryKey(sample);
  }

  // FIELD_TEXTというカラムで、SELECTする。
  public List<Sample> selectByFieldText(String fieldText) {

    var example = new SampleExample();

    example.createCriteria().andFieldTextEqualTo(fieldText);

    return sampleMapper.selectByExample(example);
  }
```

たったの3行で、SELECTできます！

SQLを書いたり、DTOを作ったり、、の作業から解放されます！

:::note info
Javaでデータベースを操作するなら、
ほかにもJPAなどがありますが、SIerにはMyBatisが最適かな？と思っています。
:::

#### WebAPIを呼び出すためのプログラム

つぎのプログラムを自動生成してます。

- [sampleFormController.service.ts](https://github.com/y-chip/andres/blob/master/front/src/gen/api/sampleFormController.service.ts)
- [sampleForm.ts](https://github.com/y-chip/andres/blob/master/front/src/gen/model/sampleForm.ts)

これらを使って、つぎのようにWebAPIを呼び出せます。

```typescript:sample-form.component.ts
        this.http([
            this.sampleFormControllerService.get(this.id).pipe(
                tap((sampleForm) => {
                    this.sampleForm = sampleForm;
                })
            ),
        ]);
```

たったの5行で、WebAPIを呼び出せます！

通信処理を書いたり、DTOを作ったり、、の作業から解放されます！

「DTOの項目名を間違えていた」とか、よくあるミスも無くなります！

:::note info
OpenAPI Generatorは、さまざまな言語に対応しています。
jQuery、C#、Rust、、夢が膨らみますね！
:::

### 共通処理の工夫

5行でWebAPIを呼び出せるようになりましたが、、それだけでは終わりません！

- 通信中はオーバーレイを表示して、二重送信を防止する。
- 入力エラーがあれば、メッセージを表示して、該当箇所までスクロールする。
- 予期せぬエラーがあれば、メッセージを表示して、ログを出力する。

といったことは、共通処理にします。

```typescript:page.component.ts
    protected http<T>(task: Observable<any>[]): void {
        // 表示中のメッセージとエラーをクリアする。
        this.messageService.clear();
        this.clearValidationError();
        // オーバーレイを表示する。
        this.progressSpinnerOverlayService.show();
        forkJoin(task)
            // 最後にオーバレイを非表示にする。
            .pipe(finalize(() => this.progressSpinnerOverlayService.hide()))
            .subscribe({
                next: (response) => console.log(response),
                // エラーなら、handleErrorを呼び出す。
                error: (error) => this.handleError(error),
            });
    }

    private handleError(response: any): void {
        if (response.status === 400 && this.isValidationError(response.error)) {
            // 入力エラーなら、メッセージを表示して、該当箇所を赤くして、該当箇所までスクロールする。
            this.toastService.addError('入力エラーがあります。');
            this.showValidationError(response.error);
        } else {
            // 予期せぬエラーなら、メッセージを表示して、ログを出力する。
            this.messageService.addError(
                '通信エラー',
                '予期せぬエラーが発生しました。管理者に問い合わせてください。'
            );
            console.error(response);
        }
    }
```

こうすることで、簡単に登録処理が実現できるようになります！

共通処理をつくった甲斐があったはず！

```typescript:sample-form.component.ts
    onSubmit(): void {
        // WebAPIを呼び出す。
        this.http([
            this.sampleFormControllerService
                .put(this.id, this.sampleForm)
                .pipe(
                    // 成功したときは、メッセージを表示する。
                    tap(() => this.toastService.addSuccess('登録しました。'))
                ),
        ]);
        // 失敗したときは、ほっといたらイイカンジになる。
    }
```

:::note info
プログラムが増えると、バグも増えるし、読むのも大変だし、テストするのも大変だから、減らしたい！
:::

### コンポーネント指向の設計

格好つけてますが、すこし工夫しているだけです。

ふつうに入力欄をつくると、つぎのような実装になります。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/405852/352308d1-0adf-fab0-5d7c-9d4b63e87ba9.png)


```html
<!-- デザインを整えるためのdiv -->
<div class="field" #field>
    <!-- label -->
    <label htmlFor="text">文字列</label>
    <!-- input -->
    <input
        id="text"
        [(ngModel)]="sampleForm.fieldText"
        pInputText
        type="text"
        [maxlength]="50"
        #ngModel="ngModel"
    />
    <!-- エラーメッセージを表示するためのsmall -->
    <small
        *ngIf="ngModel.invalid"
        class="p-error"
    >
        {{ invalidMessage }}
    </small>
</div>
```

このままだと、つぎのような問題があります。

- 記述量が多い
- 変更に弱い（デザインを変更することになったらどうする！）
- わかりにくい（divとかngModelとかpInputTextってなんだ！）
- まちがえやすい（「p-error」が「p-errOr」になってたらどうする！）

:::note alert
なんとかせねば！
:::

これらを解決するために、つぎのようなコンポーネントをつくります。

- [field-text.component.html](https://github.com/y-chip/andres/blob/master/front/src/app/component/field/text/field-text.component.html)
- [field-text.component.ts](https://github.com/y-chip/andres/blob/master/front/src/app/component/field/text/field-text.component.ts)

このコンポーネントをつかうと、6行で入力欄ができます。

```html:sample-form.component.html
<and-field-text
    name="text"
    label="文字列"
    [(value)]="sampleForm.fieldText"
    [maxlength]="50"
></and-field-text>
```

画面全体をみても、いい感じです！

[sample-form.component.html](https://github.com/y-chip/andres/blob/master/front/src/app/page/sample-form/sample-form.component.html)

:::note info
いいかんじになった！
:::

### フォーマッターの導入

これまではIDEに内蔵されているフォーマッターをつかっていました。（Eclipseとか、IntelliJ IDEAとか、Visual Studio Codeとか）

しかし、つぎのような問題がありました。

- フォーマッターの実行を忘れる人がいる。（人間なので仕方がない！）
- 好きなIDEをつかえない。（仕事道具は選ばせて！）
- IDEに依存してしまう。（OSのバージョンアップで動かなくなったらどうする！）
- GitHookやCIと連携できない。
  - フォーマッターの実行を忘れていたら、コミットしたときに通知したり、自動でフォーマットしてあげたい！（GitHook）
  - フォーマッターの実行を忘れていたら、プッシュしたときに通知したい！（CI）

:::note alert
これまた、なんとかせねば！
:::

これらを解決するために、フォーマッターを導入しました。

バックエンドにはgoogle-java-formatを導入しました。

フロントエンドにはPrettierを導入しました。

:::note info
これまた、いいかんじになった！
:::

### ビルドツールでの管理

ビルドツールで管理することで、ワンコマンドで、つぎの操作ができるようになってます。

- ビルド
- デプロイ
- フォーマッターの適用
- プログラムの自動生成

環境構築に時間がかかったり、
設定ファイルの〇〇を書き換えないと、本番環境に繋がるトラップがあったり、
ビルドするまえに、〇〇バッチを動かさないとダメというトラップがあったり、
というのは、なくしていきたいなとおもっています！

:::note info
なにかを覚えるのって大変ですよね。
Wikiの手順どおりに〇〇してください！じゃなくて、説明がなくても分かるようにできたらなぁとおもいます。
:::