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

feat: Update to dotnet 8. #2

Merged
merged 1 commit into from
Aug 22, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 13 additions & 0 deletions .config/dotnet-tools.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
{
"version": 1,
"isRoot": true,
"tools": {
"dotnet-outdated-tool": {
"version": "4.6.4",
"commands": [
"dotnet-outdated"
],
"rollForward": false
}
}
}
18 changes: 16 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,8 @@
- [The Goals of This Project](#the-goals-of-this-project)
- [Technologies - Libraries](#technologies---libraries)
- [Structure of Project](#structure-of-project)
- [Development Setup](#development_setup)
- [Dotnet Tool Packages](#dotnet_tool_packages)
- [How to Run](#how-to-run)
- [Docker Compose](#docker-compose)

Expand All @@ -21,15 +23,15 @@
- :sparkle: Using `Domain Driven Design (DDD)` for implementing `business processes` and `validation rules`.
- :sparkle: Adopting `CQRS` implementation with the `MediatR` library for better separation of `write` and `read` operations.
- :sparkle: Implementing `MediatR` to `reduce coupling` and provide support for managing `cross-cutting concerns` within `pipelines`, including `validation` and `transaction handling` for the application.
- :sparkle: Using `SqlServer` as our `relational database` management system at the database level.
- :sparkle: Using `Postgres` as our `relational database` management system at the database level.
- :sparkle: Incorporating `Unit Testing`, `Integration Testing`, and `End To End Testing` for testing level to ensure the `robustness` and `reliability` of the application.
- :sparkle: Utilizing `Fluent Validation` and a `Validation Pipeline Behaviour` on top of `MediatR` to validate requests and responses and ensure `data integrity`.
- :sparkle: Using `Minimal API` for all endpoints to create a `lightweight` and `streamlined` API.
- :sparkle: Using `Docker-Compose` for our `deployment` mechanism to enable easy deployment and scaling of the application.

## Technologies - Libraries

- ✔️ **[`.NET 7`](https://dotnet.microsoft.com/download)** - .NET Framework and .NET Core, including ASP.NET and ASP.NET Core
- ✔️ **[`.NET 8`](https://dotnet.microsoft.com/download)** - .NET Framework and .NET Core, including ASP.NET and ASP.NET Core
- ✔️ **[`MVC Versioning API`](https://github.com/microsoft/aspnet-api-versioning)** - Set of libraries which add service API versioning to ASP.NET Web API, OData with ASP.NET Web API, and ASP.NET Core
- ✔️ **[`EF Core`](https://github.com/dotnet/efcore)** - Modern object-database mapper for .NET. It supports LINQ queries, change tracking, updates, and schema migrations
- ✔️ **[`MediatR`](https://github.com/jbogard/MediatR)** - Simple, unambitious mediator implementation in .NET.
Expand Down Expand Up @@ -68,6 +70,18 @@ With `CQRS`, we can `reduce coupling` between layers and tune down specific meth

Overall, by using the `REPR` pattern and `CQRS` with the `Mediator` pattern, we can create a `better-structured` and more `maintainable` application, with improved `separation of concerns`.

## Development Setup

### Dotnet Tool Packages
For installing our requirement package with .NET cli tools, we need to install `dotnet tool manifest`.
```bash
dotnet new tool-manifest
```
And after that we can restore our dotnet tool packages with .NET cli tools from `.config` folder and `dotnet-tools.json` file.
```
dotnet tool restore
```

## How to Run

### Docker Compose
Expand Down
29 changes: 18 additions & 11 deletions deployments/docker-compose/docker-compose.yml
Original file line number Diff line number Diff line change
@@ -1,17 +1,24 @@
version: "3.3"
services:

######################################################
# SqlServer
######################################################
sql:
container_name: sql
image: mcr.microsoft.com/mssql/server
ports:
- "1433:1433"
environment:
SA_PASSWORD: "Password@1234"
ACCEPT_EULA: "Y"
#######################################################
# Postgres
######################################################
postgres:
image: postgres:latest
container_name: postgres
restart: unless-stopped
ports:
- '5432:5432'
environment:
- POSTGRES_USER=postgres
- POSTGRES_PASSWORD=postgres
command:
- "postgres"
- "-c"
- "wal_level=logical"
- "-c"
- "max_prepared_transactions=10"


#######################################################
Expand Down
6 changes: 6 additions & 0 deletions global.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
{
"sdk": {
"version": "8.0.303",
"rollForward": "latestFeature"
}
}
88 changes: 44 additions & 44 deletions src/BuildingBlocks/BuildingBlocks.csproj
Original file line number Diff line number Diff line change
@@ -1,73 +1,73 @@
<Project Sdk="Microsoft.NET.Sdk">

<PropertyGroup>
<TargetFramework>net7.0</TargetFramework>
<TargetFramework>net8.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
</PropertyGroup>


<ItemGroup>
<PackageReference Include="Ardalis.GuardClauses" Version="4.0.1" />
<PackageReference Include="Asp.Versioning.Abstractions" Version="7.0.0" />
<PackageReference Include="Asp.Versioning.Http" Version="7.0.0" />
<PackageReference Include="Asp.Versioning.Mvc" Version="7.0.0" />
<PackageReference Include="Asp.Versioning.Mvc.ApiExplorer" Version="7.0.0" />
<PackageReference Include="AutoMapper.Extensions.Microsoft.DependencyInjection" Version="12.0.1" />
<PackageReference Include="EFCore.NamingConventions" Version="7.0.2" />
<PackageReference Include="Ardalis.GuardClauses" Version="4.6.0" />
<PackageReference Include="Asp.Versioning.Abstractions" Version="8.1.0" />
<PackageReference Include="Asp.Versioning.Http" Version="8.1.0" />
<PackageReference Include="Asp.Versioning.Mvc" Version="8.1.0" />
<PackageReference Include="Asp.Versioning.Mvc.ApiExplorer" Version="8.1.0" />
<PackageReference Include="EFCore.NamingConventions" Version="8.0.3" />
<PackageReference Include="Figgle" Version="0.5.1" />
<PackageReference Include="FluentValidation" Version="11.5.2" />
<PackageReference Include="FluentValidation" Version="11.9.2" />
<PackageReference Include="FluentValidation.AspNetCore" Version="11.3.0" />
<PackageReference Include="Microsoft.AspNetCore.OpenApi" Version="7.0.2" />
<PackageReference Include="Microsoft.EntityFrameworkCore.InMemory" Version="7.0.2" />
<PackageReference Include="Microsoft.AspNetCore.OpenApi" Version="8.0.8" />
<PackageReference Include="Microsoft.EntityFrameworkCore.InMemory" Version="8.0.8" />

<PackageReference Include="Microsoft.EntityFrameworkCore.Relational" Version="7.0.2" />
<PackageReference Include="Microsoft.Extensions.DependencyModel" Version="7.0.0" />
<PackageReference Include="NSubstitute" Version="4.4.0" />
<PackageReference Include="Microsoft.EntityFrameworkCore.Relational" Version="8.0.8" />
<PackageReference Include="Microsoft.Extensions.DependencyModel" Version="8.0.1" />
<PackageReference Include="Npgsql.EntityFrameworkCore.PostgreSQL" Version="8.0.4" />
<PackageReference Include="NSubstitute" Version="5.1.0" />

<PackageReference Include="Humanizer.Core" Version="2.14.1" />
<PackageReference Include="MediatR" Version="12.0.1" />
<PackageReference Include="AutoMapper" Version="12.0.1" />
<PackageReference Include="MediatR" Version="12.4.0" />
<PackageReference Include="AutoMapper" Version="13.0.1" />
<PackageReference Include="Microsoft.AspNetCore.Http" Version="2.2.2" />
<PackageReference Include="Microsoft.EntityFrameworkCore" Version="7.0.2" />
<PackageReference Include="Microsoft.EntityFrameworkCore.SqlServer" Version="7.0.2" />
<PackageReference Include="Microsoft.Extensions.Configuration.Abstractions" Version="7.0.0" />
<PackageReference Include="Microsoft.Extensions.Configuration.Binder" Version="7.0.2" />
<PackageReference Include="Microsoft.Extensions.DependencyInjection" Version="7.0.0" />
<PackageReference Include="Microsoft.Extensions.DependencyInjection.Abstractions" Version="7.0.0" />
<PackageReference Include="Microsoft.Extensions.Logging.Abstractions" Version="7.0.0" />
<PackageReference Include="Newtonsoft.Json" Version="13.0.2" />
<PackageReference Include="Scrutor" Version="4.2.0" />
<PackageReference Include="Microsoft.EntityFrameworkCore" Version="8.0.8" />
<PackageReference Include="Microsoft.Extensions.Configuration.Abstractions" Version="8.0.0" />
<PackageReference Include="Microsoft.Extensions.Configuration.Binder" Version="8.0.2" />
<PackageReference Include="Microsoft.Extensions.DependencyInjection" Version="8.0.0" />
<PackageReference Include="Microsoft.Extensions.DependencyInjection.Abstractions" Version="8.0.1" />
<PackageReference Include="Microsoft.Extensions.Logging.Abstractions" Version="8.0.1" />
<PackageReference Include="Newtonsoft.Json" Version="13.0.3" />
<PackageReference Include="Scrutor" Version="4.2.2" />
<PackageReference Include="Scrutor.AspNetCore" Version="3.3.0" />
<PackageReference Include="Sentry.Serilog" Version="3.25.0" />
<PackageReference Include="Serilog" Version="2.12.0" />
<PackageReference Include="Serilog.AspNetCore" Version="6.1.0" />
<PackageReference Include="Serilog" Version="4.0.1" />
<PackageReference Include="Serilog.AspNetCore" Version="8.0.2" />
<PackageReference Include="Serilog.Enrichers.Span" Version="3.1.0" />
<PackageReference Include="Serilog.Exceptions" Version="8.4.0" />
<PackageReference Include="Serilog.Formatting.Elasticsearch" Version="8.4.1" />
<PackageReference Include="Serilog.Sinks.Console" Version="4.1.0" />
<PackageReference Include="Serilog.Sinks.Elasticsearch" Version="8.4.1" />
<PackageReference Include="Serilog.Sinks.Seq" Version="5.2.2" />
<PackageReference Include="Serilog.Formatting.Elasticsearch" Version="10.0.0" />
<PackageReference Include="Serilog.Sinks.Console" Version="6.0.0" />
<PackageReference Include="Serilog.Sinks.Elasticsearch" Version="10.0.0" />
<PackageReference Include="Serilog.Sinks.Seq" Version="8.0.0" />
<PackageReference Include="Serilog.Sinks.SpectreConsole" Version="0.3.3" />
<PackageReference Include="Serilog.Sinks.XUnit" Version="3.0.3" />
<PackageReference Include="Serilog.Sinks.XUnit" Version="3.0.5" />
<PackageReference Include="Sieve" Version="2.5.5" />
<PackageReference Include="Swashbuckle.AspNetCore" Version="6.5.0" />
<PackageReference Include="Swashbuckle.AspNetCore.SwaggerGen" Version="6.5.0" />
<PackageReference Include="Swashbuckle.AspNetCore.SwaggerUI" Version="6.5.0" />
<PackageReference Include="Swashbuckle.AspNetCore" Version="6.7.1" />
<PackageReference Include="Swashbuckle.AspNetCore.SwaggerGen" Version="6.7.1" />
<PackageReference Include="Swashbuckle.AspNetCore.SwaggerUI" Version="6.7.1" />
<PackageReference Include="System.Linq.Async" Version="6.0.1" />
<PackageReference Include="System.Linq.Async.Queryable" Version="6.0.1" />
<PackageReference Include="Testcontainers" Version="3.0.0" />
<PackageReference Include="Testcontainers.MsSql" Version="3.0.0" />
<PackageReference Include="Testcontainers" Version="3.9.0" />
<PackageReference Include="Testcontainers.MsSql" Version="3.9.0" />
<PackageReference Include="Testcontainers.PostgreSql" Version="3.9.0" />
<PackageReference Include="Unchase.Swashbuckle.AspNetCore.Extensions" Version="2.7.1" />

<PackageReference Include="MassTransit" Version="8.0.12" />
<PackageReference Include="MassTransit" Version="8.2.4" />

<PackageReference Include="Microsoft.Extensions.Hosting" Version="7.0.0" />
<PackageReference Include="Microsoft.Extensions.Hosting" Version="8.0.0" />
<PackageReference Include="AutoBogus" Version="2.13.1" />
<PackageReference Include="Bogus" Version="34.0.2" />
<PackageReference Include="FluentAssertions" Version="6.9.0" />
<PackageReference Include="Respawn" Version="6.0.0" />
<PackageReference Include="Microsoft.AspNetCore.TestHost" Version="7.0.2" />
<PackageReference Include="Microsoft.AspNetCore.Mvc.Testing" Version="7.0.2" />
<PackageReference Include="Bogus" Version="35.6.0" />
<PackageReference Include="FluentAssertions" Version="6.12.0" />
<PackageReference Include="Respawn" Version="6.2.1" />
<PackageReference Include="Microsoft.AspNetCore.TestHost" Version="8.0.8" />
<PackageReference Include="Microsoft.AspNetCore.Mvc.Testing" Version="8.0.8" />

<PackageReference Update="Microsoft.VisualStudio.Threading.Analyzers" Version="17.4.27" />
<PackageReference Update="AsyncFixer" Version="1.6.0" />
Expand Down
8 changes: 5 additions & 3 deletions src/BuildingBlocks/EFCore/Extensions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -20,15 +20,17 @@ public static IServiceCollection AddCustomDbContext<TContext>(
this IServiceCollection services)
where TContext : DbContext, IDbContext
{
services.AddValidateOptions<SqlOptions>();
AppContext.SetSwitch("Npgsql.EnableLegacyTimestampBehavior", true);

services.AddValidateOptions<PostgresOptions>();

services.AddDbContext<TContext>((sp, options) =>
{
var postgresOptions = sp.GetRequiredService<SqlOptions>();
var postgresOptions = sp.GetRequiredService<PostgresOptions>();

Guard.Against.Null(options, nameof(postgresOptions));

options.UseSqlServer(postgresOptions?.ConnectionString,
options.UseNpgsql(postgresOptions?.ConnectionString,
dbOptions =>
{
dbOptions.MigrationsAssembly(typeof(TContext).Assembly.GetName().Name);
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
namespace BuildingBlocks.EFCore;

public class SqlOptions
public class PostgresOptions
{
public string ConnectionString { get; set; }
}
41 changes: 20 additions & 21 deletions src/BuildingBlocks/TestBase/TestBase.cs
Original file line number Diff line number Diff line change
Expand Up @@ -20,9 +20,9 @@

namespace BuildingBlocks.TestBase;

using System.Globalization;
using Microsoft.Data.SqlClient;
using Testcontainers.MsSql;
using Npgsql;
using Testcontainers.PostgreSql;

public class TestFixture<TEntryPoint> : IAsyncLifetime
where TEntryPoint : class
Expand All @@ -31,14 +31,13 @@
private static int Timeout => 120; // Second
private ITestHarness TestHarness => ServiceProvider?.GetTestHarness();
private Action<IServiceCollection> TestRegistrationServices { get; set; }
private MsSqlContainer MsSqlTestContainer;
private PostgreSqlContainer _postgreSqlContainer;

public HttpClient HttpClient => _factory?.CreateClient();

public IServiceProvider ServiceProvider => _factory?.Services;
public IConfiguration Configuration => _factory?.Services.GetRequiredService<IConfiguration>();
public ILogger Logger { get; set; }

protected TestFixture()
{
_factory = new WebApplicationFactory<TEntryPoint>()
Expand Down Expand Up @@ -177,21 +176,21 @@

private async Task StartTestContainerAsync()
{
MsSqlTestContainer = TestContainers.MsSqlTestContainer();
_postgreSqlContainer = TestContainers.PostgresTestContainer();

await MsSqlTestContainer.StartAsync();
await _postgreSqlContainer.StartAsync();
}

private async Task StopTestContainerAsync()
{
await MsSqlTestContainer.StopAsync();
await _postgreSqlContainer.StopAsync();
}

private void AddCustomAppSettings(IConfigurationBuilder configuration)
{
configuration.AddInMemoryCollection(new KeyValuePair<string, string>[]
{
new("SqlOptions:ConnectionString", MsSqlTestContainer.GetConnectionString()),
new("PostgresOptions:ConnectionString", _postgreSqlContainer.GetConnectionString()),
});
}

Expand Down Expand Up @@ -334,7 +333,7 @@
where TEntryPoint : class
{
private Respawner _reSpawnerDefaultDb;
private SqlConnection DefaultDbConnection { get; set; }
private NpgsqlConnection _defaultDbConnection;

public TestFixtureCore(TestFixture<TEntryPoint> integrationTestFixture, ITestOutputHelper outputHelper)
{
Expand All @@ -348,35 +347,35 @@

public async Task InitializeAsync()
{
await InitSqlAsync();
await InitDatabaseAsync();
}

public async Task DisposeAsync()
{
await ResetSqlAsync();
await ResetDatabaseAsync();
}

private async Task InitSqlAsync()
private async Task InitDatabaseAsync()
{
var sqlOptions = Fixture.ServiceProvider.GetService<SqlOptions>();
var postgresOptions = Fixture.ServiceProvider.GetService<PostgresOptions>();

if (!string.IsNullOrEmpty(sqlOptions?.ConnectionString))
if (!string.IsNullOrEmpty(postgresOptions?.ConnectionString))
{
DefaultDbConnection = new SqlConnection(sqlOptions.ConnectionString);
await DefaultDbConnection.OpenAsync();
_defaultDbConnection = new NpgsqlConnection(postgresOptions.ConnectionString);
await _defaultDbConnection.OpenAsync();

_reSpawnerDefaultDb = await Respawner.CreateAsync(DefaultDbConnection,
new RespawnerOptions { DbAdapter = DbAdapter.SqlServer });
_reSpawnerDefaultDb = await Respawner.CreateAsync(_defaultDbConnection,
new RespawnerOptions { DbAdapter = DbAdapter.Postgres });

await SeedDataAsync();
}
}

private async Task ResetSqlAsync()
private async Task ResetDatabaseAsync()
{
if (DefaultDbConnection is not null)
if (_defaultDbConnection is not null)
{
await _reSpawnerDefaultDb.ResetAsync(DefaultDbConnection);
await _reSpawnerDefaultDb.ResetAsync(_defaultDbConnection);
}
}

Expand Down Expand Up @@ -421,5 +420,5 @@
Fixture = integrationTestFixture;
}

public TestFixture<TEntryPoint, TWContext> Fixture { get; }

Check warning on line 423 in src/BuildingBlocks/TestBase/TestBase.cs

View workflow job for this annotation

GitHub Actions / ci

'TestBase<TEntryPoint, TWContext>.Fixture' hides inherited member 'TestFixtureCore<TEntryPoint>.Fixture'. Use the new keyword if hiding was intended.
}
Loading
Loading