アバウトア.NET 5 ( RC 1 ) Graphql 3によるWebアプリケーション


昨日(09/14)は、最初のバージョン(RC 1)からリリースされました.ASP . NET 5、ASP . NET.net,ef core,cは9 . 9である.
まだリリースについて話しているので、先週はGraphSQLからバージョン3を入手できます.ネット
このプロジェクトでは、Graphql 3 Web APIを消費するシンプルなRazor Web MVCコアの実装とdockerizationを例示します.NET 5多層プロジェクト、開発のベストプラクティスを考慮して、固体とドライのように、六角形のアーキテクチャでドメイン駆動の概念を適用します.

アントニヤコカリ / dotnet 6。グラフ。Webアプリケーション


このプロジェクトは、完全なGraphel 4 Web APIを消費する単純なかみそりウェブMVCコアの実装とdockerizationを例示します。オニオンアーキテクチャでドメイン駆動の概念を適用し、固体と乾燥のような開発ベストプラクティスを考慮して、NET 6多層プロジェクト。



ハイライト


通知(パターン/コンテキスト)


例外を扱うには、 NotificationContext それはすべての層が要求を通してビジネス通知を加えるのを許容します.そして、他の側によって、ドメイン通知を受け取るサポートで、FluentValidationResult .
protected bool OnValidate<TEntity>(TEntity entity, AbstractValidator<TEntity> validator)
{
    ValidationResult = validator.Validate(entity);
    return IsValid;
}

protected void AddError(string errorMessage, ValidationResult validationResult = default)
{
    ValidationResult.Errors.Add(new ValidationFailure(default, errorMessage));
    validationResult?.Errors.ToList().ForEach(failure => ValidationResult.Errors.Add(failure));
}
GraphQLに通知コンテキストはExecutionErrors それはresult 個人化による実行から Executer :
var result = await base.ExecuteAsync(operationName, query, variables, context, cancellationToken);
var notificationContext = _serviceProvider.GetRequiredService<INotificationContext>();

if (notificationContext.HasNotifications)
{
    result.Errors = notificationContext.ExecutionErrors;
    result.Data = default;
}

Singletonスキーマによるスコープ依存性の解決


同じ個人化された Executer サービスプロバイダーを定義するにはresolvers on fields :
var options = base.GetOptions(operationName, query, variables, context, cancellationToken);
options.RequestServices = _serviceProvider;

抽象化


抽象的な設計では、主な振舞いのためのリソースを提供し、ドライコンセプトを適用することに加え、結合を減らすことができます.
...Domain.Abstractions
public abstract class Entity<TId>
    where TId : struct
public abstract class Builder<TBuilder, TEntity, TId> : IBuilder<TEntity, TId>
    where TBuilder : Builder<TBuilder, TEntity, TId>
    where TEntity : Entity<TId>
    where TId : struct
