LoginSignup
7
1

More than 1 year has passed since last update.

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

Last updated at Posted at 2022-02-09

デモ

image.png

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

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

ソースコード

アーキテクチャ

architecture.png

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

front

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

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

back

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

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

back-dao-generator

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

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

front-api-generator

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

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

特徴

プログラムの自動生成

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

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

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

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を作ったり、、の作業から解放されます!

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

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

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

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

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

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

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

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

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

共通処理の工夫

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

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

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

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);
        }
    }

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

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

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

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

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

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

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

image.png

<!-- デザインを整えるための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」になってたらどうする!)

なんとかせねば!

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

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

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

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

sample-form.component.html

いいかんじになった!

フォーマッターの導入

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

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

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

これまた、なんとかせねば!

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

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

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

これまた、いいかんじになった!

ビルドツールでの管理

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

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

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

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

7
1
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
7
1