├── .editorconfig
├── .github
└── workflows
│ ├── BuildStatus.yml
│ └── deploy.yml
├── .gitignore
├── App.sln
├── README.md
├── Tests
└── App.WebApi.IntegrationTest
│ ├── App.WebApi.IntegrationTest.csproj
│ ├── AppControllerTests.cs
│ ├── Infrastructure
│ ├── ClientSerializer.cs
│ ├── ControllerTestBase.cs
│ └── TestServerFixture.cs
│ ├── ProjectControllerTests.cs
│ ├── Properties
│ └── launchSettings.json
│ ├── appsettings.Development.json
│ └── appsettings.json
└── src
├── App.Application.Commands
├── App.Application.Commands.csproj
└── ProjectBC
│ ├── AddProject.cs
│ ├── DeleteProject.cs
│ ├── Handlers
│ ├── AddProjectHandler.cs
│ ├── DeleteProjectHandler.cs
│ └── UpdateProjectHandler.cs
│ ├── UpdateProject.cs
│ └── Validators
│ ├── CreateProjectValidator.cs
│ ├── DeleteProjectValidator.cs
│ └── UpdateProjectValidator.cs
├── App.Application.Events
├── App.Application.Events.csproj
├── Handlers
│ └── DummyProjectCreatedHandler.cs
└── ProjectCreated.cs
├── App.Application.Queries
├── App.Application.Queries.csproj
├── App.Application.QueryHandlers.csproj
├── MapDomainToQueryResult.cs
└── ProjectBC
│ ├── GetProject.cs
│ ├── GetProjects.cs
│ └── Handlers
│ ├── GetProjectHandler.cs
│ └── GetProjectsHandler.cs
├── App.Core
├── App.Core.csproj
├── Domain
│ └── ProjectBC
│ │ ├── IProjectRepository.cs
│ │ └── Project.cs
├── ErrorMessages.cs
└── IWorkContext.cs
├── App.Infrastructure.Persistence.SqlServer
├── App.Infrastructure.Persistence.SqlServer.csproj
├── Context
│ ├── AppDbContext.cs
│ └── DbContextFactory.cs
├── Migrations
│ ├── 20200218115151_initial.Designer.cs
│ ├── 20200218115151_initial.cs
│ └── AppDbContextModelSnapshot.cs
└── ProjectBC
│ ├── Configurations
│ └── ConfigProject.cs
│ └── ProjectRepository.cs
├── App.WebApi
├── App.WebApi.csproj
├── App.WebApi.xml
├── Controllers
│ └── ProjectsController.cs
├── Extensions
│ ├── ServicesExtensions.cs
│ └── SwaggerExtensions.cs
├── Infrastructure
│ ├── DependencyRegistrations.cs
│ ├── Scopes.cs
│ ├── UserClaimsExtender.cs
│ └── WorkContext.cs
├── Localization
│ ├── LocalizerService.cs
│ └── Resources.cs
├── Program.cs
├── Properties
│ └── launchSettings.json
├── Resources
│ └── Localization.Resources.en.resx
├── Startup.cs
├── WebApiStartup.cs
├── appsettings.Development.json
└── appsettings.json
├── MyApp.IdentityServer
├── Config.cs
├── MyApp.IdentityServer.csproj
├── Program.cs
├── Properties
│ └── launchSettings.json
├── Startup.cs
├── appsettings.Development.json
├── appsettings.json
└── tempkey.rsa
└── SeedWork
├── BinaryOrigin.SeedWork.Core
├── AppEngine.cs
├── BinaryOrigin.SeedWork.Core.csproj
├── Domain
│ ├── BaseEntity.cs
│ ├── IRepository.cs
│ ├── IResult.cs
│ ├── Maybe.cs
│ ├── Result.cs
│ ├── ValidationError.cs
│ ├── ValidationResponse.cs
│ └── ValueObject.cs
├── EngineContext.cs
├── Exceptions
│ ├── CommandValidationException.cs
│ └── GeneralException.cs
├── Extensions
│ ├── CommonExtensions.cs
│ ├── GuardExtensions.cs
│ ├── LinqExtensions.cs
│ └── StringExtensions.cs
├── Interfaces
│ ├── IAppFileProvider.cs
│ ├── IAppStartup.cs
│ ├── IDataProvider.cs
│ ├── IDependencyRegistration.cs
│ ├── IEngine.cs
│ ├── IMapperProfile.cs
│ ├── IPaginationService.cs
│ ├── ITypeAdapter.cs
│ ├── ITypeFinder.cs
│ └── IUnitOfWork.cs
└── Internal
│ ├── AppDomainTypeFinder.cs
│ ├── AppTypeFinder.cs
│ ├── ReflectionHelper.cs
│ └── TypeConverterHelper.cs
├── BinaryOrigin.SeedWork.Infrastructure
├── AppFileProvider.cs
└── BinaryOrigin.SeedWork.Infrastructure.csproj
├── BinaryOrigin.SeedWork.Messages
├── BinaryOrigin.SeedWork.Messages.csproj
├── Decorators
│ ├── DecoratorOrderAttribute.cs
│ ├── ICommandHandlerDecorator.cs
│ └── IQueryHandlerDecorator.cs
├── Extensions
│ └── RegistrationExtensions.cs
├── IBus.cs
├── ICommandHandler.cs
├── IEventHandler.cs
├── ILocalizerService.cs
├── IQueryHandler.cs
├── ISequenceEventHandler.cs
├── Internal
│ ├── Decorators
│ │ ├── LocalizationCommandHandlerDecorator.cs
│ │ ├── LocalizationQueryHandlerDecorator.cs
│ │ └── ValidateCommandHandlerDecorator.cs
│ ├── InMemoryBus.cs
│ ├── NullLocalizerService.cs
│ └── Resolvers
│ │ ├── HandlerResolver.cs
│ │ └── IHandlerResolver.cs
├── MessageTypes
│ ├── ICommand.cs
│ ├── IEvent.cs
│ └── IQuery.cs
└── Validation
│ └── ICommandValidationProvider.cs
├── BinaryOrigin.SeedWork.Persistence.MySql
├── BinaryOrigin.SeedWork.Persistence.Ef.MySql.csproj
├── BinaryOrigin.SeedWork.Persistence.MySql.csproj
├── MySqlDataProvider.cs
├── MySqlDataProviderExtensions.cs
└── MySqlDbExeptionParserProvider.cs
├── BinaryOrigin.SeedWork.Persistence.SqlServer
├── BinaryOrigin.SeedWork.Persistence.SqlServer.csproj
├── SqlDataProviderExtensions.cs
├── SqlServerDataProvider.cs
└── SqlServerDbExceptionParserProvider.cs
├── BinaryOrigin.SeedWork.Persistence
├── BinaryOrigin.SeedWork.Persistence.Ef.csproj
├── DbErrorMessagesConfiguration.cs
├── DbExceptionParser.cs
├── EfEntityTypeConfiguration.cs
├── EfObjectContext.cs
├── EfRepository.cs
├── Extensions
│ ├── DataProviderExtensions.cs
│ ├── DbContextExtensions.cs
│ └── EngineExtensions.cs
├── IDbContext.cs
├── IDbExceptionParserProvider.cs
├── IMappingConfiguration.cs
└── PaginationService.cs
└── BinaryOrigin.SeedWork.WebApi
├── Authorization
├── HasScopeHandler.cs
├── HasScopeRequirement.cs
├── ScopeAuthorizationPolicyProvider.cs
└── TestAuthenticationHandler.cs
├── BinaryOrigin.SeedWork.WebApi.csproj
├── Controllers
└── AppController.cs
├── ErrorHandlingMiddleware.cs
├── Extensions
├── EngineExtensions.cs
└── ServicesExtensions.cs
├── IWebApiEngine.cs
├── IWebApiStartup.cs
├── Mapping
├── AutoMapperConfiguration.cs
└── AutoMapperTypeAdapter.cs
├── ModelBinders
├── QueryModelBinder.cs
├── QueryModelBinderProvider.cs
├── RouteDataComplexTypeModelBinder.cs
└── RouteDataComplexTypeModelBinderProvider.cs
├── Validations
└── FluentValidationProvider.cs
└── WebApiEngine.cs
/.editorconfig:
--------------------------------------------------------------------------------
1 | [*.cs]
2 |
3 | # ASP0001: Authorization middleware is incorrectly configured.
4 | dotnet_diagnostic.ASP0001.severity = error
5 |
6 | # EF1001: Internal EF Core API usage.
7 | dotnet_diagnostic.EF1001.severity = warning
8 |
--------------------------------------------------------------------------------
/.github/workflows/BuildStatus.yml:
--------------------------------------------------------------------------------
1 | name: Build Status
2 |
3 | on: [push]
4 |
5 | jobs:
6 | build:
7 |
8 | runs-on: windows-latest
9 |
10 | steps:
11 | - uses: actions/checkout@v1
12 | - name: Setup .NET Core
13 | uses: actions/setup-dotnet@v1
14 | with:
15 | dotnet-version: 3.1.100
16 | - name: Build with dotnet
17 | run: dotnet build --configuration Release
18 | - name: run tests
19 | run: dotnet test
20 |
21 |
22 |
23 |
--------------------------------------------------------------------------------
/.github/workflows/deploy.yml:
--------------------------------------------------------------------------------
1 | name: .NET Core deploy
2 |
3 | on:
4 | push:
5 | # Sequence of patterns matched against refs/heads
6 | branches:
7 | - master # Push events on master branch
8 |
9 | jobs:
10 | build:
11 |
12 | runs-on: windows-latest
13 |
14 | steps:
15 | - uses: actions/checkout@v1
16 | - name: Setup .NET Core
17 | uses: actions/setup-dotnet@v1
18 | with:
19 | dotnet-version: 3.1.100
20 | - name: Build with dotnet
21 | run: dotnet build --configuration Release
22 | - name: run tests
23 | run: dotnet test
24 | - name: nuget pack
25 | run: dotnet pack --configuration Release
26 | - name: Install Nuget Client
27 | uses: warrenbuckley/Setup-Nuget@v1
28 | - name: setApiKey
29 | run: nuget setApiKey ${{ secrets.nuget_key }} -s https://api.nuget.org/v3/index.json
30 | - name: Push generated package to GitHub registry
31 | run: nuget push **.nupkg -Source "https://api.nuget.org/v3/index.json" -SkipDuplicate -NoSymbols
32 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 |
2 | #ignore thumbnails created by windows
3 | Thumbs.db
4 | #Ignore files build by Visual Studio
5 | *.obj
6 | *.pdb
7 | *.user
8 | *.aps
9 | *.pch
10 | *.vspscc
11 | *_i.c
12 | *_p.c
13 | *.ncb
14 | *.suo
15 | *.tlb
16 | *.tlh
17 | *.bak
18 | *.cache
19 | *.ilk
20 | *.log
21 | [Bb]in
22 | [Dd]ebug*/
23 | *.lib
24 | *.sbr
25 | obj/
26 | [Rr]elease*/
27 | _ReSharper*/
28 | [Tt]est[Rr]esult*
29 | packages/*
30 | .vs/
31 | /SPA/.sonarqube
32 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # Monolithic architecture based on CQRS and DDD principles
2 | This repository presents an approach on how to build an application using Monolithic architecture, ASP.NET Core, EntityFrameworkCore, Identity Server, CQRS, DDD etc.
3 |
4 | 
5 |
6 | ## Packages
7 | | Package | Latest Stable |
8 | | --- | --- |
9 | | [BinaryOrigin.SeedWork.Core](https://www.nuget.org/packages/BinaryOrigin.SeedWork.Core) | [](https://www.nuget.org/packages/BinaryOrigin.SeedWork.Core) |
10 | | [BinaryOrigin.SeedWork.Infrastructure](https://www.nuget.org/packages/BinaryOrigin.SeedWork.Infrastructure) | [](https://www.nuget.org/packages/BinaryOrigin.SeedWork.Infrastructure) |
11 | | [BinaryOrigin.SeedWork.Messages](https://www.nuget.org/packages/BinaryOrigin.SeedWork.Messages) | [](https://www.nuget.org/packages/BinaryOrigin.SeedWork.Messages) |
12 | | [BinaryOrigin.SeedWork.Persistence.Ef](https://www.nuget.org/packages/BinaryOrigin.SeedWork.Persistence.Ef) | [](https://www.nuget.org/packages/BinaryOrigin.SeedWork.Persistence.Ef) |
13 | | [BinaryOrigin.SeedWork.WebApi](https://www.nuget.org/packages/BinaryOrigin.SeedWork.WebApi) | [](https://www.nuget.org/packages/BinaryOrigin.SeedWork.WebApi) |
14 |
15 | ## Providers
16 | | Package | Latest Stable |
17 | | --- | --- |
18 | | [BinaryOrigin.SeedWork.Persistence.MySql](https://www.nuget.org/packages/BinaryOrigin.SeedWork.Persistence.MySql) | [](https://www.nuget.org/packages/BinaryOrigin.SeedWork.Persistence.MySql) |
19 | | [BinaryOrigin.SeedWork.Persistence.SqlServer](https://www.nuget.org/packages/BinaryOrigin.SeedWork.Persistence.SqlServer) | [](https://www.nuget.org/packages/BinaryOrigin.SeedWork.Persistence.SqlServer) |
20 |
21 | # Getting started:
22 |
23 | > 1 - Ensure that identity server is started
24 | > 2 - Use this request body for generating a token:
25 | >> client_id:clientId
26 | >> client_secret:secret
27 | >> scope:myApi profile openid
28 | >> username:getson
29 | >> password:password
30 | >> grant_type:password
31 |
32 | > 3 - Copy access token and paste it to swagger
33 | > 4 - Enjoy debugging.
34 |
35 | ## Your feedback is welcomed :)
36 |
--------------------------------------------------------------------------------
/Tests/App.WebApi.IntegrationTest/App.WebApi.IntegrationTest.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 | 1591
9 |
10 |
11 |
12 | netcoreapp3.1
13 | false
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 | all
22 | runtime; build; native; contentfiles; analyzers
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
47 | Always
48 |
49 |
50 |
51 |
52 |
--------------------------------------------------------------------------------
/Tests/App.WebApi.IntegrationTest/AppControllerTests.cs:
--------------------------------------------------------------------------------
1 | using App.WebApi.IntegrationTest.Infrastructure;
2 |
3 | namespace App.WebApi.IntegrationTest
4 | {
5 | public class AppControllerTests : ControllerTestBase
6 | {
7 | private readonly TestServerFixture _fixture;
8 |
9 | public AppControllerTests(TestServerFixture fixture)
10 | : base(fixture)
11 | {
12 | _fixture = fixture;
13 | }
14 | }
15 | }
--------------------------------------------------------------------------------
/Tests/App.WebApi.IntegrationTest/Infrastructure/ClientSerializer.cs:
--------------------------------------------------------------------------------
1 | using Newtonsoft.Json;
2 | using Newtonsoft.Json.Serialization;
3 | using System;
4 |
5 | namespace App.WebApi.IntegrationTest.Infrastructure
6 | {
7 | public static class ClientSerializer
8 | {
9 | static ClientSerializer()
10 | {
11 | JsonConvert.DefaultSettings = () => new JsonSerializerSettings
12 | {
13 | NullValueHandling = NullValueHandling.Ignore,
14 | ContractResolver = new CamelCasePropertyNamesContractResolver()
15 | };
16 | }
17 |
18 | public static T Deserialize(string jsonString)
19 | {
20 | try
21 | {
22 | return JsonConvert.DeserializeObject(jsonString);
23 | }
24 | catch (Exception)
25 | {
26 | // ignored
27 | }
28 | return default(T);
29 | }
30 |
31 | public static string Serialize(T @object)
32 | {
33 | return JsonConvert.SerializeObject(@object);
34 | }
35 | }
36 | }
--------------------------------------------------------------------------------
/Tests/App.WebApi.IntegrationTest/Infrastructure/ControllerTestBase.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Linq;
4 | using System.Net.Http;
5 | using System.Net.Http.Headers;
6 | using System.Threading.Tasks;
7 | using System.Web;
8 | using Xunit;
9 | using HttpMethod = System.Net.Http.HttpMethod;
10 |
11 | namespace App.WebApi.IntegrationTest.Infrastructure
12 | {
13 | public static class Extensions
14 | {
15 | public static async Task GetObjectAsync(this HttpResponseMessage responseMessage)
16 | {
17 | var response = await responseMessage.Content.ReadAsStringAsync();
18 |
19 | return ClientSerializer.Deserialize(response);
20 | }
21 |
22 | public static async Task> GetCollectionAsync(this HttpResponseMessage responseMessage)
23 | {
24 | var response = await responseMessage.Content.ReadAsStringAsync();
25 | return ClientSerializer.Deserialize>(response);
26 | }
27 |
28 | public static T GetObject(this HttpResponseMessage responseMessage)
29 | {
30 | var response = responseMessage.Content.ReadAsStringAsync().Result;
31 |
32 | return ClientSerializer.Deserialize(response);
33 | }
34 |
35 | public static List GetCollection(this HttpResponseMessage responseMessage)
36 | {
37 | var response = responseMessage.Content.ReadAsStringAsync().Result;
38 | return ClientSerializer.Deserialize>(response);
39 | }
40 |
41 | public static HttpContent ToJsonContent(this T @object)
42 | {
43 | var jsonString = ClientSerializer.Serialize(@object);
44 | return new StringContent(jsonString, System.Text.Encoding.UTF8, "application/json");
45 | }
46 | }
47 |
48 | public class ControllerTestBase : IClassFixture
49 | {
50 | private readonly TestServerFixture _fixture;
51 |
52 | public ControllerTestBase(TestServerFixture fixture)
53 | {
54 | _fixture = fixture;
55 | }
56 |
57 | protected async Task GetAsync(string url)
58 | {
59 | return await _fixture.Client.GetAsync(url);
60 | }
61 |
62 | protected async Task PutAsync(string url, T @object)
63 | {
64 | var requestMessage = new HttpRequestMessage(HttpMethod.Put, url)
65 | {
66 | Content = @object.ToJsonContent()
67 | };
68 | return await _fixture.Client.SendAsync(requestMessage);
69 | }
70 |
71 | protected async Task PostAsync(string url, T @object)
72 | {
73 | var requestMessage = new HttpRequestMessage(HttpMethod.Post, url)
74 | {
75 | Content = @object.ToJsonContent()
76 | };
77 | return await _fixture.Client.SendAsync(requestMessage);
78 | }
79 |
80 | protected async Task GetWithHeadersAsync(string url, Dictionary headers, T @object)
81 | {
82 | var requestMessage = new HttpRequestMessage(HttpMethod.Get, url)
83 | {
84 | Content = @object.ToJsonContent()
85 | };
86 |
87 | foreach (var header in headers)
88 | {
89 | if (header.Value.Equals("Authorization"))
90 | {
91 | var authorization = new AuthenticationHeaderValue("Bearer", header.Value);
92 | requestMessage.Headers.Authorization = authorization;
93 | }
94 | else
95 | {
96 | requestMessage.Headers.Add(header.Key, header.Value);
97 | }
98 | }
99 |
100 | return await _fixture.Client.SendAsync(requestMessage);
101 | }
102 |
103 | protected async Task DeleteAsync(string url, T @object)
104 | {
105 | var requestMessage = new HttpRequestMessage(HttpMethod.Delete, url + "?" + GetQueryString(@object));
106 | //{
107 | // Content = @object.ToJsonContent()
108 | //};
109 |
110 | return await _fixture.Client.SendAsync(requestMessage);
111 | }
112 |
113 | public string GetQueryString(object obj)
114 | {
115 | var properties = from p in obj.GetType().GetProperties()
116 | where p.GetValue(obj, null) != null
117 | select p.Name + "=" + HttpUtility.UrlEncode(p.GetValue(obj, null).ToString());
118 |
119 | return String.Join("&", properties.ToArray());
120 | }
121 |
122 | protected async Task DeleteAsync(string url)
123 | {
124 | return await _fixture.Client.DeleteAsync(url);
125 | }
126 | }
127 | }
--------------------------------------------------------------------------------
/Tests/App.WebApi.IntegrationTest/Infrastructure/TestServerFixture.cs:
--------------------------------------------------------------------------------
1 | using Microsoft.AspNetCore.Hosting;
2 | using Microsoft.AspNetCore.TestHost;
3 | using System;
4 | using System.Net.Http;
5 |
6 | namespace App.WebApi.IntegrationTest.Infrastructure
7 | {
8 | public sealed class TestServerFixture : IDisposable
9 | {
10 | public HttpClient Client { get; }
11 | private static readonly TestServer TestServer;
12 |
13 | static TestServerFixture()
14 | {
15 | var builder = new WebHostBuilder()
16 | .UseStartup();
17 |
18 | TestServer = new TestServer(builder);
19 | }
20 |
21 | public TestServerFixture()
22 | {
23 | Client = TestServer.CreateClient();
24 | }
25 |
26 | public void Dispose()
27 | {
28 | Client.Dispose();
29 | }
30 | }
31 | }
--------------------------------------------------------------------------------
/Tests/App.WebApi.IntegrationTest/ProjectControllerTests.cs:
--------------------------------------------------------------------------------
1 | using App.Application.Commands.ProjectBC;
2 | using App.Application.Queries.ProjectBC;
3 | using App.WebApi.IntegrationTest.Infrastructure;
4 | using BinaryOrigin.SeedWork.Core;
5 | using FluentAssertions;
6 | using System;
7 | using System.Collections.Generic;
8 | using System.Linq;
9 | using System.Net;
10 | using System.Threading.Tasks;
11 | using Xunit;
12 |
13 | namespace App.WebApi.IntegrationTest
14 | {
15 | public class ProjectControllerTests : ControllerTestBase
16 | {
17 | private const string _apiEndpoint = "api/projects";
18 | private readonly TestServerFixture _fixture;
19 |
20 | public ProjectControllerTests(TestServerFixture fixture)
21 | : base(fixture)
22 | {
23 | _fixture = fixture;
24 | Init();
25 | }
26 |
27 | [Fact]
28 | public async Task Should_return_projects_with_success()
29 | {
30 | var projects = await GetTop20();
31 | projects.Should().NotBeNull();
32 | }
33 |
34 | [Fact]
35 | public async Task Should_return_project_by_id_with_success()
36 | {
37 | var projects = await GetTop20();
38 | var firstProject = projects.FirstOrDefault();
39 |
40 | var response = await GetAsync($"{_apiEndpoint}/{firstProject.Id}");
41 |
42 | response.StatusCode.Should().Be(HttpStatusCode.OK);
43 | var getProject = response.GetObject();
44 |
45 | response.GetObjectAsync().Should().NotBeNull();
46 | getProject.Id.Should().Be(firstProject.Id);
47 | getProject.Name.Should().Be(firstProject.Name);
48 | getProject.Description.Should().Be(firstProject.Description);
49 | }
50 |
51 | [Fact]
52 | public async Task Project_Should_Be_Created()
53 | {
54 | var addProject = new AddProject
55 | {
56 | Name = "title 1",
57 | Description = "Description 1"
58 | };
59 |
60 | var response = await PostAsync(_apiEndpoint, addProject);
61 |
62 | response.StatusCode.Should().Be(HttpStatusCode.Created);
63 | var createdProject = response.GetObject();
64 |
65 | createdProject.Should().NotBe(Guid.Empty);
66 | }
67 |
68 | [Fact]
69 | public async Task Project_Should_Be_Updated()
70 | {
71 | var project = (await GetTop20()).First();
72 | var updateProjectCommand = new UpdateProject
73 | {
74 | Id = project.Id,
75 | Name = "Name updated",
76 | Description = "Description updated"
77 | };
78 | var response = await PutAsync(_apiEndpoint, updateProjectCommand);
79 | response.EnsureSuccessStatusCode();
80 |
81 | var updatedProject = (await GetTop20()).FirstOrDefault(x => x.Id == project.Id);
82 |
83 | updatedProject.Description.Should().Be(updateProjectCommand.Description);
84 | updatedProject.Name.Should().Be(updateProjectCommand.Name);
85 | }
86 |
87 | [Fact]
88 | public async Task Project_Should_not_Be_Updated()
89 | {
90 | var updateProjectCommand = new UpdateProject
91 | {
92 | Id = Guid.Empty,
93 | Name = "Name updated",
94 | Description = "Description updated"
95 | };
96 |
97 | var putResponse = await PutAsync(_apiEndpoint, updateProjectCommand);
98 | putResponse.StatusCode.Should().Be(HttpStatusCode.BadRequest);
99 | }
100 |
101 | [Fact]
102 | public async Task Project_should_be_deleted()
103 | {
104 | var project = new AddProject
105 | {
106 | Name = "Name 3",
107 | Description = "Description 3"
108 | };
109 |
110 | var response = await PostAsync(_apiEndpoint, project);
111 | response.StatusCode.Should().Be(HttpStatusCode.Created);
112 | var projectId = response.GetObject();
113 |
114 | var deleteCommand = new DeleteProject(projectId);
115 | var deleteResponse = await DeleteAsync($"{_apiEndpoint}/{deleteCommand.Id}");
116 | deleteResponse.StatusCode.Should().Be(HttpStatusCode.NoContent);
117 | }
118 |
119 | [Fact]
120 | public async Task Project_should_not_be_deleted()
121 | {
122 | var deleteResponse = await DeleteAsync($"{_apiEndpoint}/{Guid.Empty}");
123 | deleteResponse.StatusCode.Should().Be(HttpStatusCode.BadRequest);
124 | }
125 |
126 | private async Task> GetTop20()
127 | {
128 | var result = await GetAsync($"{_apiEndpoint}?pageSize=20");
129 | result.EnsureSuccessStatusCode();
130 | return result.GetObject>().Data;
131 | }
132 |
133 | private void Init()
134 | {
135 | var project = new AddProject
136 | {
137 | Name = "Name 3",
138 | Description = "Description 3"
139 | };
140 |
141 | var response = PostAsync(_apiEndpoint, project).Result;
142 | response.StatusCode.Should().Be(HttpStatusCode.Created);
143 | }
144 | }
145 | }
--------------------------------------------------------------------------------
/Tests/App.WebApi.IntegrationTest/Properties/launchSettings.json:
--------------------------------------------------------------------------------
1 | {
2 | "iisSettings": {
3 | "windowsAuthentication": false,
4 | "anonymousAuthentication": true,
5 | "iisExpress": {
6 | "applicationUrl": "http://localhost:56836",
7 | "sslPort": 44344
8 | }
9 | },
10 | "profiles": {
11 | "IIS Express": {
12 | "commandName": "IISExpress",
13 | "launchBrowser": true,
14 | "environmentVariables": {
15 | "ASPNETCORE_ENVIRONMENT": "Testing"
16 | }
17 | },
18 | "App.WebApi.IntegrationTest": {
19 | "commandName": "Project",
20 | "launchBrowser": true,
21 | "environmentVariables": {
22 | "ASPNETCORE_ENVIRONMENT": "Development"
23 | },
24 | "applicationUrl": "https://localhost:5001;http://localhost:5000"
25 | }
26 | }
27 | }
--------------------------------------------------------------------------------
/Tests/App.WebApi.IntegrationTest/appsettings.Development.json:
--------------------------------------------------------------------------------
1 | {
2 | "Logging": {
3 | "LogLevel": {
4 | "Default": "Debug",
5 | "System": "Information",
6 | "Microsoft": "Information"
7 | }
8 | }
9 | }
--------------------------------------------------------------------------------
/Tests/App.WebApi.IntegrationTest/appsettings.json:
--------------------------------------------------------------------------------
1 | {
2 | "Logging": {
3 | "LogLevel": {
4 | "Default": "Warning"
5 | }
6 | },
7 | "AllowedHosts": "*",
8 | "Db": {
9 | "Type": "InMemory"
10 | },
11 | "Auth": {
12 | "Authority": "http://localhost:5000",
13 | "Audience": "myApi",
14 | "RequireHttps": "false"
15 | },
16 | "IsTesting": "true"
17 | }
--------------------------------------------------------------------------------
/src/App.Application.Commands/App.Application.Commands.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | netcoreapp3.1
5 | false
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
--------------------------------------------------------------------------------
/src/App.Application.Commands/ProjectBC/AddProject.cs:
--------------------------------------------------------------------------------
1 | using BinaryOrigin.SeedWork.Messages;
2 | using System;
3 |
4 | namespace App.Application.Commands.ProjectBC
5 | {
6 | public sealed class AddProject : ICommand
7 | {
8 | public string Name { get; set; }
9 | public string Description { get; set; }
10 | }
11 | }
--------------------------------------------------------------------------------
/src/App.Application.Commands/ProjectBC/DeleteProject.cs:
--------------------------------------------------------------------------------
1 | using BinaryOrigin.SeedWork.Messages;
2 | using System;
3 |
4 | namespace App.Application.Commands.ProjectBC
5 | {
6 | public sealed class DeleteProject : ICommand
7 | {
8 | public DeleteProject()
9 | {
10 | }
11 |
12 | public DeleteProject(Guid id)
13 | {
14 | Id = id;
15 | }
16 |
17 | public Guid Id { get; set; }
18 | }
19 | }
--------------------------------------------------------------------------------
/src/App.Application.Commands/ProjectBC/Handlers/AddProjectHandler.cs:
--------------------------------------------------------------------------------
1 | using App.Core.Domain.ProjectBC;
2 | using BinaryOrigin.SeedWork.Core.Domain;
3 | using BinaryOrigin.SeedWork.Messages;
4 | using System;
5 | using System.Threading.Tasks;
6 |
7 | namespace App.Application.Commands.ProjectBC.Handlers
8 | {
9 | public sealed class AddProjectHandler : ICommandHandler
10 | {
11 | private readonly IProjectRepository _projectRepository;
12 |
13 | public AddProjectHandler(IProjectRepository projectRepository)
14 | {
15 | _projectRepository = projectRepository;
16 | }
17 |
18 | public async Task> HandleAsync(AddProject command)
19 | {
20 | var projectResult = Project.Create(Guid.NewGuid(),
21 | command.Name,
22 | command.Description);
23 | if (projectResult.IsFailure)
24 | {
25 | return Result.Fail(projectResult.Error);
26 | }
27 | await _projectRepository.CreateAsync(projectResult.Value);
28 |
29 | return Result.Ok(projectResult.Value.Id);
30 | }
31 | }
32 | }
--------------------------------------------------------------------------------
/src/App.Application.Commands/ProjectBC/Handlers/DeleteProjectHandler.cs:
--------------------------------------------------------------------------------
1 | using App.Core.Domain.ProjectBC;
2 | using BinaryOrigin.SeedWork.Core.Domain;
3 | using BinaryOrigin.SeedWork.Messages;
4 | using System.Threading.Tasks;
5 |
6 | namespace App.Application.Commands.ProjectBC.Handlers
7 | {
8 | public sealed class DeleteProjectHandler : ICommandHandler
9 | {
10 | private readonly IProjectRepository _projectRepository;
11 |
12 | public DeleteProjectHandler(IProjectRepository projectRepository)
13 | {
14 | _projectRepository = projectRepository;
15 | }
16 |
17 | public IProjectRepository ProjectRepository { get; }
18 |
19 | public async Task> HandleAsync(DeleteProject command)
20 | {
21 | var projectOrNothing = await _projectRepository.GetByIdAsync(command.Id);
22 | if (projectOrNothing.HasNoValue)
23 | {
24 | return Result.Ok(true);
25 | }
26 | await _projectRepository.DeleteAsync(projectOrNothing.Value);
27 | return Result.Ok(true);
28 | }
29 | }
30 | }
--------------------------------------------------------------------------------
/src/App.Application.Commands/ProjectBC/Handlers/UpdateProjectHandler.cs:
--------------------------------------------------------------------------------
1 | using App.Core;
2 | using App.Core.Domain.ProjectBC;
3 | using BinaryOrigin.SeedWork.Core.Domain;
4 | using BinaryOrigin.SeedWork.Messages;
5 | using System.Threading.Tasks;
6 |
7 | namespace App.Application.Commands.ProjectBC.Handlers
8 | {
9 | public sealed class UpdateProjectHandler : ICommandHandler
10 | {
11 | private readonly IProjectRepository _projectRepository;
12 |
13 | public UpdateProjectHandler(IProjectRepository projectRepository)
14 | {
15 | _projectRepository = projectRepository;
16 | }
17 |
18 | public async Task> HandleAsync(UpdateProject command)
19 | {
20 | var projectOrNothing = await _projectRepository.GetByIdAsync(command.Id);
21 | if (projectOrNothing.HasNoValue)
22 | {
23 | return Result.Fail(ErrorMessages.ProjectNotFound);
24 | }
25 | var result = projectOrNothing.Value.UpdateDetails(command.Name, command.Description);
26 | if (result.IsFailure)
27 | {
28 | return Result.Fail(result.Error);
29 | }
30 | await _projectRepository.UpdateAsync(projectOrNothing.Value);
31 | return Result.Ok(true);
32 | }
33 | }
34 | }
--------------------------------------------------------------------------------
/src/App.Application.Commands/ProjectBC/UpdateProject.cs:
--------------------------------------------------------------------------------
1 | using BinaryOrigin.SeedWork.Messages;
2 | using System;
3 |
4 | namespace App.Application.Commands.ProjectBC
5 | {
6 | public sealed class UpdateProject : ICommand
7 | {
8 | public Guid Id { get; set; }
9 |
10 | public string Name { get; set; }
11 |
12 | public string Description { get; set; }
13 | }
14 | }
--------------------------------------------------------------------------------
/src/App.Application.Commands/ProjectBC/Validators/CreateProjectValidator.cs:
--------------------------------------------------------------------------------
1 | using FluentValidation;
2 |
3 | namespace App.Application.Commands.ProjectBC.Validators
4 | {
5 | public class CreateProjectValidator : AbstractValidator
6 | {
7 | public CreateProjectValidator()
8 | {
9 | RuleFor(x => x.Name).NotEmpty();
10 | RuleFor(x => x.Description).NotEmpty();
11 | }
12 | }
13 | }
--------------------------------------------------------------------------------
/src/App.Application.Commands/ProjectBC/Validators/DeleteProjectValidator.cs:
--------------------------------------------------------------------------------
1 | using FluentValidation;
2 | using System;
3 |
4 | namespace App.Application.Commands.ProjectBC.Validators
5 | {
6 | public class DeleteProjectValidator : AbstractValidator
7 | {
8 | public DeleteProjectValidator()
9 | {
10 | RuleFor(x => x.Id).NotEqual(Guid.Empty);
11 | }
12 | }
13 | }
--------------------------------------------------------------------------------
/src/App.Application.Commands/ProjectBC/Validators/UpdateProjectValidator.cs:
--------------------------------------------------------------------------------
1 | using App.Core;
2 | using FluentValidation;
3 | using System;
4 |
5 | namespace App.Application.Commands.ProjectBC.Validators
6 | {
7 | public class UpdateProjectValidator : AbstractValidator
8 | {
9 | public UpdateProjectValidator()
10 | {
11 | RuleFor(x => x.Id)
12 | .NotEqual(Guid.Empty)
13 | .WithMessage(ErrorMessages.InvalidId);
14 | RuleFor(x => x.Name).NotEmpty();
15 | RuleFor(x => x.Description).NotEmpty();
16 | }
17 | }
18 | }
--------------------------------------------------------------------------------
/src/App.Application.Events/App.Application.Events.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | netcoreapp3.1
5 | false
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
--------------------------------------------------------------------------------
/src/App.Application.Events/Handlers/DummyProjectCreatedHandler.cs:
--------------------------------------------------------------------------------
1 | using BinaryOrigin.SeedWork.Messages;
2 | using System;
3 | using System.Threading.Tasks;
4 |
5 | namespace App.Application.Events.Handlers
6 | {
7 | internal class DummyProjectCreatedHandler : IEventHandler
8 | {
9 | public Task HandleAsync(ProjectCreated message)
10 | {
11 | Console.WriteLine($"DummyHandler: {message.Id} - {message.Name}");
12 | return Task.CompletedTask;
13 | }
14 | }
15 |
16 | internal class Dummy2ProjectCreatedHandler : IEventHandler
17 | {
18 | public Task HandleAsync(ProjectCreated message)
19 | {
20 | Console.WriteLine($"DummyHandler2: {message.Id} - {message.Name}");
21 | return Task.CompletedTask;
22 | }
23 | }
24 | }
--------------------------------------------------------------------------------
/src/App.Application.Events/ProjectCreated.cs:
--------------------------------------------------------------------------------
1 | using BinaryOrigin.SeedWork.Messages;
2 | using System;
3 |
4 | namespace App.Application.Events
5 | {
6 | public class ProjectCreated : IEvent
7 | {
8 | public Guid Id { get; set; }
9 | public string Name { get; set; }
10 | }
11 | }
--------------------------------------------------------------------------------
/src/App.Application.Queries/App.Application.Queries.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | netcoreapp3.1
5 | false
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
--------------------------------------------------------------------------------
/src/App.Application.Queries/App.Application.QueryHandlers.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | netcoreapp3.1
5 |
6 |
7 |
8 |
9 |
10 |
11 |
--------------------------------------------------------------------------------
/src/App.Application.Queries/MapDomainToQueryResult.cs:
--------------------------------------------------------------------------------
1 | using App.Application.Queries.ProjectBC;
2 | using App.Core.Domain.ProjectBC;
3 | using AutoMapper;
4 | using BinaryOrigin.SeedWork.Core;
5 |
6 | namespace App.Application.Queries
7 | {
8 | public class MapDomainToQueryResult : Profile, IMapperProfile
9 | {
10 | public int Order => 1;
11 |
12 | public MapDomainToQueryResult()
13 | {
14 | CreateMap();
15 | }
16 | }
17 | }
--------------------------------------------------------------------------------
/src/App.Application.Queries/ProjectBC/GetProject.cs:
--------------------------------------------------------------------------------
1 | using BinaryOrigin.SeedWork.Messages;
2 | using System;
3 |
4 | namespace App.Application.Queries.ProjectBC
5 | {
6 | public sealed class GetProject : IQuery
7 | {
8 | public Guid Id { get; set; }
9 | }
10 |
11 | public sealed class GetProjectResult
12 | {
13 | public Guid Id { get; set; }
14 |
15 | public string Name { get; set; }
16 |
17 | public string Description { get; set; }
18 | }
19 | }
--------------------------------------------------------------------------------
/src/App.Application.Queries/ProjectBC/GetProjects.cs:
--------------------------------------------------------------------------------
1 | using BinaryOrigin.SeedWork.Core;
2 | using BinaryOrigin.SeedWork.Messages;
3 |
4 | namespace App.Application.Queries.ProjectBC
5 | {
6 | public sealed class GetProjects : IQuery>
7 | {
8 | public int PageIndex { get; set; }
9 | public int PageSize { get; set; }
10 | }
11 | }
--------------------------------------------------------------------------------
/src/App.Application.Queries/ProjectBC/Handlers/GetProjectHandler.cs:
--------------------------------------------------------------------------------
1 | using App.Core;
2 | using App.Core.Domain.ProjectBC;
3 | using BinaryOrigin.SeedWork.Core;
4 | using BinaryOrigin.SeedWork.Core.Domain;
5 | using BinaryOrigin.SeedWork.Messages;
6 | using BinaryOrigin.SeedWork.Persistence.Ef;
7 | using Microsoft.EntityFrameworkCore;
8 | using System.Threading.Tasks;
9 |
10 | namespace App.Application.Queries.ProjectBC.Handlers
11 | {
12 | public class GetProjectHandler : IQueryHandler
13 | {
14 | private readonly IDbContext _dbContext;
15 | private readonly ITypeAdapter _typeAdapter;
16 |
17 | public GetProjectHandler(IDbContext dbContext, ITypeAdapter typeAdapter)
18 | {
19 | _dbContext = dbContext;
20 | _typeAdapter = typeAdapter;
21 | }
22 |
23 | public async Task> HandleAsync(GetProject queryModel)
24 | {
25 | var result = await _dbContext.Set()
26 | .SingleOrDefaultAsync(p => p.Id == queryModel.Id);
27 |
28 | if (result == null)
29 | {
30 | return Result.Fail(ErrorMessages.ProjectNotFound);
31 | }
32 |
33 | return Result.Ok(_typeAdapter.Adapt(result));
34 | }
35 | }
36 | }
--------------------------------------------------------------------------------
/src/App.Application.Queries/ProjectBC/Handlers/GetProjectsHandler.cs:
--------------------------------------------------------------------------------
1 | using App.Core.Domain.ProjectBC;
2 | using BinaryOrigin.SeedWork.Core;
3 | using BinaryOrigin.SeedWork.Core.Domain;
4 | using BinaryOrigin.SeedWork.Messages;
5 | using BinaryOrigin.SeedWork.Persistence.Ef;
6 | using Microsoft.EntityFrameworkCore;
7 | using System.Threading.Tasks;
8 |
9 | namespace App.Application.Queries.ProjectBC.Handlers
10 | {
11 | public class GetProjectsHandler : IQueryHandler>
12 | {
13 | private readonly IDbContext _dbContext;
14 | private readonly IPaginationService _paginationService;
15 |
16 | public GetProjectsHandler(IDbContext dbContext,
17 | IPaginationService paginationService)
18 | {
19 | _dbContext = dbContext;
20 | _paginationService = paginationService;
21 | }
22 |
23 | public async Task>> HandleAsync(GetProjects queryModel)
24 | {
25 | var baseQuery = _dbContext.Set().AsNoTracking();
26 |
27 | var result = await _paginationService.PaginateAsync
28 | (
29 | baseQuery,
30 | queryModel.PageIndex,
31 | queryModel.PageSize
32 | );
33 |
34 | return Result.Ok(result);
35 | }
36 | }
37 | }
--------------------------------------------------------------------------------
/src/App.Core/App.Core.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | netcoreapp3.1
5 | App.Core
6 | App.Core
7 | false
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
--------------------------------------------------------------------------------
/src/App.Core/Domain/ProjectBC/IProjectRepository.cs:
--------------------------------------------------------------------------------
1 | using BinaryOrigin.SeedWork.Core.Domain;
2 |
3 | namespace App.Core.Domain.ProjectBC
4 | {
5 | public interface IProjectRepository : IRepository
6 | {
7 | }
8 | }
--------------------------------------------------------------------------------
/src/App.Core/Domain/ProjectBC/Project.cs:
--------------------------------------------------------------------------------
1 | using BinaryOrigin.SeedWork.Core;
2 | using BinaryOrigin.SeedWork.Core.Domain;
3 | using System;
4 |
5 | namespace App.Core.Domain.ProjectBC
6 | {
7 | public sealed class Project : BaseEntity
8 | {
9 | public string Name { get; private set; }
10 | public string Description { get; private set; }
11 |
12 | public static Result Create(Guid id, string name, string description)
13 | {
14 | if (id == Guid.Empty)
15 | {
16 | return Result.Fail(ErrorMessages.InvalidId);
17 | }
18 | if (name.IsNullOrEmpty())
19 | {
20 | return Result.Fail(ErrorMessages.NameShouldNotBeEmpty);
21 | }
22 | var project = new Project
23 | {
24 | Id = id,
25 | Name = name,
26 | Description = description
27 | };
28 | return Result.Ok(project);
29 | }
30 |
31 | public Result UpdateDetails(string name, string description)
32 | {
33 | if (name.IsNullOrEmpty())
34 | {
35 | return Result.Fail(ErrorMessages.NameShouldNotBeEmpty);
36 | }
37 | this.Name = name;
38 | Description = description;
39 |
40 | return Result.Ok();
41 | }
42 | }
43 | }
--------------------------------------------------------------------------------
/src/App.Core/ErrorMessages.cs:
--------------------------------------------------------------------------------
1 | namespace App.Core
2 | {
3 | public static class ErrorMessages
4 | {
5 | public const string ProjectNotFound = "PROJECT_NOT_FOUND";
6 | public const string NameShouldNotBeEmpty = "NAME_SHOULD_NOT_BE_EMPTY";
7 |
8 | public const string InvalidId = "INVALID_ID";
9 | public const string GenericUniqueError = @"{0} should be unique";
10 | public const string GenericCombinationUniqueError = @"Combination of {0} should be unique";
11 |
12 | }
13 | }
--------------------------------------------------------------------------------
/src/App.Core/IWorkContext.cs:
--------------------------------------------------------------------------------
1 | namespace App.Core
2 | {
3 | public interface IWorkContext
4 | {
5 | string UserId { get; }
6 | string UserName { get; }
7 | string FullName { get; }
8 | string Email { get; }
9 | }
10 | }
--------------------------------------------------------------------------------
/src/App.Infrastructure.Persistence.SqlServer/App.Infrastructure.Persistence.SqlServer.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | netcoreapp3.1
5 | false
6 |
7 |
8 |
9 |
10 |
11 | all
12 | runtime; build; native; contentfiles; analyzers; buildtransitive
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
--------------------------------------------------------------------------------
/src/App.Infrastructure.Persistence.SqlServer/Context/AppDbContext.cs:
--------------------------------------------------------------------------------
1 | using BinaryOrigin.SeedWork.Persistence.Ef;
2 | using Microsoft.EntityFrameworkCore;
3 |
4 | namespace App.Infrastructure.Persistence.SqlServer.Context
5 | {
6 | public class AppDbContext : EfObjectContext
7 | {
8 | public AppDbContext(DbContextOptions dbContextOptions)
9 | : base(dbContextOptions)
10 | {
11 | }
12 | }
13 | }
--------------------------------------------------------------------------------
/src/App.Infrastructure.Persistence.SqlServer/Context/DbContextFactory.cs:
--------------------------------------------------------------------------------
1 | using Microsoft.EntityFrameworkCore;
2 | using Microsoft.EntityFrameworkCore.Design;
3 | using Microsoft.Extensions.Configuration;
4 | using System;
5 |
6 | namespace App.Infrastructure.Persistence.SqlServer.Context
7 | {
8 | public class AppObjectContextFactory : IDesignTimeDbContextFactory
9 | {
10 | ///
11 | /// create design time DbContext
12 | ///
13 | ///
14 | ///
15 | public virtual AppDbContext CreateDbContext(string[] args)
16 | {
17 | var optionsBuilder = new DbContextOptionsBuilder();
18 | var configuration = new ConfigurationBuilder()
19 | .SetBasePath(AppContext.BaseDirectory)
20 | .AddJsonFile("appsettings.json", optional: false, reloadOnChange: true)
21 | .Build();
22 | optionsBuilder.UseSqlServer(configuration["Db:ConnectionString"], x =>
23 | {
24 | x.MigrationsAssembly(this.GetType().Assembly.GetName().Name);
25 | x.MigrationsHistoryTable("MigrationHistory");
26 | });
27 | return new AppDbContext(optionsBuilder.Options);
28 | }
29 | }
30 | }
--------------------------------------------------------------------------------
/src/App.Infrastructure.Persistence.SqlServer/Migrations/20200218115151_initial.Designer.cs:
--------------------------------------------------------------------------------
1 | //
2 | using System;
3 | using App.Infrastructure.Persistence.SqlServer.Context;
4 | using Microsoft.EntityFrameworkCore;
5 | using Microsoft.EntityFrameworkCore.Infrastructure;
6 | using Microsoft.EntityFrameworkCore.Metadata;
7 | using Microsoft.EntityFrameworkCore.Migrations;
8 | using Microsoft.EntityFrameworkCore.Storage.ValueConversion;
9 |
10 | namespace App.Infrastructure.Persistence.SqlServer.Migrations
11 | {
12 | [DbContext(typeof(AppDbContext))]
13 | [Migration("20200218115151_initial")]
14 | partial class initial
15 | {
16 | protected override void BuildTargetModel(ModelBuilder modelBuilder)
17 | {
18 | #pragma warning disable 612, 618
19 | modelBuilder
20 | .HasAnnotation("ProductVersion", "3.1.1")
21 | .HasAnnotation("Relational:MaxIdentifierLength", 128)
22 | .HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn);
23 |
24 | modelBuilder.Entity("App.Core.Domain.ProjectBC.Project", b =>
25 | {
26 | b.Property("Id")
27 | .ValueGeneratedOnAdd()
28 | .HasColumnType("uniqueidentifier");
29 |
30 | b.Property("Description")
31 | .HasColumnType("nvarchar(500)")
32 | .HasMaxLength(500);
33 |
34 | b.Property("Name")
35 | .IsRequired()
36 | .HasColumnType("nvarchar(300)")
37 | .HasMaxLength(300);
38 |
39 | b.HasKey("Id");
40 |
41 | b.HasIndex("Name")
42 | .IsUnique();
43 |
44 | b.ToTable("Projects");
45 | });
46 | #pragma warning restore 612, 618
47 | }
48 | }
49 | }
50 |
--------------------------------------------------------------------------------
/src/App.Infrastructure.Persistence.SqlServer/Migrations/20200218115151_initial.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using Microsoft.EntityFrameworkCore.Migrations;
3 |
4 | namespace App.Infrastructure.Persistence.SqlServer.Migrations
5 | {
6 | public partial class initial : Migration
7 | {
8 | protected override void Up(MigrationBuilder migrationBuilder)
9 | {
10 | migrationBuilder.CreateTable(
11 | name: "Projects",
12 | columns: table => new
13 | {
14 | Id = table.Column(nullable: false),
15 | Name = table.Column(maxLength: 300, nullable: false),
16 | Description = table.Column(maxLength: 500, nullable: true)
17 | },
18 | constraints: table =>
19 | {
20 | table.PrimaryKey("PK_Projects", x => x.Id);
21 | });
22 |
23 | migrationBuilder.CreateIndex(
24 | name: "IX_Projects_Name",
25 | table: "Projects",
26 | column: "Name",
27 | unique: true);
28 | }
29 |
30 | protected override void Down(MigrationBuilder migrationBuilder)
31 | {
32 | migrationBuilder.DropTable(
33 | name: "Projects");
34 | }
35 | }
36 | }
37 |
--------------------------------------------------------------------------------
/src/App.Infrastructure.Persistence.SqlServer/Migrations/AppDbContextModelSnapshot.cs:
--------------------------------------------------------------------------------
1 | //
2 | using System;
3 | using App.Infrastructure.Persistence.SqlServer.Context;
4 | using Microsoft.EntityFrameworkCore;
5 | using Microsoft.EntityFrameworkCore.Infrastructure;
6 | using Microsoft.EntityFrameworkCore.Metadata;
7 | using Microsoft.EntityFrameworkCore.Storage.ValueConversion;
8 |
9 | namespace App.Infrastructure.Persistence.SqlServer.Migrations
10 | {
11 | [DbContext(typeof(AppDbContext))]
12 | partial class AppDbContextModelSnapshot : ModelSnapshot
13 | {
14 | protected override void BuildModel(ModelBuilder modelBuilder)
15 | {
16 | #pragma warning disable 612, 618
17 | modelBuilder
18 | .HasAnnotation("ProductVersion", "3.1.1")
19 | .HasAnnotation("Relational:MaxIdentifierLength", 128)
20 | .HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn);
21 |
22 | modelBuilder.Entity("App.Core.Domain.ProjectBC.Project", b =>
23 | {
24 | b.Property("Id")
25 | .ValueGeneratedOnAdd()
26 | .HasColumnType("uniqueidentifier");
27 |
28 | b.Property("Description")
29 | .HasColumnType("nvarchar(500)")
30 | .HasMaxLength(500);
31 |
32 | b.Property("Name")
33 | .IsRequired()
34 | .HasColumnType("nvarchar(300)")
35 | .HasMaxLength(300);
36 |
37 | b.HasKey("Id");
38 |
39 | b.HasIndex("Name")
40 | .IsUnique();
41 |
42 | b.ToTable("Projects");
43 | });
44 | #pragma warning restore 612, 618
45 | }
46 | }
47 | }
48 |
--------------------------------------------------------------------------------
/src/App.Infrastructure.Persistence.SqlServer/ProjectBC/Configurations/ConfigProject.cs:
--------------------------------------------------------------------------------
1 | using App.Core.Domain.ProjectBC;
2 | using BinaryOrigin.SeedWork.Persistence.Ef;
3 | using Microsoft.EntityFrameworkCore.Metadata.Builders;
4 |
5 | namespace App.Infrastructure.Persistence.SqlServer.ProjectBC.Configurations
6 | {
7 | internal class ConfigProject : EfEntityTypeConfiguration
8 | {
9 | public override void PostConfigure(EntityTypeBuilder builder)
10 | {
11 | builder.HasKey(x => x.Id);
12 | builder.Property(x => x.Name).HasMaxLength(300).IsRequired();
13 | builder.Property(x => x.Description).HasMaxLength(500);
14 |
15 | builder.HasIndex(x => x.Name).IsUnique();
16 | }
17 | }
18 | }
--------------------------------------------------------------------------------
/src/App.Infrastructure.Persistence.SqlServer/ProjectBC/ProjectRepository.cs:
--------------------------------------------------------------------------------
1 | using App.Core.Domain.ProjectBC;
2 | using BinaryOrigin.SeedWork.Persistence.Ef;
3 |
4 | namespace App.Infrastructure.Persistence.SqlServer.ProjectBC
5 | {
6 | public sealed class ProjectRepository : EfRepository, IProjectRepository
7 | {
8 | public ProjectRepository(IDbContext dbContext)
9 | : base(dbContext)
10 | {
11 | }
12 | }
13 | }
--------------------------------------------------------------------------------
/src/App.WebApi/App.WebApi.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | netcoreapp3.1
5 | App.WebApi
6 | App.WebApi
7 | 1591
8 | false
9 |
10 |
11 | App.WebApi.xml
12 |
13 |
14 |
15 |
16 |
17 |
18 | all
19 | runtime; build; native; contentfiles; analyzers; buildtransitive
20 |
21 |
22 | all
23 | runtime; build; native; contentfiles; analyzers; buildtransitive
24 |
25 |
26 | all
27 | runtime; build; native; contentfiles; analyzers; buildtransitive
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
--------------------------------------------------------------------------------
/src/App.WebApi/App.WebApi.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | App.WebApi
5 |
6 |
7 |
8 | [FromRoute] and [FromQuery] is added in order to generate swagger documentaion correctly until it will be fixed
9 | in swagger configuration
10 |
11 | This controller contains methods for the projects
12 |
13 |
14 |
15 |
16 | the controller cunstructor
17 |
18 |
19 |
20 |
21 |
22 | Get All projects
23 |
24 |
25 |
26 |
27 |
28 | get project by Id
29 |
30 |
31 |
32 |
33 |
34 |
35 | Add a project
36 |
37 |
38 |
39 |
40 |
41 |
42 | Update a project
43 |
44 |
45 |
46 |
47 |
48 |
49 | Delete a project
50 |
51 |
52 |
53 |
54 |
55 |
56 | Add entity framework services
57 |
58 |
59 |
60 |
61 |
62 |
63 | Configure Swagger
64 |
65 |
66 |
67 |
68 |
69 | Add services to the application and configure service provider
70 |
71 | Collection of service descriptors
72 |
73 |
74 |
75 | Configure the application HTTP request pipeline
76 |
77 |
78 |
79 |
80 |
81 |
82 |
83 |
84 |
85 |
86 |
87 |
88 |
89 |
90 |
91 |
92 |
93 |
--------------------------------------------------------------------------------
/src/App.WebApi/Controllers/ProjectsController.cs:
--------------------------------------------------------------------------------
1 | using App.Application.Commands.ProjectBC;
2 | using App.Application.Events;
3 | using App.Application.Queries.ProjectBC;
4 | using BinaryOrigin.SeedWork.Messages;
5 | using BinaryOrigin.SeedWork.WebApi.Controllers;
6 | using Microsoft.AspNetCore.Authorization;
7 | using Microsoft.AspNetCore.Mvc;
8 | using System.Threading.Tasks;
9 |
10 | namespace App.WebApi.Controllers
11 | {
12 |
13 | /// [FromRoute] and [FromQuery] is added in order to generate swagger documentaion correctly until it will be fixed
14 | /// in swagger configuration
15 |
16 |
17 | ///
18 | /// This controller contains methods for the projects
19 | ///
20 | [Route("api/projects")]
21 | public class ProjectsController : AppController
22 | {
23 | private readonly IBus _bus;
24 |
25 | ///
26 | /// the controller cunstructor
27 | ///
28 | ///
29 | public ProjectsController(IBus bus)
30 | {
31 | _bus = bus;
32 | }
33 |
34 | ///
35 | /// Get All projects
36 | ///
37 | ///
38 | [HttpGet]
39 | public async Task GetAll([FromQuery]GetProjects queryModel)
40 | {
41 | var result = await _bus.QueryAsync(queryModel);
42 | return Ok(result);
43 | }
44 |
45 | ///
46 | /// get project by Id
47 | ///
48 | ///
49 | ///
50 | [HttpGet("{id}")]
51 | public async Task GetById([FromRoute]GetProject queryModel)
52 | {
53 | var result = await _bus.QueryAsync(queryModel);
54 |
55 | return Ok(result);
56 | }
57 |
58 | ///
59 | /// Add a project
60 | ///
61 | ///
62 | ///
63 | [HttpPost]
64 | [Authorize(Scopes.CreateProject)]
65 | public async Task Post(AddProject command)
66 | {
67 | var result = await _bus.ExecuteAsync(command);
68 | await _bus.PublishAsync(new ProjectCreated
69 | {
70 | Id = result
71 | });
72 | return Created(result);
73 | }
74 |
75 | ///
76 | /// Update a project
77 | ///
78 | ///
79 | ///
80 | [HttpPut]
81 | [Authorize(Scopes.UpdateProject)]
82 | public async Task Put(UpdateProject command)
83 | {
84 | _ = await _bus.ExecuteAsync(command);
85 | return Updated();
86 | }
87 | ///
88 | /// Delete a project
89 | ///
90 | ///
91 | ///
92 | [HttpDelete("{id}")]
93 | [Authorize(Scopes.DeleteProject)]
94 | public async Task Delete([FromRoute]DeleteProject command)
95 | {
96 | _ = await _bus.ExecuteAsync(command);
97 | return Deleted();
98 | }
99 | }
100 | }
--------------------------------------------------------------------------------
/src/App.WebApi/Extensions/ServicesExtensions.cs:
--------------------------------------------------------------------------------
1 | using App.Core;
2 | using App.Infrastructure.Persistence.SqlServer.Context;
3 | using BinaryOrigin.SeedWork.Core;
4 | using BinaryOrigin.SeedWork.Persistence.Ef;
5 | using BinaryOrigin.SeedWork.WebApi;
6 | using BinaryOrigin.SeedWork.WebApi.Authorization;
7 | using Microsoft.AspNetCore.Authentication;
8 | using Microsoft.AspNetCore.Authentication.JwtBearer;
9 | using Microsoft.AspNetCore.Authorization;
10 | using Microsoft.AspNetCore.Builder;
11 | using Microsoft.AspNetCore.Http;
12 | using Microsoft.Extensions.Configuration;
13 | using Microsoft.Extensions.DependencyInjection;
14 |
15 | namespace App.WebApi.Extensions
16 | {
17 | public static class ServicesExtensions
18 | {
19 | ///
20 | /// Add entity framework services
21 | ///
22 | ///
23 | ///
24 | public static void AddDbServices(this IEngine engine, IConfiguration configuration)
25 | {
26 | var connectionString = configuration["Db:ConnectionString"];
27 | var dbType = configuration["Db:Type"];
28 |
29 | if (dbType == "InMemory")
30 | {
31 | engine.AddInMemoryDbContext();
32 | }
33 | else
34 | {
35 | // 1
36 | engine.AddDbContext(connectionString);
37 | //var optionsBuilder = new DbContextOptionsBuilder();
38 | //optionsBuilder.UseSqlServer(connectionString);
39 | // 2 - engine.AddDbContext(() => new AppDbContext(optionsBuilder.Options));
40 |
41 | // 3 - engine.AddDbContext(optionsBuilder.Options);
42 | }
43 | engine.AddSqlServerDbExceptionParser(new DbErrorMessagesConfiguration
44 | {
45 | UniqueErrorTemplate = ErrorMessages.GenericUniqueError,
46 | CombinationUniqueErrorTemplate = ErrorMessages.GenericCombinationUniqueError
47 | });
48 | engine.AddRepositories();
49 | }
50 |
51 | public static void AddAuth(this IServiceCollection services, IConfiguration configuration)
52 |
53 | {
54 | var authConfig = configuration.GetSection("Auth");
55 |
56 | if (configuration.GetValue("IsTesting"))
57 | {
58 | services.AddAuthentication(options =>
59 | {
60 | options.DefaultAuthenticateScheme = "Test Scheme";
61 | options.DefaultChallengeScheme = "Test Scheme";
62 | }).AddTestAuth(options =>
63 | {
64 | options.Scopes = ReflectionHelper.GetConstants();
65 | options.Authority = authConfig["Authority"];
66 | });
67 | }
68 | else
69 | {
70 | services.AddAuthentication(options =>
71 | {
72 | options.DefaultAuthenticateScheme = JwtBearerDefaults.AuthenticationScheme;
73 | options.DefaultChallengeScheme = JwtBearerDefaults.AuthenticationScheme;
74 | }).AddJwtBearer(options =>
75 | {
76 | options.Authority = authConfig["Authority"];
77 | options.RequireHttpsMetadata = authConfig.GetValue("RequireHttps");
78 | options.Audience = authConfig["Audience"];
79 | });
80 | }
81 | services.AddAuthorization();
82 | services.AddSingleton();
83 | services.AddTransient(x=>new UserClaimsExtender(authConfig["Authority"]));
84 | }
85 | public static void UseAppExceptionHandler(this IApplicationBuilder app)
86 | {
87 | app.UseMiddleware();
88 | }
89 | }
90 | }
--------------------------------------------------------------------------------
/src/App.WebApi/Extensions/SwaggerExtensions.cs:
--------------------------------------------------------------------------------
1 | using Microsoft.AspNetCore.Builder;
2 | using Microsoft.Extensions.DependencyInjection;
3 | using Microsoft.OpenApi.Models;
4 | using System;
5 | using System.Collections.Generic;
6 | using System.IO;
7 | using System.Reflection;
8 |
9 | namespace App.WebApi.Extensions
10 | {
11 | public static class SwaggerExtensions
12 | {
13 | public static void UseAppSwagger(this IApplicationBuilder app)
14 | {
15 | app.UseSwagger();
16 | app.UseSwaggerUI(options =>
17 | {
18 | options.RoutePrefix = string.Empty;
19 | options.SwaggerEndpoint($"/swagger/v1/swagger.json", "App");
20 | });
21 | }
22 |
23 | ///
24 | /// Configure Swagger
25 | ///
26 | ///
27 | public static void AddAppSwagger(this IServiceCollection services)
28 | {
29 | services.AddSwaggerGen(c =>
30 | {
31 | c.SwaggerDoc("v1", new OpenApiInfo
32 | {
33 | Title = "My App",
34 | Version = "v1"
35 | });
36 | c.DescribeAllParametersInCamelCase();
37 | c.AddSecurityDefinition("Bearer", new OpenApiSecurityScheme
38 | {
39 | Name = "Bearer",
40 | Description = "Please enter your token access token",
41 | Type = SecuritySchemeType.Http,
42 | Scheme = "bearer",
43 | In = ParameterLocation.Header
44 | });
45 |
46 | c.AddSecurityRequirement(new OpenApiSecurityRequirement
47 | {
48 | {
49 | new OpenApiSecurityScheme
50 | {
51 | Reference = new OpenApiReference
52 | {
53 | Type = ReferenceType.SecurityScheme,
54 | Id = "Bearer"
55 | },
56 | }, new List()
57 | }
58 | });
59 | // Set the comments path for the Swagger JSON and UI.
60 | var basePath = AppContext.BaseDirectory;
61 | var xmlPath = Path.Combine(basePath, $"{Assembly.GetEntryAssembly().GetName().Name}.xml");
62 | c.IncludeXmlComments(xmlPath);
63 | });
64 | }
65 | }
66 | }
--------------------------------------------------------------------------------
/src/App.WebApi/Infrastructure/DependencyRegistrations.cs:
--------------------------------------------------------------------------------
1 | using App.WebApi.Localization;
2 | using Autofac;
3 | using BinaryOrigin.SeedWork.Core;
4 | using BinaryOrigin.SeedWork.Messages;
5 | using BinaryOrigin.SeedWork.WebApi.Authorization;
6 | using Microsoft.AspNetCore.Authentication;
7 | using Microsoft.AspNetCore.Authorization;
8 | using Microsoft.AspNetCore.Http;
9 | using Microsoft.Extensions.Configuration;
10 | using System;
11 | using System.Collections.Generic;
12 | using System.Linq;
13 | using System.Threading.Tasks;
14 |
15 | namespace App.WebApi.Infrastructure
16 | {
17 | public class DependencyRegistrations : IDependencyRegistration
18 | {
19 | public int Order => 2;
20 |
21 | public void Register(ContainerBuilder builder, ITypeFinder typeFinder, IConfiguration config)
22 | {
23 | builder.RegisterType()
24 | .As()
25 | .SingleInstance();
26 | builder.RegisterType()
27 | .As()
28 | .SingleInstance();
29 | builder.RegisterInstance(new HasScopeHandler(config["Auth:Authority"]))
30 | .As()
31 | .SingleInstance();
32 | builder.RegisterType()
33 | .As()
34 | .InstancePerLifetimeScope();
35 |
36 |
37 | }
38 | }
39 | }
40 |
--------------------------------------------------------------------------------
/src/App.WebApi/Infrastructure/Scopes.cs:
--------------------------------------------------------------------------------
1 | namespace App.WebApi
2 | {
3 | public class Scopes
4 | {
5 | public const string CreateProject = "create:project";
6 | public const string UpdateProject = "update:project";
7 | public const string DeleteProject = "delete:project";
8 | }
9 | }
--------------------------------------------------------------------------------
/src/App.WebApi/Infrastructure/UserClaimsExtender.cs:
--------------------------------------------------------------------------------
1 | using BinaryOrigin.SeedWork.Core;
2 | using Microsoft.AspNetCore.Authentication;
3 | using System.Linq;
4 | using System.Security.Claims;
5 | using System.Threading.Tasks;
6 |
7 | namespace App.WebApi
8 | {
9 | public class UserClaimsExtender : IClaimsTransformation
10 | {
11 | private readonly string _authority;
12 |
13 | public UserClaimsExtender(string authority)
14 | {
15 | _authority = authority;
16 | }
17 |
18 | public Task TransformAsync(ClaimsPrincipal principal)
19 | {
20 | // Here you can extend claims
21 |
22 | var identity = principal.Identities.FirstOrDefault();
23 | if (identity == null)
24 | {
25 | return null;
26 | }
27 | // actually all the scopes will be provided to every user
28 | // but normally this is not a real world example
29 | var claims = ReflectionHelper.GetConstants()
30 | .Select(x => new Claim("scope", x, ClaimValueTypes.String, _authority))
31 | .ToList();
32 |
33 | claims.AddRange(identity.Claims);
34 |
35 | var claimsIdentity = new ClaimsIdentity(claims, identity.AuthenticationType);
36 | var claimsPrincipal = new ClaimsPrincipal(claimsIdentity);
37 |
38 | return Task.FromResult(claimsPrincipal);
39 | }
40 | }
41 | }
--------------------------------------------------------------------------------
/src/App.WebApi/Infrastructure/WorkContext.cs:
--------------------------------------------------------------------------------
1 | using App.Core;
2 | using BinaryOrigin.SeedWork.Core;
3 | using Microsoft.AspNetCore.Http;
4 | using System.Linq;
5 |
6 | namespace App.WebApi
7 | {
8 | public class WorkContext : IWorkContext
9 | {
10 | private readonly IHttpContextAccessor _httpContextAccessor;
11 |
12 | private string _userId;
13 | private string _fullName;
14 | private string _email;
15 | private string _username;
16 |
17 | public WorkContext(IHttpContextAccessor httpContextAccessor)
18 | {
19 | _httpContextAccessor = httpContextAccessor;
20 | }
21 |
22 | public string UserId
23 | {
24 | get
25 | {
26 | if (string.IsNullOrEmpty(_userId))
27 | {
28 | _userId = GetClaimValue("user_id");
29 | }
30 | return _userId;
31 | }
32 | }
33 |
34 | private string GetClaimValue(string claimType)
35 | {
36 | var claim = _httpContextAccessor.HttpContext
37 | .User.Claims
38 | .FirstOrDefault(x => x.Type == claimType);
39 | return claim.Value.ToString();
40 | }
41 |
42 | public string FullName
43 | {
44 | get
45 | {
46 | if (_fullName.IsNullOrEmpty())
47 | {
48 | _fullName = GetClaimValue("full_name");
49 | }
50 | return _fullName;
51 | }
52 | }
53 |
54 | public string Email
55 | {
56 | get
57 | {
58 | if (_email.IsNullOrEmpty())
59 | {
60 | _email = GetClaimValue("email");
61 | }
62 |
63 | return _email;
64 | }
65 | }
66 |
67 | public string UserName
68 | {
69 | get
70 | {
71 | if (_username.IsNullOrEmpty())
72 | {
73 | _username = GetClaimValue("username");
74 | }
75 |
76 | return _username;
77 | }
78 | }
79 | }
80 | }
--------------------------------------------------------------------------------
/src/App.WebApi/Localization/LocalizerService.cs:
--------------------------------------------------------------------------------
1 | using BinaryOrigin.SeedWork.Messages;
2 | using Microsoft.Extensions.Localization;
3 |
4 | namespace App.WebApi.Localization
5 | {
6 | public class LocalizerService : ILocalizerService
7 | {
8 | private readonly IStringLocalizer _localizer;
9 |
10 | public LocalizerService(IStringLocalizer localizer)
11 | {
12 | _localizer = localizer;
13 | }
14 |
15 | public string Localize(string message)
16 | {
17 | return _localizer.GetString(message).Value;
18 | }
19 | }
20 | }
21 |
--------------------------------------------------------------------------------
/src/App.WebApi/Localization/Resources.cs:
--------------------------------------------------------------------------------
1 | namespace App.WebApi.Localization
2 | {
3 | public class Resources
4 | {
5 | }
6 | }
--------------------------------------------------------------------------------
/src/App.WebApi/Program.cs:
--------------------------------------------------------------------------------
1 | using Microsoft.AspNetCore;
2 | using Microsoft.AspNetCore.Hosting;
3 |
4 | namespace App.WebApi
5 | {
6 | public class Program
7 | {
8 | public static void Main(string[] args)
9 | {
10 | CreateWebHostBuilder(args).Build().Run();
11 | }
12 |
13 | public static IWebHostBuilder CreateWebHostBuilder(string[] args) =>
14 | WebHost.CreateDefaultBuilder(args)
15 | .UseStartup();
16 | }
17 | }
--------------------------------------------------------------------------------
/src/App.WebApi/Properties/launchSettings.json:
--------------------------------------------------------------------------------
1 | {
2 | "iisSettings": {
3 | "windowsAuthentication": false,
4 | "anonymousAuthentication": true,
5 | "iisExpress": {
6 | "applicationUrl": "http://localhost:5001",
7 | "sslPort": 0
8 | }
9 | },
10 | "$schema": "http://json.schemastore.org/launchsettings.json",
11 | "profiles": {
12 | "IIS Express": {
13 | "commandName": "IISExpress",
14 | "environmentVariables": {
15 | "ASPNETCORE_ENVIRONMENT": "Development"
16 | }
17 | },
18 | "App.WebApi": {
19 | "commandName": "Project",
20 | "launchBrowser": true,
21 | "launchUrl": "",
22 | "environmentVariables": {
23 | "ASPNETCORE_ENVIRONMENT": "Development"
24 | },
25 | "applicationUrl": "http://localhost:5001"
26 | }
27 | }
28 | }
--------------------------------------------------------------------------------
/src/App.WebApi/Startup.cs:
--------------------------------------------------------------------------------
1 | using BinaryOrigin.SeedWork.Core;
2 | using BinaryOrigin.SeedWork.WebApi;
3 | using Microsoft.AspNetCore.Builder;
4 | using Microsoft.AspNetCore.Hosting;
5 | using Microsoft.AspNetCore.Mvc;
6 | using Microsoft.Extensions.Configuration;
7 | using Microsoft.Extensions.DependencyInjection;
8 | using System;
9 |
10 | [assembly: ApiConventionType(typeof(DefaultApiConventions))]
11 |
12 | namespace App.WebApi
13 | {
14 | public class Startup
15 | {
16 | public IConfigurationRoot Configuration { get; }
17 |
18 | public Startup(IWebHostEnvironment environment)
19 | {
20 | Configuration = new ConfigurationBuilder()
21 | .SetBasePath(environment.ContentRootPath)
22 | .AddJsonFile("appsettings.json", optional: false, reloadOnChange: true)
23 | .AddEnvironmentVariables()
24 | .Build();
25 | }
26 |
27 | ///
28 | /// Add services to the application and configure service provider
29 | ///
30 | /// Collection of service descriptors
31 | public IServiceProvider ConfigureServices(IServiceCollection services)
32 | {
33 |
34 | var engine = (AppWebApiEngine)EngineContext.Create();
35 |
36 | engine.Initialize(Configuration);
37 |
38 | return engine.ConfigureServices(services);
39 |
40 | }
41 |
42 | ///
43 | /// Configure the application HTTP request pipeline
44 | ///
45 | ///
46 | public void Configure(IApplicationBuilder application)
47 | {
48 | ((AppWebApiEngine)EngineContext.Current).ConfigureRequestPipeline(application);
49 | }
50 | }
51 | }
--------------------------------------------------------------------------------
/src/App.WebApi/WebApiStartup.cs:
--------------------------------------------------------------------------------
1 | using App.WebApi.Extensions;
2 | using BinaryOrigin.SeedWork.Core;
3 | using BinaryOrigin.SeedWork.Messages;
4 | using BinaryOrigin.SeedWork.WebApi;
5 | using Microsoft.AspNetCore.Builder;
6 | using Microsoft.AspNetCore.Mvc;
7 | using Microsoft.Extensions.Configuration;
8 | using Microsoft.Extensions.DependencyInjection;
9 |
10 | namespace App.WebApi
11 | {
12 | ///
13 | public class WebApiStartup : IWebAppStartup
14 | {
15 | ///
16 | public int Order => 1;
17 |
18 | ///
19 | public void ConfigureServices(IServiceCollection services, IEngine engine, IConfiguration configuration)
20 | {
21 | //add framework services
22 | services.AddCors();
23 | services.AddControllers()
24 | .AddNewtonsoftJson()
25 | .SetCompatibilityVersion(CompatibilityVersion.Version_3_0);
26 | services.AddAppSwagger();
27 | services.AddDefaultEfSecondLevelCache();
28 | services.AddLocalization(options => options.ResourcesPath = "Resources");
29 | services.AddAuth(configuration);
30 |
31 | // add custom services
32 | engine.AddAutoMapper();
33 | engine.AddDbServices(configuration);
34 | engine.AddInMemoryBus();
35 | engine.AddFluentValidation();
36 | engine.AddHandlers();
37 | engine.AddDefaultDecorators();
38 | engine.AddDefaultPagination(c =>
39 | {
40 | c.MaxPageSizeAllowed = 100;
41 | });
42 | }
43 |
44 | ///
45 | public void Configure(IApplicationBuilder application, IConfiguration configuration)
46 | {
47 | application.UseAppExceptionHandler();
48 |
49 | application.UseAuthentication();
50 |
51 | application.UseRouting();
52 |
53 | application.UseAuthorization();
54 |
55 | application.UseEndpoints(cfg =>
56 | {
57 | cfg.MapControllers();
58 | });
59 |
60 | application.UseAppSwagger();
61 | }
62 | }
63 | }
--------------------------------------------------------------------------------
/src/App.WebApi/appsettings.Development.json:
--------------------------------------------------------------------------------
1 | {
2 | "Logging": {
3 | "LogLevel": {
4 | "Default": "Debug",
5 | "System": "Information",
6 | "Microsoft": "Information"
7 | }
8 | }
9 | }
--------------------------------------------------------------------------------
/src/App.WebApi/appsettings.json:
--------------------------------------------------------------------------------
1 | {
2 | "Logging": {
3 | "LogLevel": {
4 | "Default": "Warning"
5 | }
6 | },
7 | "AllowedHosts": "*",
8 | "Db": {
9 | "Type": "sqlserver",
10 | "ConnectionString": "Data Source=localhost;Initial Catalog=App;Persist Security Info=True;User ID=local;Password=local"
11 | },
12 | "Auth": {
13 | "Authority": "http://localhost:5000",
14 | "Audience": "myApi",
15 | "RequireHttps": "false"
16 | }
17 | }
--------------------------------------------------------------------------------
/src/MyApp.IdentityServer/Config.cs:
--------------------------------------------------------------------------------
1 | // Copyright (c) Brock Allen & Dominick Baier. All rights reserved.
2 | // Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information.
3 |
4 | using IdentityServer4;
5 | using IdentityServer4.Models;
6 | using IdentityServer4.Test;
7 | using System.Collections.Generic;
8 | using System.Security.Claims;
9 |
10 | namespace MyApp.IdentityServer
11 | {
12 | public static class Config
13 | {
14 | // scopes define the resources in your system
15 | public static IEnumerable GetIdentityResources()
16 | {
17 | return new List
18 | {
19 | new IdentityResources.OpenId(),
20 | new IdentityResources.Profile()
21 | };
22 | }
23 |
24 | public static IEnumerable GetApiResources()
25 | {
26 | return new List
27 | {
28 | new ApiResource("myApi", "My API")
29 | };
30 | }
31 |
32 | // clients want to access resources (aka scopes)
33 | public static IEnumerable GetClients()
34 | {
35 | // client credentials client
36 | return new List
37 | {
38 | // OpenID Connect hybrid flow and client credentials client (MVC)
39 | new Client
40 | {
41 | ClientId = "clientId",
42 | ClientName = "Sample client",
43 | AllowedGrantTypes = GrantTypes.ResourceOwnerPassword,
44 | ClientSecrets =
45 | {
46 | new Secret("secret".Sha256())
47 | },
48 | RedirectUris = { "http://localhost:5001" },
49 | AllowedScopes =
50 | {
51 | IdentityServerConstants.StandardScopes.OpenId,
52 | IdentityServerConstants.StandardScopes.Profile,
53 | "myApi"
54 | },
55 | AllowOfflineAccess = true
56 | }
57 | };
58 | }
59 |
60 | public static List GetUsers()
61 | {
62 | return new List
63 | {
64 | new TestUser
65 | {
66 | SubjectId = "1",
67 | Username = "getson",
68 | Password = "password",
69 |
70 | Claims = new List
71 | {
72 | new Claim(ClaimTypes.Email, "cela.getson@gmail.com"),
73 | new Claim(ClaimTypes.Name, "getson")
74 | }
75 | }
76 | };
77 | }
78 | }
79 | }
--------------------------------------------------------------------------------
/src/MyApp.IdentityServer/MyApp.IdentityServer.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | netcoreapp3.1
5 | 1591
6 | false
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
--------------------------------------------------------------------------------
/src/MyApp.IdentityServer/Program.cs:
--------------------------------------------------------------------------------
1 | // Copyright (c) Brock Allen & Dominick Baier. All rights reserved.
2 | // Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information.
3 |
4 | using Microsoft.AspNetCore;
5 | using Microsoft.AspNetCore.Hosting;
6 | using System;
7 |
8 | namespace MyApp.IdentityServer
9 | {
10 | public class Program
11 | {
12 | public static void Main(string[] args)
13 | {
14 | Console.Title = "IdentityServer";
15 |
16 | BuildWebHost(args).Run();
17 | }
18 |
19 | public static IWebHost BuildWebHost(string[] args) =>
20 | WebHost.CreateDefaultBuilder(args)
21 | .UseStartup()
22 | .Build();
23 | }
24 | }
--------------------------------------------------------------------------------
/src/MyApp.IdentityServer/Properties/launchSettings.json:
--------------------------------------------------------------------------------
1 | {
2 | "iisSettings": {
3 | "windowsAuthentication": false,
4 | "anonymousAuthentication": true,
5 | "iisExpress": {
6 | "applicationUrl": "http://localhost:5000",
7 | "sslPort": 0
8 | }
9 | },
10 | "profiles": {
11 | "IIS Express": {
12 | "commandName": "IISExpress",
13 | "launchBrowser": true,
14 | "launchUrl": ".well-known/openid-configuration",
15 | "environmentVariables": {
16 | "ASPNETCORE_ENVIRONMENT": "Development"
17 | }
18 | },
19 | "MyApp.IdentityServer": {
20 | "commandName": "Project",
21 | "launchBrowser": true,
22 | "applicationUrl": "http://localhost:5000",
23 | "launchUrl": ".well-known/openid-configuration",
24 | "environmentVariables": {
25 | "ASPNETCORE_ENVIRONMENT": "Development"
26 | }
27 | }
28 | }
29 | }
--------------------------------------------------------------------------------
/src/MyApp.IdentityServer/Startup.cs:
--------------------------------------------------------------------------------
1 | using Microsoft.AspNetCore.Builder;
2 | using Microsoft.AspNetCore.Hosting;
3 | using Microsoft.Extensions.DependencyInjection;
4 |
5 | namespace MyApp.IdentityServer
6 | {
7 | public class Startup
8 | {
9 | public void ConfigureServices(IServiceCollection services)
10 | {
11 | services.AddMvc();
12 | services.AddIdentityServer()
13 | .AddDeveloperSigningCredential()
14 | .AddInMemoryIdentityResources(Config.GetIdentityResources())
15 | .AddInMemoryApiResources(Config.GetApiResources())
16 | .AddInMemoryClients(Config.GetClients())
17 | .AddTestUsers(Config.GetUsers());
18 | }
19 |
20 | public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
21 | {
22 | if (env.EnvironmentName == "dev")
23 | {
24 | app.UseDeveloperExceptionPage();
25 | }
26 | app.UseIdentityServer();
27 | }
28 | }
29 | }
--------------------------------------------------------------------------------
/src/MyApp.IdentityServer/appsettings.Development.json:
--------------------------------------------------------------------------------
1 | {
2 | "Logging": {
3 | "LogLevel": {
4 | "Default": "Debug",
5 | "System": "Information",
6 | "Microsoft": "Information"
7 | }
8 | }
9 | }
--------------------------------------------------------------------------------
/src/MyApp.IdentityServer/appsettings.json:
--------------------------------------------------------------------------------
1 | {
2 | "Logging": {
3 | "LogLevel": {
4 | "Default": "Warning"
5 | }
6 | },
7 | "AllowedHosts": "*"
8 | }
--------------------------------------------------------------------------------
/src/MyApp.IdentityServer/tempkey.rsa:
--------------------------------------------------------------------------------
1 | {"KeyId":"6b00d297bb2cc450e23fcf1b5e1680de","Parameters":{"D":"Qe4zktzVfkbH4nElCkgqkJQSkRIJ0kHWae4H1MwfR4K5Rem+Pq+VbE9UBqbSVgigWk56ISS8cynrlkxul+10/DnvKpj+RVInxgUTlLvF5+D9s4ublE+Nxae7Oet6mzYzlBQXgPgY/1SWsGuGcnohV0o9YJQaEZlUxStExfGtFs5ofPksfPaSLbahz5HDkFevCMfxhGPdZoJAGqtzTyPP3JDbMbS7Bm1+flfZAqY4Skt0YvV84X4B0qAAKqlnpQmsYW+yEKPpi0Dr43yLNbkwNQOlPFERk9f7gI2k0njNpkomD+cZrjCeUUIINKSE+2NxFnorADsSsTrbD1VzF92DWQ==","DP":"tkG+shaufqJmskqXIq/RhCc6rWaX95oVn+w7gqgD0ma/UzZQeuN2347+cwJFYJVid2uNZlGDwOYDtZEjW5PK4il7AzCKlTKfmk4I9JEgoESpnKVZQ9lficyHGfV4nHqAm8tTjkI2m03G7e/ZMYzWQvlM1GQmQAkTwMZ41fONfz8=","DQ":"C+Nzu4euIOaN1y6V6sqjXMxD+WWlZIjG7oQX6uZpC4H8b+7F4iGZ4G6xk/5aXEOEA1DvUlhuMnc+nMbNQvhTWLqw0XcGx/ixI4c+1dhLx9TrQyR/6o3idNkL/lvlKhD28jWjSTJk9RvhAndpFdm2PHlcZTdvLEJbhRk08J33uac=","Exponent":"AQAB","InverseQ":"C4sYa0v06V+DdJ+S9M+gn0OybvhRP/WGWklplfa4P0vf8X/ULQpflWH+XtbtjPDndP3oIld0lQM7UyPkVEykU+HAOyaaAlfFB2pYPATW3HKGQ+st6iRG990GSFMXjPphI/lH+5xkldsyJn+DB4fvalYxZz/kq2f13fAXftsRyGA=","Modulus":"xVrStwTYTfha19v6/SNAb3xUhziyVy1CM4vveehVugHtQJo0h1ROPw5K2z+AvyPgsqaaSt3mOmXOMRATgfQJj5jc/467NC2FUn2BObsjMOLPY1VGvWlgJspjOF33/KRWpWyqxcoAseNObEzgwdMxAk9stHWE60wBwn8xSYdp3xMJGCtCzOgaXwUIoYw8d+nmVYDiFxl+wOpgLBxIn/KwlhF2KWO9aiivwatNhDfzSIX3/692Aa1oS4+EIt8g3P0vO+0psTCdMD2CSdKqXfdLctTgROOjty1YnBsNdJ1yOuAKPYrjuEOCZ08vAEeoRUp5tgwf+8xXjg/NkumP5lDFWQ==","P":"9AK5dJflIEmpjm18hWTrQOjyToE2bT0YQr2t0Hr0BVkGKmaS+Mxi+6dFymYuJsmfI6wJMXOn1uywmFCVmDo66EZQgoKzMJMgbIPI+d2wVnbm+4311Ehq/uhHCFgqZE/VBhJE7O+8IEz+Px3HWTlyjlcYP6PtzTHqpBwaPAdcTbM=","Q":"zw09f6dmj90VI77D4uurVM8ja82TKbbmCDvCtCr94q5KjmcfSaRuWSjATUdAmmvqbDvGQsoWpa9jh9FVXPhVhLsN0iL8DdeqS8/+tqv02gSzVVzMTNx9FyorF1O2qP9SDpkrzPSDZtn6lL50sMxe2P2tlf8S9bIFiqDT1gTJEsM="}}
--------------------------------------------------------------------------------
/src/SeedWork/BinaryOrigin.SeedWork.Core/BinaryOrigin.SeedWork.Core.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | netcoreapp3.1
5 | 1.1.2
6 | https://github.com/getson/MonolithicArchitecture
7 | https://github.com/getson/MonolithicArchitecture
8 | CQRS,DDD
9 | Getson Cela
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
--------------------------------------------------------------------------------
/src/SeedWork/BinaryOrigin.SeedWork.Core/Domain/BaseEntity.cs:
--------------------------------------------------------------------------------
1 | using System;
2 |
3 | namespace BinaryOrigin.SeedWork.Core.Domain
4 | {
5 | ///
6 | /// Base class for entities
7 | ///
8 | public abstract class BaseEntity : IEquatable
9 | {
10 | ///
11 | /// get or set the identifier
12 | ///
13 | public Guid Id { get; set; }
14 |
15 | public bool IsTransient()
16 | {
17 | return Id == default;
18 | }
19 |
20 | ///
21 | /// check for object equality
22 | ///
23 | /// Object
24 | /// Result
25 | public override bool Equals(object obj)
26 | {
27 | return Equals(obj as BaseEntity);
28 | }
29 |
30 | ///
31 | /// Equals
32 | ///
33 | /// other entity
34 | /// Result
35 | public virtual bool Equals(BaseEntity other)
36 | {
37 | if (other == null)
38 | return false;
39 |
40 | if (ReferenceEquals(this, other))
41 | return true;
42 |
43 | if (IsTransient() || other.IsTransient() || !Equals(Id, other.Id))
44 | return false;
45 |
46 | var otherType = other.GetType();
47 | var thisType = GetType();
48 |
49 | return thisType.IsAssignableFrom(otherType) || otherType.IsAssignableFrom(thisType);
50 | }
51 |
52 | ///
53 | /// Get hash code
54 | ///
55 | ///
56 | public override int GetHashCode()
57 | {
58 | return Id.GetHashCode();
59 | }
60 | }
61 | }
--------------------------------------------------------------------------------
/src/SeedWork/BinaryOrigin.SeedWork.Core/Domain/IRepository.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Threading.Tasks;
4 |
5 | namespace BinaryOrigin.SeedWork.Core.Domain
6 | {
7 | public interface IRepository where TEntity : BaseEntity
8 | {
9 | Task> GetAllAsync();
10 |
11 | Task CreateAsync(TEntity entity);
12 |
13 | Task CreateAsync(IEnumerable entities);
14 |
15 | Task UpdateAsync(TEntity entity);
16 |
17 | Task DeleteAsync(TEntity entity);
18 |
19 | Task> GetByIdAsync(Guid id);
20 |
21 | Task ExistsAsync(Guid id);
22 |
23 | IUnitOfWork UnitOfWork { get; }
24 | }
25 | }
--------------------------------------------------------------------------------
/src/SeedWork/BinaryOrigin.SeedWork.Core/Domain/IResult.cs:
--------------------------------------------------------------------------------
1 | namespace BinaryOrigin.SeedWork.Core.Domain
2 | {
3 | public interface IResult
4 | {
5 | bool IsFailure { get; }
6 | bool IsSuccess { get; }
7 | }
8 | }
--------------------------------------------------------------------------------
/src/SeedWork/BinaryOrigin.SeedWork.Core/Domain/Maybe.cs:
--------------------------------------------------------------------------------
1 | using System;
2 |
3 | namespace BinaryOrigin.SeedWork.Core.Domain
4 | {
5 | public struct Maybe : IEquatable>
6 | {
7 | private readonly MaybeValueWrapper _value;
8 |
9 | public T Value
10 | {
11 | get
12 | {
13 | if (HasNoValue)
14 | throw new InvalidOperationException();
15 |
16 | return _value._value;
17 | }
18 | }
19 |
20 | public static Maybe None => new Maybe();
21 |
22 | public bool HasValue => _value != null;
23 | public bool HasNoValue => !HasValue;
24 |
25 | private Maybe(T value)
26 | {
27 | _value = value == null ? null : new MaybeValueWrapper(value);
28 | }
29 |
30 | public static implicit operator Maybe(T value)
31 | {
32 | return new Maybe(value);
33 | }
34 |
35 | public static Maybe From(T obj)
36 | {
37 | return new Maybe(obj);
38 | }
39 |
40 | public static bool operator ==(Maybe maybe, T value)
41 | {
42 | if (maybe.HasNoValue)
43 | return false;
44 |
45 | return maybe.Value.Equals(value);
46 | }
47 |
48 | public static bool operator !=(Maybe maybe, T value)
49 | {
50 | return !(maybe == value);
51 | }
52 |
53 | public static bool operator ==(Maybe first, Maybe second)
54 | {
55 | return first.Equals(second);
56 | }
57 |
58 | public static bool operator !=(Maybe first, Maybe second)
59 | {
60 | return !(first == second);
61 | }
62 |
63 | public override bool Equals(object obj)
64 | {
65 | if (obj is T)
66 | {
67 | obj = new Maybe((T)obj);
68 | }
69 |
70 | if (!(obj is Maybe))
71 | return false;
72 |
73 | var other = (Maybe)obj;
74 | return Equals(other);
75 | }
76 |
77 | public bool Equals(Maybe other)
78 | {
79 | if (HasNoValue && other.HasNoValue)
80 | return true;
81 |
82 | if (HasNoValue || other.HasNoValue)
83 | return false;
84 |
85 | return _value._value.Equals(other._value._value);
86 | }
87 |
88 | public override int GetHashCode()
89 | {
90 | if (HasNoValue)
91 | return 0;
92 |
93 | return _value._value.GetHashCode();
94 | }
95 |
96 | public override string ToString()
97 | {
98 | if (HasNoValue)
99 | return "No value";
100 |
101 | return Value.ToString();
102 | }
103 |
104 | private class MaybeValueWrapper
105 | {
106 | public MaybeValueWrapper(T value)
107 | {
108 | _value = value;
109 | }
110 |
111 | internal readonly T _value;
112 | }
113 | }
114 | }
--------------------------------------------------------------------------------
/src/SeedWork/BinaryOrigin.SeedWork.Core/Domain/ValidationError.cs:
--------------------------------------------------------------------------------
1 | namespace BinaryOrigin.SeedWork.Core.Domain
2 | {
3 | public class ValidationError
4 | {
5 | public string PropertyName { get; set; }
6 | public string ErrorMessage { get; set; }
7 | }
8 | }
--------------------------------------------------------------------------------
/src/SeedWork/BinaryOrigin.SeedWork.Core/Domain/ValidationResponse.cs:
--------------------------------------------------------------------------------
1 | using System.Collections.Generic;
2 |
3 | namespace BinaryOrigin.SeedWork.Core.Domain
4 | {
5 | public class ValidationResponse
6 | {
7 | public IList Errors { get; set; } = new List();
8 | public bool IsValid => Errors.Count == 0;
9 | }
10 | }
11 |
--------------------------------------------------------------------------------
/src/SeedWork/BinaryOrigin.SeedWork.Core/Domain/ValueObject.cs:
--------------------------------------------------------------------------------
1 | using System.Collections.Generic;
2 | using System.Linq;
3 |
4 | namespace BinaryOrigin.SeedWork.Core.Domain
5 | {
6 | public abstract class ValueObject
7 | {
8 | protected static bool EqualOperator(ValueObject left, ValueObject right)
9 | {
10 | if (ReferenceEquals(left, null) ^ ReferenceEquals(right, null))
11 | {
12 | return false;
13 | }
14 | return ReferenceEquals(left, null) || left.Equals(right);
15 | }
16 |
17 | protected static bool NotEqualOperator(ValueObject left, ValueObject right)
18 | {
19 | return !(EqualOperator(left, right));
20 | }
21 |
22 | protected abstract IEnumerable