...Repositories.Abstractions
public abstract class Repository<TEntity, TId> : IRepository<TEntity, TId>
    where TEntity : Entity<TId>
    where TId : struct
{
    private readonly DbSet<TEntity> _dbSet;

    protected Repository(DbContext dbDbContext)
    {
        _dbSet = dbDbContext.Set<TEntity>();
    }
...Services.Abstractions
public abstract class Service<TEntity, TModel, TId> : IService<TEntity, TModel, TId>
    where TEntity : Entity<TId>
    where TModel : Model<TId>
    where TId : struct
{
    protected readonly IMapper Mapper;
    protected readonly INotificationContext NotificationContext;
    protected readonly IRepository<TEntity, TId> Repository;
    protected readonly IUnitOfWork UnitOfWork;

    protected Service(
        IUnitOfWork unitOfWork,
        IRepository<TEntity, TId> repository,
        IMapper mapper,
        INotificationContext notificationContext)
    {
        UnitOfWork = unitOfWork;
        Repository = repository;
        Mapper = mapper;
        NotificationContext = notificationContext;
    }
public abstract class MessageService<TMessage, TModel, TId> : IMessageService<TMessage, TModel, TId>
    where TMessage : class
    where TModel : Model<TId>
    where TId : struct
{
    private readonly IMapper _mapper;
    private readonly ISubject<TMessage> _subject;

    protected MessageService(IMapper mapper, ISubject<TMessage> subject)
    {
        _mapper = mapper;
        _subject = subject;
    }

EF TPHからGraphSQLインタフェースへ


GraphSQLインターフェイスは、エンティティから派生した型を表す非常に興味深い方法を提供します.一方、EFコアではTPHで構成することができます.
エンティティ
public class ProductConfig : IEntityTypeConfiguration<Product>
{
    public void Configure(EntityTypeBuilder<Product> builder)
    {
        builder
            .HasDiscriminator()
            .HasValue<Boot>(nameof(Boot))
            .HasValue<Kayak>(nameof(Kayak))
            .HasValue<Backpack>(nameof(Backpack));
    }
}
相続人
public class KayakConfig : IEntityTypeConfiguration<Kayak>
{
    public void Configure(EntityTypeBuilder<Kayak> builder)
    {
        builder
            .HasBaseType<Product>();
    }
}
インターフェース
public sealed class ProductInterfaceGraphType : InterfaceGraphType<Product>
{
    public ProductInterfaceGraphType(BootGraphType bootGraphType, BackpackGraphType backpackGraphType, KayakGraphType kayakGraphType)
    {
        Name = "product";

        ResolveType = @object =>
        {
            return @object switch
            {
                Boot _ => bootGraphType,
                Backpack _ => backpackGraphType,
                Kayak _ => kayakGraphType,
                _ => default
            };
        };
    }
}
オブジェクト
public sealed class KayakGraphType : ObjectGraphType<Kayak>
{
    public KayakGraphType()
    {
        Name = "kayak";
        Interface<ProductInterfaceGraphType>();
        IsTypeOf = o => o is Product;
    }
}

環境設定


開発



秘密
データベースリソースを設定するにはinit 秘密 https://github.com/AntonioFalcao/Dotnet5.GraphQL3.WebApplication/tree/master/src/Dotnet5.GraphQL3.Store.WebAPI , そして、DefaultConnection :
dotnet user-secrets init
dotnet user-secrets set "ConnectionStrings:DefaultConnection" "Server=localhost,1433;Database=Store;User=sa;Password=!MyComplexPassword"
この後、HTTPクライアントを設定します.init 秘密 https://github.com/AntonioFalcao/Dotnet5.GraphQL3.WebApplication/tree/master/src/Dotnet5.GraphQL3.Store.WebMVC ストアクライアントホストを定義します.
dotnet user-secrets init
dotnet user-secrets set "HttpClient:Store" "http://localhost:5000/graphql"

appsettings
あなたが好むなら、WebAPI appsettings.Development.json とWebMVC appsettings.Development.json ファイル
WebAPI
{
  "ConnectionStrings": {
    "DefaultConnection": "Server=localhost,1433;Database=Store;User=sa;Password=!MyComplexPassword"
  }
}
WebMCV
{
  "HttpClient": {
    "Store": "http://localhost:5000/graphql"
  }
}

生産


CD(連続展開)用の使用Dockerを考慮する.それぞれcompose WebアプリケーションとSQLサーバは同じネットワークにあり、名前付きホストを使用できます.既にWebAPIで定義されている appsettings.json とWebMVC appsettings.json ファイル

appsettings
WebAPI
{
  "ConnectionStrings": {
    "DefaultConnection": "Server=mssql;Database=Store;User=sa;Password=!MyComplexPassword"
  }
}
WebMCV
{
  "HttpClient": {
    "Store": "http://webapi:5000/graphql"
  }
}

走る


The ./docker-compose.yml 提供するWebAPI , WebMVC and MS SQL Server アプリケーション
docker-compose up -d

グラフィックプレイグラウンド


デフォルトのプレイグラウンドで応答http://localhost:5000/ui/playground しかし、ホストと他の多くの詳細を設定できます ../...WebAPI/GraphQL/DependencyInjection/Configure.cs
app.UseGraphQLPlayground(
    new GraphQLPlaygroundOptions
    {
        Path = "/ui/playground",
        BetaUpdates = true,
        RequestCredentials = RequestCredentials.Omit,
        HideTracingResponse = false,

        EditorCursorShape = EditorCursorShape.Line,
        EditorTheme = EditorTheme.Dark,
        EditorFontSize = 14,
        EditorReuseHeaders = true,
        EditorFontFamily = "JetBrains Mono"
    });

クエリ


比較と引数のフラグメント


クエリ
{
  First: product(id: "2c05b59b-8fb3-4cba-8698-01d55a0284e5") {
    ...comparisonFields
  }
  Second: product(id: "65af82e8-27f6-44f3-af4a-029b73f14530") {
    ...comparisonFields
  }
}

fragment comparisonFields on Product {
  id
  name
  rating
  description
}
結果
{
  "data": {
    "First": {
      "id": "2c05b59b-8fb3-4cba-8698-01d55a0284e5",
      "name": "libero",
      "rating": 5,
      "description": "Deleniti voluptas quidem accusamus est debitis quisquam enim."
    },
    "Second": {
      "id": "65af82e8-27f6-44f3-af4a-029b73f14530",
      "name": "debitis",
      "rating": 10,
      "description": "Est veniam unde."
    }
  }
}

クエリ名と変数


クエリ
query all {
  products {
    id
    name
  }
}

query byid($productId: ID!) {
  product(id: $productId) {
    id
    name
  }
}
変数
{
  "productId": "2c05b59b-8fb3-4cba-8698-01d55a0284e5"
}
HTTPボディ
{
    "operationName": "byid",
    "variables": {
        "productId": "2c05b59b-8fb3-4cba-8698-01d55a0284e5"
    },
    "query": "query all {
        products {
          id
          name
        }
    }
    query byid($productId: ID!) {
        product(id: $productId) {
          id
          name
        }
    }"
}
遊び場

インクルード、スキップ、デフォルト値


クエリ
query all($showPrice: Boolean = false) {
  products {
    id
    name
    price @include(if: $showPrice)
    rating @skip(if: $showPrice)
  }
}
変数
{
  "showPrice": true
}
HTTPボディ
{
    "operationName": "all",
    "variables": {
        "showPrice": false
    },
    "query": "query all($showPrice: Boolean = false) {
          products {
            id
            name
            price @include(if: $showPrice)
            rating @skip(if: $showPrice)
          }
    }"
}

突然変異


突然変異

Creating / adding a new Review to the respective product.


mutation($review: reviewInput!) {
  createReview(review: $review) {
    id
  }
}
変数
{
  "review": {
    "title": "some title",
    "comment": "some comment",
    "productId": "0fb8ec7e-7af1-4fe3-a2e2-000996ffd20f"
  }
}
結果
{
  "data": {
    "createReview": {
      "title": "some title"
    }
  }
}

購読


購読

The Mutation stay listening if a new review is added.


subscription {
  reviewAdded {
    title
  }
}

結果
{
  "data": {
    "reviewAdded": {
      "title": "Some title"
    }
  }
}

組み込み


マイクロソフトスタック.0 ( rc 1 )


  • .NET 5.0 - ベースフレームワーク

  • ASP.NET 5.0 - Webフレームワーク

  • Entity Framework Core 5.0 - ORM

  • Microsoft SQL Server on Linux for Docker - データベース.
  • グラムスタックスタック- v 3 .0 (プレビュー/アルファ)


  • GraphQL - GraphSQLは、APIを使用してクエリを実行するためのクエリ言語です.

  • GraphQL for .NET - これはGraphSQLの実装です.ネット

  • GraphQL.Client - のためのGraphSQLクライアント.HTTP経由でネット;

  • GraphQL Playground - より良い開発ワークフローのためのGraphSQL IDE.
  • コミュニティスタック


  • AutoMapper - コンベンションベースのオブジェクトオブジェクトマッパー;

  • FluentValidation - 人気.厳密な型付け検証規則を構築するためのネットライブラリ

  • Bogus - Cφ,F,KHI,VBのための単純で正気の偽データ発生器ネット

  • Bootstrap - 世界で最も人気のあるHTML、CSS、およびJSライブラリ.
  • アントニヤコカリ / dotnet 6。グラフ。Webアプリケーション


    このプロジェクトは、完全なGraphel 4 Web APIを消費する単純なかみそりウェブMVCコアの実装とdockerizationを例示します。オニオンアーキテクチャでドメイン駆動の概念を適用し、固体と乾燥のような開発ベストプラクティスを考慮して、NET 6多層プロジェクト。