Skip to content

アプリケーション開発手順(.NET)のドキュメントを作成する #8

@tsuna-can-se

Description

@tsuna-can-se

目次案

.NET編

事前準備

  • 開発環境構築を解説したところへのリンク
  • ローカルPC内でアプリケーションの実行が完結するように環境を作る
    • RDBMSはVSに付属するSQL Server LocalDBを利用する
    • WebサーバーはKestrelまたはIIS Expressを利用する

ソシューション構造の作成

  • リポジトリとディレクトリ構造の作成

    • Gitリポジトリ構築編にリンク
  • ここでは単一のソリューション内の構造を示す

  • ソリューション構造へのリンク(おおまかなプロジェクトの構成は「Maris OSS 版のアプリケーションアーキテクチャ概要編」に以下のように書かれている。詳しい構造化については書かれていないので、ここで示す)

    Maris OSS 版では、 1 システム 1 ソリューションを基本として推奨します。 ただし、複数システム(複数ソリューション)で共用する共通基盤を作成する場合は、必要に応じてソリューション分割を検討してください。

    概要編の記載もあわせて見直す。

    • Web アプリケーションやコンソールアプリケーション(バッチ)など、 1 つのサブシステムは通常複数のアプリケーションで構成されます。 Maris OSS 版では、 1 サブシステム 1 ソリューションを基本構成として推奨します。
  • ソリューションフォルダーを使って物理的なファイル配置とソリューション内の構造を一致させる

  • src(プロダクションコードを配置)、tests(テストで使用するコードを配置)ソリューションフォルダーでまず大きく分ける(多くの場合コード分析ルールの厳しさが異なるので、分けておいた方がよい。editorconfigをtests直下に置くと、テスト用のコード分析ルールを作成できる)

  • srcフォルダー内は原則フラットに並べるが、プロジェクト数が多くなる場合は適宜ソリューションフォルダーを使って整理する(srcフォルダー内のプロジェクトが30を超える場合はソリューションフォルダーを用いて整理することを推奨)。

    • 特にコンソールアプリケーションを大量に作る場合、肥大化しやすい
    • ディレクトリ名はプロジェクト名と一致させる。
    • プロジェクト名は、プロジェクトのルート名前空間と原則一致させる。
  • testsフォルダー内はテストの目的(単体機能確認、結合機能確認、性能、E2Eなど)に応じてテストプロジェクトを分割する。

    • サブシステムの規模が大きい場合は、業務単位で単体機能確認のテストプロジェクトを分割することも検討する(ソリューションフィルターを使う場合は、ソリューションフィルターで分割する単位を意識してテストプロジェクトも分割する)
    • ディレクトリ名はプロジェクト名と一致させる。

プロジェクトの作成

  • .NET テンプレートを用いたブランクプロジェクトとディレクトリの作成(プロジェクト別の選択)
    • クラスライブラリプロジェクト
    • コンソールアプリ
    • Web APIのみ
    • フロントエンドJSを含むWeb API(ASP.NET Core with Vite)
    • 単体テストプロジェクト(xUnit)
  • プロジェクト名の命名ルール
    • 「<ソリューション名>.<業務名>.<レイヤー名>」を原則にする
    • プロジェクト規模が小さく、業務分割を行う必要がない場合は業務名をつけなくても良い
    • Infrastructure層は、実装する技術をプロジェクト名の末尾や接頭辞として付ける(例:Dressca.EFInfrastucture(EF Coreを使ったRepositoryの実装)、Dressca.Store.Assets.StaticFiles(静的ファイルを用いたAssetsストアの実装))
    • マイクロサービスアーキテクチャを採用しない場合、Webアプリケーションプロジェクトは1つだけでよい。
    • Web APIを作る場合、Web APIのインターフェースに使用するDTOだけを集めたプロジェクトを作成しておく(Blazorなど、クライアント側とDTOを共有するケースがあるため、手間にならないうちにプロジェクト構造として定義しておく)

プロジェクトの共通設定

  • 全プロジェクト共通で設定したほうがいい設定を解説(解説は短めにして、詳細情報はMSサイトへリンクする)

プロジェクトファイルの設定

  • csproj ファイルを開いて直接編集する
  • ImplicitUsings、Nullable、GenerateDocumentationFileの設定を以下のようにする
  • GenerateDocumentationFile はテストプロジェクトでは不要
  <PropertyGroup>
    <TargetFramework>net6.0</TargetFramework>
    <ImplicitUsings>enable</ImplicitUsings>
    <Nullable>enable</Nullable>
    <GenerateDocumentationFile>true</GenerateDocumentationFile>
  </PropertyGroup>

