Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

ガイドのVue.js編にエラーハンドラーの設定方法を追加する #1362

Open
1 task
KentaHizume opened this issue Oct 10, 2024 · 7 comments
Open
1 task
Assignees
Labels
target: ガイド/AP開発手順 ドキュメントのガイド/アプリケーション開発手順に関係がある
Milestone

Comments

@KentaHizume
Copy link
Contributor

KentaHizume commented Oct 10, 2024

概要

「プロジェクトの共通設定」と「開発に使用するパッケージ」の間に、
「エラーハンドラーの設定」ページを追加し、
システム例外を補足するグローバルエラーハンドラーと、
業務例外を補足するカスタムエラーハンドラーについて実装、設定方法を記載する。
Image

詳細 / 機能詳細(オプション)

ガイドには、どのプロジェクトでも実施すべき初期構築作業を記載したい。
たとえばMarisのバックエンド側では、
下記のようにエラーハンドラーの実装・設定方法を記載している。

https://maris.alesinfiny.org/guidebooks/how-to-develop/dotnet/configure-asp-net-core-web-api-project/#exception-handling-configuration

完了条件

  • ガイド>Vue.js編にエラーハンドラーの設定方法が追加されていること
@KentaHizume KentaHizume added the target: ガイド/AP開発手順 ドキュメントのガイド/アプリケーション開発手順に関係がある label Oct 10, 2024
@KentaHizume KentaHizume added this to the v0.9.1 milestone Oct 10, 2024
@KentaHizume KentaHizume self-assigned this Oct 10, 2024
@tsuna-can-se tsuna-can-se modified the milestones: v0.9.2, v1.0 Oct 23, 2024
@KentaHizume
Copy link
Contributor Author

KentaHizume commented Oct 24, 2024

リリース、サブissue進捗

・ Dresscaのカスタムエラーハンドラーの実装の修正
はv0.9.1に含める。

・ドキュメントの追加、
・AzureADB2Cへの組み込み
はv1.0以降で対応する。

カスタムエラーハンドラーについて見直すべき点が多数出てきたため、
カスタムエラーハンドラーのガイド追加、ADB2Cへの組み込みは後に回し、
先にグローバルエラーハンドラー分のガイド追加の組み込みを行う。

@KentaHizume
Copy link
Contributor Author

KentaHizume commented Oct 30, 2024

コンポーネント以外ではプラグインをinjectできない問題

サービスクラスなど、
Vueのコンポーネント以外でuseCustomErrorHandlerを実行した場合、
下記のエラーで落ちてしまう。
現象としてはrouterと同じで、
use経由ではなく、直接importすることで想定通りの動作になる。

しかし、この場合、カスタムエラーハンドラーを使用する際に、
・Vueコンポーネントの場合はprovide/inject(useCustomErrorHandler)
・Vueコンポーネント以外の場合は直接import
と異なる使い方をしなければならないので、ややこしくなる。

よって、カスタムエラーハンドラーは必ずVueのコンポーネントから呼ぶというように定めたほうがよい可能性がある。

そうなると、
・service(モデル)で業務例外をthrowするのはアリだが、例外のcatchは責務ではない
・vueコンポーネントのscript部分(ビューモデル)で、カスタムエラーハンドラーを使って業務例外をcatchする
という作りになる。

そのため、このような構成が妥当なのかどうかを検討すべき。

処理フロー図とは合致している。

https://maris.alesinfiny.org/app-architecture/client-side-rendering/global-function/exception-handling/#api-connection-error-flow
Image

業務例外をハンドリングしてユーザーに通知するのはプレゼンテーションロジックのはずなので、
これも合致しているはず。

https://maris.alesinfiny.org/app-architecture/client-side-rendering/frontend-architecture/#linkage-with-model-component
Vue.js ではバックエンドのアプリケーションとの連携をモデルが行います。そのため、ユーザーが行う画面コンポーネントからの処理や入力情報をモデルに連携する必要があります。この連携ではビューモデルのプレゼンテーションロジックから、後述するモデルコンポーネントの Service や Store の Action を呼び出すことで、データの取得・更新をします。

エラー

[Vue warn]: inject() can only be used inside setup() or functional components.

authentication-service.tsの実装例

