Skip to content

Commit d27d100

Browse files
committed
Added example of basic CQRS using Endpoints, Nullable Reference Types, Records and other C# 8-9 goodies
1 parent 523eed8 commit d27d100

33 files changed

+1222
-0
lines changed

EventSourcing.NetCore.sln

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -177,6 +177,14 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Tools", "Workshops\BuildYou
177177
EndProject
178178
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solved", "Solved", "{C9011E7F-42EA-4FEE-A5BB-EFC11BBA0DB0}"
179179
EndProject
180+
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Warehouse", "Warehouse", "{4AC3138B-6FD1-4620-A75A-3FCACE995162}"
181+
EndProject
182+
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Warehouse", "Sample\Warehouse\Warehouse\Warehouse.csproj", "{C45ACE62-41BA-49D9-956A-39B479D7A50A}"
183+
EndProject
184+
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Warehouse.Api", "Sample\Warehouse\Warehouse.Api\Warehouse.Api.csproj", "{76C04CB6-32C7-47EA-884A-6343BDD39644}"
185+
EndProject
186+
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Warehouse.Api.Tests", "Sample\Warehouse\Warehouse.Api.Tests\Warehouse.Api.Tests.csproj", "{69B22937-CA8B-478D-97F8-4D33558B5BC9}"
187+
EndProject
180188
Global
181189
GlobalSection(SolutionConfigurationPlatforms) = preSolution
182190
Debug|Any CPU = Debug|Any CPU
@@ -419,6 +427,18 @@ Global
419427
{5C54B28C-7746-4DD0-865E-1AC0D8E8D46B}.Debug|Any CPU.Build.0 = Debug|Any CPU
420428
{5C54B28C-7746-4DD0-865E-1AC0D8E8D46B}.Release|Any CPU.ActiveCfg = Release|Any CPU
421429
{5C54B28C-7746-4DD0-865E-1AC0D8E8D46B}.Release|Any CPU.Build.0 = Release|Any CPU
430+
{C45ACE62-41BA-49D9-956A-39B479D7A50A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
431+
{C45ACE62-41BA-49D9-956A-39B479D7A50A}.Debug|Any CPU.Build.0 = Debug|Any CPU
432+
{C45ACE62-41BA-49D9-956A-39B479D7A50A}.Release|Any CPU.ActiveCfg = Release|Any CPU
433+
{C45ACE62-41BA-49D9-956A-39B479D7A50A}.Release|Any CPU.Build.0 = Release|Any CPU
434+
{76C04CB6-32C7-47EA-884A-6343BDD39644}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
435+
{76C04CB6-32C7-47EA-884A-6343BDD39644}.Debug|Any CPU.Build.0 = Debug|Any CPU
436+
{76C04CB6-32C7-47EA-884A-6343BDD39644}.Release|Any CPU.ActiveCfg = Release|Any CPU
437+
{76C04CB6-32C7-47EA-884A-6343BDD39644}.Release|Any CPU.Build.0 = Release|Any CPU
438+
{69B22937-CA8B-478D-97F8-4D33558B5BC9}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
439+
{69B22937-CA8B-478D-97F8-4D33558B5BC9}.Debug|Any CPU.Build.0 = Debug|Any CPU
440+
{69B22937-CA8B-478D-97F8-4D33558B5BC9}.Release|Any CPU.ActiveCfg = Release|Any CPU
441+
{69B22937-CA8B-478D-97F8-4D33558B5BC9}.Release|Any CPU.Build.0 = Release|Any CPU
422442
EndGlobalSection
423443
GlobalSection(SolutionProperties) = preSolution
424444
HideSolutionNode = FALSE
@@ -496,6 +516,10 @@ Global
496516
{C9011E7F-42EA-4FEE-A5BB-EFC11BBA0DB0} = {94524EA9-A4BA-4684-99B8-BBE9EE85E791}
497517
{7ACC398F-87BF-4B3E-AD61-DFB5F56D4B25} = {C9011E7F-42EA-4FEE-A5BB-EFC11BBA0DB0}
498518
{03D0848C-7B19-4685-BA1F-59FFAF1DCEA6} = {C9011E7F-42EA-4FEE-A5BB-EFC11BBA0DB0}
519+
{4AC3138B-6FD1-4620-A75A-3FCACE995162} = {A7186B6B-D56D-4AEF-B6B7-FAA827764C34}
520+
{C45ACE62-41BA-49D9-956A-39B479D7A50A} = {4AC3138B-6FD1-4620-A75A-3FCACE995162}
521+
{76C04CB6-32C7-47EA-884A-6343BDD39644} = {4AC3138B-6FD1-4620-A75A-3FCACE995162}
522+
{69B22937-CA8B-478D-97F8-4D33558B5BC9} = {4AC3138B-6FD1-4620-A75A-3FCACE995162}
499523
EndGlobalSection
500524
GlobalSection(ExtensibilityGlobals) = postSolution
501525
SolutionGuid = {A5F55604-2FF3-43B7-B657-4F18E6E95D3B}
Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
using System;
2+
using System.Net;
3+
using System.Net.Http;
4+
using System.Threading.Tasks;
5+
using Core.Testing;
6+
using FluentAssertions;
7+
using Microsoft.AspNetCore.Hosting;
8+
using Warehouse.Products.RegisteringProduct;
9+
using Xunit;
10+
11+
namespace Warehouse.Api.Tests.Products.RegisteringProduct
12+
{
13+
public class RegisteringProduct
14+
{
15+
public class RegisterProductFixture: ApiFixture
16+
{
17+
protected override string ApiUrl => "/api/products";
18+
19+
protected override Func<IWebHostBuilder, IWebHostBuilder> SetupWebHostBuilder =>
20+
WarehouseTestWebHostBuilder.Configure;
21+
}
22+
23+
public class RegisterProductTests: IClassFixture<RegisterProductFixture>
24+
{
25+
private readonly RegisterProductFixture fixture;
26+
27+
public RegisterProductTests(RegisterProductFixture fixture)
28+
{
29+
this.fixture = fixture;
30+
}
31+
32+
[Fact]
33+
public async Task ValidRequest_ShouldReturn_OK()
34+
{
35+
// Given
36+
const string sku = "test";
37+
const string name = "test";
38+
const string description = "test";
39+
var request = new RegisterProductRequest(sku, name, description);
40+
41+
// When
42+
var response = await fixture.Post(request);
43+
44+
// Then
45+
response.EnsureSuccessStatusCode();
46+
response.StatusCode.Should().Be(HttpStatusCode.Created);
47+
}
48+
}
49+
}
50+
}
Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
<Project Sdk="Microsoft.NET.Sdk">
2+
3+
<PropertyGroup>
4+
<TargetFramework>net5.0</TargetFramework>
5+
<Nullable>enable</Nullable>
6+
<TreatWarningsAsErrors>true</TreatWarningsAsErrors>
7+
</PropertyGroup>
8+
9+
<ItemGroup>
10+
<PackageReference Include="FluentAssertions" Version="5.10.3" />
11+
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="16.9.4" />
12+
<PackageReference Include="Swashbuckle.AspNetCore.Swagger" Version="6.1.4" />
13+
<PackageReference Include="xunit" Version="2.4.1" />
14+
<PackageReference Include="xunit.runner.visualstudio" Version="2.4.3">
15+
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
16+
<PrivateAssets>all</PrivateAssets>
17+
</PackageReference>
18+
<PackageReference Include="coverlet.collector" Version="1.3.0">
19+
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
20+
<PrivateAssets>all</PrivateAssets>
21+
</PackageReference>
22+
</ItemGroup>
23+
24+
25+
<ItemGroup>
26+
<FrameworkReference Include="Microsoft.AspNetCore.App" />
27+
</ItemGroup>
28+
29+
<ItemGroup>
30+
<ProjectReference Include="..\..\..\Core.Testing\Core.Testing.csproj" />
31+
<ProjectReference Include="..\Warehouse.Api\Warehouse.Api.csproj" />
32+
<ProjectReference Include="..\Warehouse\Warehouse.csproj" />
33+
</ItemGroup>
34+
35+
<ItemGroup>
36+
<Folder Include="Products" />
37+
</ItemGroup>
38+
39+
<ItemGroup>
40+
<Content Include="appsettings.Development.json">
41+
<ExcludeFromSingleFile>true</ExcludeFromSingleFile>
42+
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
43+
<CopyToPublishDirectory>PreserveNewest</CopyToPublishDirectory>
44+
</Content>
45+
<Content Include="appsettings.json">
46+
<ExcludeFromSingleFile>true</ExcludeFromSingleFile>
47+
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
48+
<CopyToPublishDirectory>PreserveNewest</CopyToPublishDirectory>
49+
</Content>
50+
</ItemGroup>
51+
52+
</Project>
Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
using Microsoft.AspNetCore.Builder;
2+
using Microsoft.AspNetCore.Hosting;
3+
using Microsoft.EntityFrameworkCore;
4+
using Microsoft.Extensions.DependencyInjection;
5+
using Warehouse.Storage;
6+
7+
namespace Warehouse.Api.Tests
8+
{
9+
public static class WarehouseTestWebHostBuilder
10+
{
11+
public static IWebHostBuilder Configure(IWebHostBuilder webHostBuilder)
12+
{
13+
webHostBuilder
14+
.ConfigureServices(services =>
15+
{
16+
services.AddMvcCore()
17+
.AddAuthorization()
18+
.AddCors()
19+
.AddDataAnnotations()
20+
.AddFormatterMappings();
21+
22+
services.AddWarehouseServices();
23+
})
24+
.Configure(app =>
25+
{
26+
app.UseHttpsRedirection()
27+
.UseRouting()
28+
.UseAuthorization()
29+
.UseEndpoints(endpoints => { endpoints.UseWarehouseEndpoints(); });
30+
31+
// Kids, do not try this at home!
32+
app.ApplicationServices.GetRequiredService<WarehouseDBContext>().Database.Migrate();
33+
});
34+
35+
return webHostBuilder;
36+
}
37+
}
38+
}
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
{
2+
"Logging": {
3+
"LogLevel": {
4+
"Default": "Information",
5+
"Microsoft": "Warning",
6+
"Microsoft.Hosting.Lifetime": "Information"
7+
}
8+
}
9+
}
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
{
2+
"Logging": {
3+
"LogLevel": {
4+
"Default": "Information",
5+
"Microsoft": "Warning",
6+
"Microsoft.Hosting.Lifetime": "Information"
7+
}
8+
},
9+
"ConnectionStrings": {
10+
"WarehouseDB": "PORT = 5432; HOST = 127.0.0.1; TIMEOUT = 15; POOLING = True; MINPOOLSIZE = 1; MAXPOOLSIZE = 100; COMMANDTIMEOUT = 20; DATABASE = 'postgres'; PASSWORD = 'Password12!'; USER ID = 'postgres'"
11+
},
12+
"AllowedHosts": "*"
13+
}
Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
using Microsoft.AspNetCore.Builder;
2+
using Microsoft.AspNetCore.Hosting;
3+
using Microsoft.Extensions.DependencyInjection;
4+
using Microsoft.Extensions.Hosting;
5+
using Microsoft.OpenApi.Models;
6+
using Warehouse;
7+
8+
var builder = Host.CreateDefaultBuilder(args)
9+
.ConfigureWebHostDefaults(webBuilder =>
10+
{
11+
webBuilder
12+
.ConfigureServices(services =>
13+
{
14+
services.AddMvcCore()
15+
.AddApiExplorer()
16+
.AddAuthorization()
17+
.AddCors()
18+
.AddDataAnnotations()
19+
.AddFormatterMappings();
20+
21+
services
22+
.AddWarehouseServices()
23+
.AddSwaggerGen(c =>
24+
{
25+
c.SwaggerDoc("v1", new OpenApiInfo {Title = "Warehouse.Api", Version = "v1"});
26+
});
27+
})
28+
.Configure(app =>
29+
{
30+
app.UseSwagger()
31+
.UseSwaggerUI(c => c.SwaggerEndpoint("/swagger/v1/swagger.json", "CashRegisters.Api v1"))
32+
.UseHttpsRedirection()
33+
.UseRouting()
34+
.UseAuthorization()
35+
.UseEndpoints(endpoints =>
36+
{
37+
endpoints.UseWarehouseEndpoints();
38+
});
39+
});
40+
})
41+
.Build();
42+
builder.Run();
Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
{
2+
"$schema": "http://json.schemastore.org/launchsettings.json",
3+
"iisSettings": {
4+
"windowsAuthentication": false,
5+
"anonymousAuthentication": true,
6+
"iisExpress": {
7+
"applicationUrl": "http://localhost:59471",
8+
"sslPort": 44389
9+
}
10+
},
11+
"profiles": {
12+
"IIS Express": {
13+
"commandName": "IISExpress",
14+
"launchBrowser": true,
15+
"launchUrl": "swagger",
16+
"environmentVariables": {
17+
"ASPNETCORE_ENVIRONMENT": "Development"
18+
}
19+
},
20+
"CashRegisters.Api": {
21+
"commandName": "Project",
22+
"dotnetRunMessages": "true",
23+
"launchBrowser": true,
24+
"launchUrl": "swagger",
25+
"applicationUrl": "https://localhost:5001;http://localhost:5000",
26+
"environmentVariables": {
27+
"ASPNETCORE_ENVIRONMENT": "Development"
28+
}
29+
}
30+
}
31+
}
Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
<Project Sdk="Microsoft.NET.Sdk.Web">
2+
3+
<PropertyGroup>
4+
<TargetFramework>net5.0</TargetFramework>
5+
<Nullable>enable</Nullable>
6+
<TreatWarningsAsErrors>true</TreatWarningsAsErrors>
7+
</PropertyGroup>
8+
9+
<ItemGroup>
10+
<PackageReference Include="Microsoft.AspNetCore.Authentication.JwtBearer" Version="5.0.1" NoWarn="NU1605" />
11+
<PackageReference Include="Microsoft.AspNetCore.Authentication.OpenIdConnect" Version="5.0.1" NoWarn="NU1605" />
12+
<PackageReference Include="Swashbuckle.AspNetCore" Version="6.1.4" />
13+
<PackageReference Include="Microsoft.EntityFrameworkCore.Design" Version="5.0.5">
14+
<PrivateAssets>all</PrivateAssets>
15+
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
16+
</PackageReference>
17+
</ItemGroup>
18+
19+
<ItemGroup>
20+
<ProjectReference Include="..\Warehouse\Warehouse.csproj" />
21+
</ItemGroup>
22+
23+
</Project>
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
{
2+
"Logging": {
3+
"LogLevel": {
4+
"Default": "Information",
5+
"Microsoft": "Warning",
6+
"Microsoft.Hosting.Lifetime": "Information"
7+
}
8+
}
9+
}

0 commit comments

Comments
 (0)