静的コード分析用パッケージと設定ファイルの導入

editorconfig
  • editorconfigとは何か
  • 本コード用/単体テスト用/自動生成コード用 の3種類用意する
  • 調整方法
StyleCop Analyzers
  • StyleCop Analyzersとは何か
  • stylecop.jsonの配置と各プロジェクトへのリンク配置(原則stylecop.jsonはソリューション内に1つとし、ルールの適用設定を各editorconfigでやる)
  • 設定の調整、変更方法(設定値の詳細はStyleCopのgithubにリンク)

メッセージリソースファイルの作成

  • ResourcesフォルダにMessages.resxファイルを作成
  • internalでコード生成
  • プロジェクト内で使用するメッセージをここに配置する
  • internalなのでプロジェクトの外からは見えず、プロジェクトごとに独立したメッセージを定義する
  • プロジェクト間で共通するメッセージが出てきた場合も、原則はバラバラに定義する。そのメッセージを使う処理を共通機能として切り出しておくことも検討する。

プロジェクト構造の作成

  • 業務で分割→レイヤーで分割、の基本を忘れずに
    • 慣例によってそうならないWebのControllersとかもある
    • レイヤーで分割する必要がないなら分割しなくてもよい(Application Coreとかだと業務で分割した後レイヤーで分割していない、業務単位のディレクトリに全部入れてある)
  • アプリケーションアーキテクチャのガイドを参照

ASP.NET Core Web API プロジェクトの構成

Open API 仕様書の出力設定

UIとOpen API仕様書を生成できるようにする。

  1. NSwag.AspNetCoreとNSwag.MSBuild、Microsoft.AspNetCore.Mvc.NewtonsoftJsonのNuGetパッケージを追加

  2. nswag.jsonをプロジェクトルートに追加。
    コードの生成は行わないので、ドキュメント生成の設定のみ実施。詳細は以下に飛ばす。
    https://github.com/RicoSuter/NSwag/wiki/NSwag-Configuration-Document

    以下nswag.jsonの設定例

    {
      "runtime": "Net60",
      "defaultVariables": null,
      "documentGenerator": {
        "aspNetCoreToOpenApi": {
          "project": "AaaSubSystem.Web.csproj", // プロジェクト名を指定
          "msBuildProjectExtensionsPath": null,
          "configuration": "$(Configuration)",
          "runtime": null,
          "targetFramework": null,
          "noBuild": true,
          "msBuildOutputPath": null,
          "verbose": true,
          "workingDirectory": null,
          "requireParametersWithoutDefault": true,
          "apiGroupNames": null,
          "defaultPropertyNameHandling": "Default",
          "defaultReferenceTypeNullHandling": "Null",
          "defaultDictionaryValueReferenceTypeNullHandling": "NotNull",
          "defaultResponseReferenceTypeNullHandling": "NotNull",
          "generateOriginalParameterNames": true,
          "defaultEnumHandling": "Integer",
          "flattenInheritanceHierarchy": false,
          "generateKnownTypes": true,
          "generateEnumMappingDescription": false,
          "generateXmlObjects": false,
          "generateAbstractProperties": false,
          "generateAbstractSchemas": true,
          "ignoreObsoleteProperties": false,
          "allowReferencesWithProperties": false,
          "useXmlDocumentation": true,
          "resolveExternalXmlDocumentation": true,
          "excludedTypeNames": [],
          "serviceHost": "localhost:5001", // vite のホスト指定を推奨
          "serviceBasePath": null,
          "serviceSchemes": [
            "https"
          ],
          "infoTitle": "Xxx Web API",
          "infoDescription": "Xxx の Web API 仕様",
          "infoVersion": "1.0.0",
          "documentTemplate": null,
          "documentProcessorTypes": [],
          "operationProcessorTypes": [],
          "typeNameGeneratorType": null,
          "schemaNameGeneratorType": null,
          "contractResolverType": null,
          "serializerSettingsType": null,
          "useDocumentProvider": false,
          "documentName": "v1",
          "aspNetCoreEnvironment": "Development",
          "createWebHostBuilderMethod": null,
          "startupType": null,
          "allowNullableBodyParameters": true,
          "useHttpAttributeNameAsOperationId": false,
          "output": "XXXXXXXXX-api.json", // 出力するファイル名を指定
          "outputType": "OpenApi3",
          "newLineBehavior": "Auto",
          "assemblyPaths": [],
          "assemblyConfig": null,
          "referencePaths": [],
          "useNuGetCache": false
        }
      },
      "codeGenerators": {}
    }
  3. Open API 仕様がビルド時に生成されるように設定
    詳細は以下を参照。
    https://github.com/RicoSuter/NSwag/wiki/NSwag.MSBuild

    .NET6の場合は以下のように設定するなる。

      <Target Name="NSwag" AfterTargets="PostBuildEvent" Condition="'$(Configuration)' == 'Debug'">
        <Exec WorkingDirectory="$(ProjectDir)" EnvironmentVariables="ASPNETCORE_ENVIRONMENT=Development" Command="$(NSwagExe_Net60) run nswag.json /variables:Configuration=$(Configuration)" />
      </Target>
    
      <PropertyGroup>
        <RunPostBuildEvent>OnBuildSuccess</RunPostBuildEvent>
      </PropertyGroup>
  4. swaggerの設定を変更(Program.cs)
    https://github.com/RicoSuter/NSwag/wiki/AspNetCore-Middleware

    ドキュメント出力の設定例

    builder.Services.AddSwaggerDocument(config =>
    {
        config.PostProcess = document =>
        {
            document.Info.Version = "1.0.0";
            document.Info.Title = "Xxx Web API";
            document.Info.Description = "Xxx の Web API 仕様";
        };
    });

    swaggerのUIを開発環境でのみ有効化(Program.cs)

    if (app.Environment.IsDevelopment())
    {
        app.UseOpenApi();
        app.UseSwaggerUi3();
    }