export const authenticationService = {
  async signInAzureADB2C() {
    const authenticationStore = useAuthenticationStore();
    const customErrorHandler = useCustomErrorHandler();
    try {
      const response = await msalInstance.loginPopup(loginRequest);
      msalInstance.setActiveAccount(response.account);
      authenticationStore.updateAuthenticated(true);
      return true;
    } catch (error) {
      customErrorHandler.handle(error, () => {
        authenticationStore.updateAuthenticated(false);
      });
      return false;
    }
  },

@KentaHizume
Copy link
Contributor Author

KentaHizume commented Oct 31, 2024

Serviceクラスから直でimportした場合、
やはりrouter経由で循環参照が検出されてLintエラーになる。

エラー

  3:1  error  Dependency cycle via @/shared/error-handler/custom-error-handler:2=>@/router:4=>@/router/basket/basket:4  import/no-cycle

エラーになるコード

import { createCustomErrorHandler } from '@/shared/error-handler/custom-error-handler';

export async function updateItemInBasket(
  catalogItemId: number,
  newQuantity: number,
) {
  const basketStore = useBasketStore();
  const customErrorHandler = createCustomErrorHandler();
  // 直前に追加された商品の表示を更新するためIDを削除
  basketStore.deleteAddedItemId();

  try {
    await basketStore.update(catalogItemId, newQuantity);
  } catch (error) {
    customErrorHandler.handle(error, () => alert(error));
  } finally {
    await basketStore.fetch();
  }
}

@KentaHizume
Copy link
Contributor Author

KentaHizume commented Oct 31, 2024

dependency-cruiserでチェックすると、
InjectionKey経由の依存関係も警告として検出されてしまう。

InjectionKeyがインターフェースになって、
各コンポーネントからエラーハンドラーへの直接の依存関係がなくなるので問題ないはずと思っていたが、
そういうわけではない?

  warn no-circular: src/router/catalog/catalog.ts →
      src/views/catalog/CatalogView.vue →
      src/shared/error-handler/use-custom-error-handler.ts →
      src/shared/injection-symbols.ts →
      src/shared/error-handler/custom-error-handler.ts →
      src/router/index.ts →
      src/router/catalog/catalog.ts

@KentaHizume
Copy link
Contributor Author

https://qiita.com/yoyoyoheyheyhey/items/df886c6baed88252654c
やはりカスタムエラーハンドラーからrouterをimportしているのが問題な気がするので、
カスタムエラーハンドラーにrouterをDIするようにしたほうがよいのかもしれない。
この記事だとstoreもDIしているが、piniaはコンポーネント外で下記のような処理をしてもうまく動いているように見えるのでその理由が不明。そのためpiniaと同じような実装にすることができれば解決の可能性がある。

const authenticationStore = useAuthenticationStore();

@KentaHizume
Copy link
Contributor Author

要検証・検討ポイント

暫定案

main.tsに既定のカスタムエラーハンドラーを実装してしまう

main.tsにカスタムエラーハンドラーの実装を書いてしまい、
createCustomErrorHandlerにそれを渡す

routerを使用する

同じmain.ts中に入れてしまえば、routerをimportすると警告が出る問題は解決するはず。
app.useでrouterを入れているのでこの後ろであれば動く想定。

showToastを使用する

showToastに関してはapp.useしていないのでそのままだとうまく動かないかもしれない。

カスタムエラーハンドラーからvueアプリケーションへの依存を取り除く

カスタムエラーハンドラーがVueアプリケーションの機能(vue-routerやToastコンポーネント)に依存していることが問題。レイヤーの関係が逆転しているという理解。

イメージとしては、

  • モチベーション
    下記のimportによりVueアプリへ依存関係ができてしまうのでやめたい
import { showToast } from '@/services/notification/notificationService';
import { useRoutingStore } from '@/stores/routing/routing';
import { router } from '@/router';
  • カスタムエラーハンドラー
    エラーハンドラーがやるべきことは、ログ出力と(抽象化された)ユーザーへのアクションで、
    ハンドラーでは
    ユーザーへアクションすべきイベントの発生をキューに詰める。
     if (error instanceof UnauthorizedError) {
            // ログ出力

            // ユーザーへの通知
            // イベントを発生させてQueueに詰める

          }
  • ユーザーへのアクションの実装を持つVueコンポーネント
    そうなると、イベントをQueueから取り出して必要な処理をしてくれるコンポーネントが欲しい
            // ユーザーへのアクションの実装1
    // 画面上に通知 
            showToast('ログインしてください。');

            // ユーザーへのアクションの実装2
    // リダイレクト
            router.push()

@KentaHizume
Copy link
Contributor Author

KentaHizume commented Dec 24, 2024

・カスタムイベントを格納するキュー構造のストアを作成する
・通知用コンポーネントを作成し、コンポーネント内ではストアの中身の変化をwatchする
で実現できるはず?

Vuexでキューを実装する
https://serversideup.net/building-a-queue-with-vue-3-and-vuex-4/

TypeScriptでCustomEventを起動して受信したいとき
https://qiita.com/SallyCinnamon/items/3810c44201c7bbc9d785

@tsuna-can-se tsuna-can-se modified the milestones: v1.1.0, v1.2.0 Jan 16, 2025
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
target: ガイド/AP開発手順 ドキュメントのガイド/アプリケーション開発手順に関係がある
Projects
None yet
Development

No branches or pull requests

2 participants