例外ハンドリングの設定

  1. Bad Request返却時のログ出力設定(これ以外ないかも)

    builder.Services
        .AddControllers()
        .ConfigureApiBehaviorOptions(options =>
        {
            // Bad Request となった場合の処理。
            var builtInFactory = options.InvalidModelStateResponseFactory;
            options.InvalidModelStateResponseFactory = context =>
            {
                // エラーの原因をログに出力。
                var logger = context.HttpContext.RequestServices.GetRequiredService<ILogger<Program>>();
                logger.LogInformation("HTTP 要求に誤りがあります。詳細情報: {0} 。", JsonSerializer.Serialize(context.ModelState));
    
                // ASP.NET Core の既定の実装を使ってレスポンスを返却。
                return builtInFactory(context);
            };
        });

ASP.NET Core with Vite プロジェクトの構成

ASP.NET Core Web API プロジェクトの設定を行ったうえで以下を実施する。

  1. SpaRoot プロパティにフロントエンドアプリケーションのルートディレクトリを設定。
    テンプレートの構成だとプロジェクト内にフロントエンドアプリケーションを配置することになるが、Maris OSS版としてはソリューション外のディレクトリ(以下の例だとfrontendディレクトリ)に配置することを推奨。

    <SpaRoot>..\..\..\frontend\</SpaRoot>
  2. ClientAppディレクトリを削除または移動(上記SpaRootにもともと設定されているディレクトリを消す、または設定したfrontendディレクトリに移動する)。
    frontendディレクトリ内に配置するアプリケーションの構築についてはVue.js編を参照。

  3. プロジェクトファイル内の以下の要素を削除。
    プロジェクト内からSpaRootに設定されているディレクトリがすべて消えるので、以下の設定は不要になる。

    <ItemGroup>
    	<!-- Don't publish the SPA source files, but do show them in the project files list -->
    	<Content Remove="$(SpaRoot)**" />
    	<None Remove="$(SpaRoot)**" />
    	<None Include="$(SpaRoot)**" Exclude="$(SpaRoot)node_modules\**" />
    </ItemGroup>
  4. SpaProxyLaunchCommand プロパティにフロンエンドアプリケーションの起動コマンドを設定。
    フロントエンドアプリケーションの起動コマンドは、構築したアプリケーションによって異なるので注意。

    <SpaProxyLaunchCommand>npm run dev</SpaProxyLaunchCommand>

xUnit テストプロジェクトの設定(いらないかも)

  1. Moq NuGetパッケージを追加

  2. ImplicitUsing に XunitとMoqを追加

      <ItemGroup>
        <Using Include="Xunit" />
        <Using Include="Moq" />
      </ItemGroup>

Metadata

Metadata

Assignees

Labels

No labels
No labels

Type

No type

Projects

No projects

Milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions