├── .gitignore ├── Blog ├── Blog.sln ├── src │ ├── Application │ │ ├── Application.csproj │ │ ├── Articles │ │ │ ├── Commands │ │ │ │ ├── ChangeVisibility │ │ │ │ │ └── ChangeArticleVisibilityCommand.cs │ │ │ │ └── Create │ │ │ │ │ ├── CreateArticleCommand.cs │ │ │ │ │ └── CreateArticleCommandValidator.cs │ │ │ └── Queries │ │ │ │ ├── Details │ │ │ │ ├── ArticleDetailsModel.cs │ │ │ │ ├── ArticleDetailsOutputModel.cs │ │ │ │ └── ArticleDetailsQuery.cs │ │ │ │ └── IsByUser │ │ │ │ └── ArticleIsByUserQuery.cs │ │ ├── Common │ │ │ ├── Behaviours │ │ │ │ ├── RequestLogger.cs │ │ │ │ ├── RequestPerformanceBehaviour.cs │ │ │ │ └── RequestValidationBehavior.cs │ │ │ ├── Exceptions │ │ │ │ ├── ModelValidationException.cs │ │ │ │ └── NotFoundException.cs │ │ │ ├── Interfaces │ │ │ │ ├── IBlogData.cs │ │ │ │ ├── ICurrentUser.cs │ │ │ │ ├── IDateTime.cs │ │ │ │ └── IIdentity.cs │ │ │ ├── Mappings │ │ │ │ ├── IMapFrom.cs │ │ │ │ └── MappingProfile.cs │ │ │ ├── Models │ │ │ │ └── Result.cs │ │ │ └── Services │ │ │ │ ├── IScopedService.cs │ │ │ │ ├── IService.cs │ │ │ │ └── ISingletonService.cs │ │ └── ServiceRegistration.cs │ ├── Domain │ │ ├── Common │ │ │ ├── AuditableEntity.cs │ │ │ ├── Entity.cs │ │ │ ├── IAuditableEntity.cs │ │ │ └── IEntity.cs │ │ ├── Domain.csproj │ │ ├── Entities │ │ │ ├── Article.cs │ │ │ └── Comment.cs │ │ ├── Events │ │ │ ├── IDomainEvent.cs │ │ │ ├── IEventDispatcher.cs │ │ │ └── IEventHandler.cs │ │ └── Exceptions │ │ │ ├── InvalidArticleException.cs │ │ │ ├── InvalidCommentException.cs │ │ │ └── InvalidEntityException.cs │ ├── Infrastructure │ │ ├── Identity │ │ │ ├── IdentityResultExtensions.cs │ │ │ ├── IdentityService.cs │ │ │ └── User.cs │ │ ├── Infrastructure.csproj │ │ ├── Persistence │ │ │ ├── BlogDbContext.cs │ │ │ ├── BlogDbContextSeed.cs │ │ │ ├── Configurations │ │ │ │ ├── ArticleConfiguration.cs │ │ │ │ └── CommentConfiguration.cs │ │ │ └── Migrations │ │ │ │ ├── 00000000000000_CreateIdentitySchema.Designer.cs │ │ │ │ ├── 00000000000000_CreateIdentitySchema.cs │ │ │ │ ├── 20200205204810_Articles.Designer.cs │ │ │ │ ├── 20200205204810_Articles.cs │ │ │ │ └── ApplicationDbContextModelSnapshot.cs │ │ ├── ServiceRegistration.cs │ │ └── Services │ │ │ ├── DateTimeService.cs │ │ │ └── EventDispatcherService.cs │ ├── Startup │ │ ├── Initializer.cs │ │ ├── Program.cs │ │ ├── Properties │ │ │ └── launchSettings.json │ │ ├── Startup.cs │ │ ├── Startup.csproj │ │ ├── appsettings.Development.json │ │ └── appsettings.json │ └── Web │ │ ├── Common │ │ ├── ApiController.cs │ │ └── OidcConfigurationController.cs │ │ ├── Features │ │ └── ArticlesController.cs │ │ ├── Middleware │ │ └── ExceptionHandlerMiddleware.cs │ │ ├── ServiceRegistration.cs │ │ ├── Services │ │ └── CurrentUserService.cs │ │ └── Web.csproj └── tests │ ├── Application.UnitTests │ ├── Application.UnitTests.csproj │ ├── ApplicationDbContextFactory.cs │ ├── Articles │ │ ├── Commands │ │ │ └── Create │ │ │ │ ├── CreateArticleCommandTests.cs │ │ │ │ └── CreateArticleCommandValidatorTests.cs │ │ └── Queries │ │ │ └── Details │ │ │ └── ArticleDetailsQueryTests.cs │ ├── CommandTestBase.cs │ ├── Common │ │ └── Mappings │ │ │ ├── MappingTests.cs │ │ │ └── MappingTestsFixture.cs │ └── QueryTestFixture.cs │ ├── Domain.UnitTests │ ├── Domain.UnitTests.csproj │ └── Entities │ │ └── ArticleTests.cs │ ├── Infrastructure.IntegrationTests │ ├── Events │ │ └── EventDispatcherTests.cs │ ├── Infrastructure.IntegrationTests.csproj │ └── Persistence │ │ └── BlogDbContextTests.cs │ └── Web.IntegrationTests │ ├── Features │ └── ArticlesControllerTests.cs │ ├── Mocks.cs │ ├── Pipeline │ └── ArticlesPipelineTests.cs │ ├── Routing │ └── ArticlesRoutingTests.cs │ ├── TestData.cs │ ├── TestStartup.cs │ ├── Web.IntegrationTests.csproj │ └── testsettings.json ├── Car Dealers ├── 1. Initial Architecture │ ├── Application │ │ ├── ApplicationConfiguration.cs │ │ ├── ApplicationSettings.cs │ │ ├── Behaviours │ │ │ └── RequestValidationBehavior.cs │ │ ├── CarRentalSystem.Application.csproj │ │ ├── Common │ │ │ ├── Result.cs │ │ │ └── SortOrder.cs │ │ ├── Contracts │ │ │ ├── ICurrentUser.cs │ │ │ └── IRepository.cs │ │ ├── Exceptions │ │ │ ├── ModelValidationException.cs │ │ │ └── NotFoundException.cs │ │ ├── Features │ │ │ ├── CarAds │ │ │ │ ├── Commands │ │ │ │ │ ├── ChangeAvailability │ │ │ │ │ │ └── ChangeAvailabilityCommand.cs │ │ │ │ │ ├── Common │ │ │ │ │ │ ├── CarAdCommand.cs │ │ │ │ │ │ ├── CarAdCommandValidator.cs │ │ │ │ │ │ └── ChangeCarAdCommand.cs │ │ │ │ │ ├── Create │ │ │ │ │ │ ├── CreateCarAdCommand.cs │ │ │ │ │ │ ├── CreateCarAdCommandValidator.cs │ │ │ │ │ │ └── CreateCarAdOutputModel.cs │ │ │ │ │ ├── Delete │ │ │ │ │ │ └── DeleteCarAdCommand.cs │ │ │ │ │ └── Edit │ │ │ │ │ │ ├── EditCarAdCommand.cs │ │ │ │ │ │ └── EditCarAdCommandValidator.cs │ │ │ │ ├── ICarAdRepository.cs │ │ │ │ └── Queries │ │ │ │ │ ├── Categories │ │ │ │ │ ├── GetCarAdCategoriesQuery.cs │ │ │ │ │ └── GetCarAdCategoryOutputModel.cs │ │ │ │ │ ├── Common │ │ │ │ │ ├── CarAdOutputModel.cs │ │ │ │ │ ├── CarAdsOutputModel.cs │ │ │ │ │ ├── CarAdsQuery.cs │ │ │ │ │ └── CarAdsSortOrder.cs │ │ │ │ │ ├── Details │ │ │ │ │ ├── CarAdDetailsOutputModel.cs │ │ │ │ │ └── CarAdDetailsQuery.cs │ │ │ │ │ ├── Mine │ │ │ │ │ ├── MineCarAdOutputModel.cs │ │ │ │ │ ├── MineCarAdsOutputModel.cs │ │ │ │ │ └── MineCarAdsQuery.cs │ │ │ │ │ └── Search │ │ │ │ │ ├── SearchCarAdsOutputModel.cs │ │ │ │ │ └── SearchCarAdsQuery.cs │ │ │ ├── Dealers │ │ │ │ ├── Commands │ │ │ │ │ └── Edit │ │ │ │ │ │ ├── EditDealerCommand.cs │ │ │ │ │ │ └── EditDealerCommandValidator.cs │ │ │ │ ├── IDealerRepository.cs │ │ │ │ └── Queries │ │ │ │ │ ├── Common │ │ │ │ │ └── DealerOutputModel.cs │ │ │ │ │ └── Details │ │ │ │ │ ├── DealerDetailsOutputModel.cs │ │ │ │ │ └── DealerDetailsQuery.cs │ │ │ ├── EntityCommand.cs │ │ │ └── Identity │ │ │ │ ├── Commands │ │ │ │ ├── ChangePassword │ │ │ │ │ ├── ChangePasswordCommand.cs │ │ │ │ │ └── ChangePasswordInputModel.cs │ │ │ │ ├── CreateUser │ │ │ │ │ ├── CreateUserCommand.Fakes.cs │ │ │ │ │ ├── CreateUserCommand.cs │ │ │ │ │ └── CreateUserCommandValidator.cs │ │ │ │ ├── LoginUser │ │ │ │ │ ├── LoginOutputModel.cs │ │ │ │ │ ├── LoginSuccessModel.cs │ │ │ │ │ └── LoginUserCommand.cs │ │ │ │ └── UserInputModel.cs │ │ │ │ ├── IIdentity.cs │ │ │ │ └── IUser.cs │ │ └── Mapping │ │ │ ├── IMapFrom.cs │ │ │ └── MappingProfile.cs │ ├── CarRentalSystem.sln │ ├── Domain │ │ ├── CarRentalSystem.Domain.csproj │ │ ├── Common │ │ │ ├── Entity.Specs.cs │ │ │ ├── Entity.cs │ │ │ ├── Enumeration.cs │ │ │ ├── Guard.cs │ │ │ ├── IAggregateRoot.cs │ │ │ ├── IInitialData.cs │ │ │ ├── ValueObject.Specs.cs │ │ │ └── ValueObject.cs │ │ ├── DomainConfiguration.Specs.cs │ │ ├── DomainConfiguration.cs │ │ ├── Exceptions │ │ │ ├── BaseDomainException.cs │ │ │ ├── InvalidCarAdException.cs │ │ │ ├── InvalidDealerException.cs │ │ │ ├── InvalidOptionsException.cs │ │ │ └── InvalidPhoneNumberException.cs │ │ ├── Factories │ │ │ ├── CarAds │ │ │ │ ├── CarAdFactory.Specs.cs │ │ │ │ ├── CarAdFactory.cs │ │ │ │ └── ICarAdFactory.cs │ │ │ ├── Dealers │ │ │ │ ├── DealerFactory.cs │ │ │ │ └── IDealerFactory.cs │ │ │ └── IFactory.cs │ │ ├── Models │ │ │ ├── CarAds │ │ │ │ ├── CarAd.Fakes.cs │ │ │ │ ├── CarAd.Specs.cs │ │ │ │ ├── CarAd.cs │ │ │ │ ├── Category.Data.cs │ │ │ │ ├── Category.Fakes.cs │ │ │ │ ├── Category.Specs.cs │ │ │ │ ├── Category.cs │ │ │ │ ├── Manufacturer.Fakes.cs │ │ │ │ ├── Manufacturer.cs │ │ │ │ ├── Options.Fakes.cs │ │ │ │ ├── Options.cs │ │ │ │ └── TransmissionType.cs │ │ │ ├── Dealers │ │ │ │ ├── Dealer.Fakes.cs │ │ │ │ ├── Dealer.Specs.cs │ │ │ │ ├── Dealer.cs │ │ │ │ └── PhoneNumber.cs │ │ │ └── ModelConstants.cs │ │ └── Specifications │ │ │ ├── CarAds │ │ │ ├── CadAdOnlyAvailableSpecification.cs │ │ │ ├── CarAdByCategorySpecification.cs │ │ │ ├── CarAdByManufacturerSpecification.cs │ │ │ └── CarAdByPricePerDaySpecification.cs │ │ │ ├── Dealers │ │ │ ├── DealerByIdSpecification.cs │ │ │ └── DealerByNameSpecification.cs │ │ │ └── Specification.cs │ ├── Infrastructure │ │ ├── CarRentalSystem.Infrastructure.csproj │ │ ├── Common │ │ │ └── QueryableExtensions.cs │ │ ├── IInitializer.cs │ │ ├── Identity │ │ │ ├── IJwtTokenGenerator.cs │ │ │ ├── IdentityService.Fakes.cs │ │ │ ├── IdentityService.cs │ │ │ ├── JwtTokenGeneratorService.Fakes.cs │ │ │ ├── JwtTokenGeneratorService.cs │ │ │ └── User.cs │ │ ├── InfrastructureConfiguration.Specs.cs │ │ ├── InfrastructureConfiguration.cs │ │ └── Persistence │ │ │ ├── CarRentalDbContext.cs │ │ │ ├── CarRentalDbInitializer.cs │ │ │ ├── Configurations │ │ │ ├── CarAdConfiguration.cs │ │ │ ├── CategoryConfiguration.cs │ │ │ ├── DealerConfiguration.cs │ │ │ ├── ManufacturerConfiguration.cs │ │ │ └── UserConfiguration.cs │ │ │ ├── Migrations │ │ │ ├── 20200523125432_InitialDomainTables.Designer.cs │ │ │ ├── 20200523125432_InitialDomainTables.cs │ │ │ ├── 20200524151609_UserTable.Designer.cs │ │ │ ├── 20200524151609_UserTable.cs │ │ │ └── CarRentalDbContextModelSnapshot.cs │ │ │ └── Repositories │ │ │ ├── CarAdRepository.cs │ │ │ ├── DataRepository.cs │ │ │ └── DealerRepository.cs │ ├── Startup │ │ ├── ApplicationInitialization.cs │ │ ├── CarRentalSystem.Startup.csproj │ │ ├── Program.cs │ │ ├── Properties │ │ │ └── launchSettings.json │ │ ├── Specs │ │ │ ├── CarAdsController.Specs.cs │ │ │ ├── DealersController.Specs.cs │ │ │ └── IdentityController.Specs.cs │ │ ├── Startup.Specs.cs │ │ ├── Startup.cs │ │ ├── appsettings.Development.json │ │ ├── appsettings.json │ │ └── testsettings.json │ └── Web │ │ ├── ApiController.cs │ │ ├── CarRentalSystem.Web.csproj │ │ ├── Common │ │ └── ResultExtensions.cs │ │ ├── Features │ │ ├── CarAdsController.cs │ │ ├── DealersController.cs │ │ └── IdentityController.cs │ │ ├── Middleware │ │ └── ValidationExceptionHandlerMiddleware.cs │ │ ├── Services │ │ └── CurrentUserService.cs │ │ └── WebConfiguration.cs ├── 2. Common Functionality │ ├── Application │ │ ├── ApplicationConfiguration.cs │ │ ├── CarRentalSystem.Application.csproj │ │ ├── Common │ │ │ ├── ApplicationSettings.cs │ │ │ ├── Behaviours │ │ │ │ └── RequestValidationBehavior.cs │ │ │ ├── Contracts │ │ │ │ ├── ICurrentUser.cs │ │ │ │ └── IRepository.cs │ │ │ ├── EntityCommand.cs │ │ │ ├── Exceptions │ │ │ │ ├── ModelValidationException.cs │ │ │ │ └── NotFoundException.cs │ │ │ ├── IEventHandler.cs │ │ │ ├── Mapping │ │ │ │ ├── IMapFrom.cs │ │ │ │ └── MappingProfile.cs │ │ │ ├── Result.cs │ │ │ └── SortOrder.cs │ │ ├── Dealerships │ │ │ ├── CarAds │ │ │ │ ├── Commands │ │ │ │ │ ├── ChangeAvailability │ │ │ │ │ │ └── ChangeAvailabilityCommand.cs │ │ │ │ │ ├── Common │ │ │ │ │ │ ├── CarAdCommand.cs │ │ │ │ │ │ ├── CarAdCommandValidator.cs │ │ │ │ │ │ └── ChangeCarAdCommand.cs │ │ │ │ │ ├── Create │ │ │ │ │ │ ├── CreateCarAdCommand.cs │ │ │ │ │ │ ├── CreateCarAdCommandValidator.cs │ │ │ │ │ │ └── CreateCarAdOutputModel.cs │ │ │ │ │ ├── Delete │ │ │ │ │ │ └── DeleteCarAdCommand.cs │ │ │ │ │ └── Edit │ │ │ │ │ │ ├── EditCarAdCommand.cs │ │ │ │ │ │ └── EditCarAdCommandValidator.cs │ │ │ │ ├── ICarAdRepository.cs │ │ │ │ └── Queries │ │ │ │ │ ├── Categories │ │ │ │ │ ├── GetCarAdCategoriesQuery.cs │ │ │ │ │ └── GetCarAdCategoryOutputModel.cs │ │ │ │ │ ├── Common │ │ │ │ │ ├── CarAdOutputModel.cs │ │ │ │ │ ├── CarAdsOutputModel.cs │ │ │ │ │ ├── CarAdsQuery.cs │ │ │ │ │ └── CarAdsSortOrder.cs │ │ │ │ │ ├── Details │ │ │ │ │ ├── CarAdDetailsOutputModel.cs │ │ │ │ │ └── CarAdDetailsQuery.cs │ │ │ │ │ ├── Mine │ │ │ │ │ ├── MineCarAdOutputModel.cs │ │ │ │ │ ├── MineCarAdsOutputModel.cs │ │ │ │ │ └── MineCarAdsQuery.cs │ │ │ │ │ └── Search │ │ │ │ │ ├── SearchCarAdsOutputModel.cs │ │ │ │ │ └── SearchCarAdsQuery.cs │ │ │ └── Dealers │ │ │ │ ├── Commands │ │ │ │ └── Edit │ │ │ │ │ ├── EditDealerCommand.cs │ │ │ │ │ └── EditDealerCommandValidator.cs │ │ │ │ ├── IDealerRepository.cs │ │ │ │ └── Queries │ │ │ │ ├── Common │ │ │ │ └── DealerOutputModel.cs │ │ │ │ └── Details │ │ │ │ ├── DealerDetailsOutputModel.cs │ │ │ │ └── DealerDetailsQuery.cs │ │ ├── Identity │ │ │ ├── Commands │ │ │ │ ├── ChangePassword │ │ │ │ │ ├── ChangePasswordCommand.cs │ │ │ │ │ └── ChangePasswordInputModel.cs │ │ │ │ ├── CreateUser │ │ │ │ │ ├── CreateUserCommand.Fakes.cs │ │ │ │ │ ├── CreateUserCommand.cs │ │ │ │ │ └── CreateUserCommandValidator.cs │ │ │ │ ├── LoginUser │ │ │ │ │ ├── LoginOutputModel.cs │ │ │ │ │ ├── LoginSuccessModel.cs │ │ │ │ │ └── LoginUserCommand.cs │ │ │ │ └── UserInputModel.cs │ │ │ ├── IIdentity.cs │ │ │ └── IUser.cs │ │ └── Statistics │ │ │ ├── Handlers │ │ │ └── CarAdAddedEventHandler.cs │ │ │ ├── IStatisticsRepository.cs │ │ │ └── Queries │ │ │ ├── CarAdViews │ │ │ └── GetCarAdViewsQuery.cs │ │ │ └── Current │ │ │ ├── GetCurrentStatisticsOutputModel.cs │ │ │ └── GetCurrentStatisticsQuery.cs │ ├── CarRentalSystem.sln │ ├── Domain │ │ ├── CarRentalSystem.Domain.csproj │ │ ├── Common │ │ │ ├── BaseDomainException.cs │ │ │ ├── IAggregateRoot.cs │ │ │ ├── IDomainEvent.cs │ │ │ ├── IFactory.cs │ │ │ ├── IInitialData.cs │ │ │ ├── Models │ │ │ │ ├── Entity.Specs.cs │ │ │ │ ├── Entity.cs │ │ │ │ ├── Enumeration.cs │ │ │ │ ├── Guard.cs │ │ │ │ ├── IEntity.cs │ │ │ │ ├── ValueObject.Specs.cs │ │ │ │ └── ValueObject.cs │ │ │ └── Specification.cs │ │ ├── Dealerships │ │ │ ├── Events │ │ │ │ └── Dealers │ │ │ │ │ └── CarAdAddedEvent.cs │ │ │ ├── Exceptions │ │ │ │ ├── InvalidCarAdException.cs │ │ │ │ ├── InvalidDealerException.cs │ │ │ │ ├── InvalidOptionsException.cs │ │ │ │ └── InvalidPhoneNumberException.cs │ │ │ ├── Factories │ │ │ │ ├── CarAds │ │ │ │ │ ├── CarAdFactory.Specs.cs │ │ │ │ │ ├── CarAdFactory.cs │ │ │ │ │ └── ICarAdFactory.cs │ │ │ │ └── Dealers │ │ │ │ │ ├── DealerFactory.cs │ │ │ │ │ └── IDealerFactory.cs │ │ │ ├── Models │ │ │ │ ├── CarAds │ │ │ │ │ ├── CarAd.Fakes.cs │ │ │ │ │ ├── CarAd.Specs.cs │ │ │ │ │ ├── CarAd.cs │ │ │ │ │ ├── Category.Data.cs │ │ │ │ │ ├── Category.Fakes.cs │ │ │ │ │ ├── Category.Specs.cs │ │ │ │ │ ├── Category.cs │ │ │ │ │ ├── Manufacturer.Fakes.cs │ │ │ │ │ ├── Manufacturer.cs │ │ │ │ │ ├── Options.Fakes.cs │ │ │ │ │ ├── Options.cs │ │ │ │ │ └── TransmissionType.cs │ │ │ │ ├── Dealers │ │ │ │ │ ├── Dealer.Fakes.cs │ │ │ │ │ ├── Dealer.Specs.cs │ │ │ │ │ ├── Dealer.cs │ │ │ │ │ └── PhoneNumber.cs │ │ │ │ └── ModelConstants.cs │ │ │ └── Specifications │ │ │ │ ├── CarAds │ │ │ │ ├── CadAdOnlyAvailableSpecification.cs │ │ │ │ ├── CarAdByCategorySpecification.cs │ │ │ │ ├── CarAdByManufacturerSpecification.cs │ │ │ │ └── CarAdByPricePerDaySpecification.cs │ │ │ │ └── Dealers │ │ │ │ ├── DealerByIdSpecification.cs │ │ │ │ └── DealerByNameSpecification.cs │ │ ├── DomainConfiguration.Specs.cs │ │ ├── DomainConfiguration.cs │ │ └── Statistics │ │ │ └── Models │ │ │ ├── CarAdView.cs │ │ │ ├── Statistics.Data.cs │ │ │ └── Statistics.cs │ ├── Infrastructure │ │ ├── CarRentalSystem.Infrastructure.csproj │ │ ├── Common │ │ │ ├── Events │ │ │ │ ├── EventDispatcher.cs │ │ │ │ └── IEventDispatcher.cs │ │ │ ├── IInitializer.cs │ │ │ ├── Persistence │ │ │ │ ├── CarRentalDbContext.cs │ │ │ │ ├── DataRepository.cs │ │ │ │ ├── DatabaseInitializer.cs │ │ │ │ ├── IDbContext.cs │ │ │ │ └── Migrations │ │ │ │ │ ├── 20200523125432_InitialDomainTables.Designer.cs │ │ │ │ │ ├── 20200523125432_InitialDomainTables.cs │ │ │ │ │ ├── 20200524151609_UserTable.Designer.cs │ │ │ │ │ ├── 20200524151609_UserTable.cs │ │ │ │ │ ├── 20200915173038_StatisticsTables.Designer.cs │ │ │ │ │ ├── 20200915173038_StatisticsTables.cs │ │ │ │ │ └── CarRentalDbContextModelSnapshot.cs │ │ │ └── QueryableExtensions.cs │ │ ├── Dealership │ │ │ ├── Configuration │ │ │ │ ├── CarAdConfiguration.cs │ │ │ │ ├── CategoryConfiguration.cs │ │ │ │ ├── DealerConfiguration.cs │ │ │ │ └── ManufacturerConfiguration.cs │ │ │ ├── IDealershipDbContext.cs │ │ │ └── Repositories │ │ │ │ ├── CarAdRepository.cs │ │ │ │ └── DealerRepository.cs │ │ ├── Identity │ │ │ ├── Configuration │ │ │ │ └── UserConfiguration.cs │ │ │ ├── IJwtTokenGenerator.cs │ │ │ ├── IdentityService.Fakes.cs │ │ │ ├── IdentityService.cs │ │ │ ├── JwtTokenGeneratorService.Fakes.cs │ │ │ ├── JwtTokenGeneratorService.cs │ │ │ └── User.cs │ │ ├── InfrastructureConfiguration.Specs.cs │ │ ├── InfrastructureConfiguration.cs │ │ └── Statistics │ │ │ ├── Configuration │ │ │ ├── CarAdViewConfiguration.cs │ │ │ └── StatisticsConfiguration.cs │ │ │ ├── IStatisticsDbContext.cs │ │ │ └── Repositories │ │ │ └── StatisticsRepository.cs │ ├── Startup │ │ ├── ApplicationInitialization.cs │ │ ├── CarRentalSystem.Startup.csproj │ │ ├── Program.cs │ │ ├── Properties │ │ │ └── launchSettings.json │ │ ├── Specs │ │ │ ├── CarAdsController.Specs.cs │ │ │ ├── DealersController.Specs.cs │ │ │ └── IdentityController.Specs.cs │ │ ├── Startup.Specs.cs │ │ ├── Startup.cs │ │ ├── appsettings.Development.json │ │ ├── appsettings.json │ │ └── testsettings.json │ └── Web │ │ ├── ApiController.cs │ │ ├── CarRentalSystem.Web.csproj │ │ ├── Common │ │ └── ResultExtensions.cs │ │ ├── Features │ │ ├── CarAdsController.cs │ │ ├── DealersController.cs │ │ └── IdentityController.cs │ │ ├── Middleware │ │ └── ValidationExceptionHandlerMiddleware.cs │ │ ├── Services │ │ └── CurrentUserService.cs │ │ └── WebConfiguration.cs ├── 3. Separation of Concerns │ ├── Application │ │ ├── ApplicationConfiguration.cs │ │ ├── CarRentalSystem.Application.csproj │ │ ├── Common │ │ │ ├── ApplicationSettings.cs │ │ │ ├── Behaviours │ │ │ │ └── RequestValidationBehavior.cs │ │ │ ├── Contracts │ │ │ │ ├── ICurrentUser.cs │ │ │ │ └── IQueryRepository.cs │ │ │ ├── EntityCommand.cs │ │ │ ├── Exceptions │ │ │ │ ├── ModelValidationException.cs │ │ │ │ └── NotFoundException.cs │ │ │ ├── IEventHandler.cs │ │ │ ├── Mapping │ │ │ │ ├── IMapFrom.cs │ │ │ │ └── MappingProfile.cs │ │ │ ├── Result.cs │ │ │ └── SortOrder.cs │ │ ├── Dealerships │ │ │ ├── CarAds │ │ │ │ ├── Commands │ │ │ │ │ ├── ChangeAvailability │ │ │ │ │ │ └── ChangeAvailabilityCommand.cs │ │ │ │ │ ├── Common │ │ │ │ │ │ ├── CarAdCommand.cs │ │ │ │ │ │ ├── CarAdCommandValidator.cs │ │ │ │ │ │ └── ChangeCarAdCommand.cs │ │ │ │ │ ├── Create │ │ │ │ │ │ ├── CreateCarAdCommand.cs │ │ │ │ │ │ ├── CreateCarAdCommandValidator.cs │ │ │ │ │ │ └── CreateCarAdOutputModel.cs │ │ │ │ │ ├── Delete │ │ │ │ │ │ └── DeleteCarAdCommand.cs │ │ │ │ │ └── Edit │ │ │ │ │ │ ├── EditCarAdCommand.cs │ │ │ │ │ │ └── EditCarAdCommandValidator.cs │ │ │ │ ├── ICarAdQueryRepository.cs │ │ │ │ └── Queries │ │ │ │ │ ├── Categories │ │ │ │ │ ├── GetCarAdCategoriesQuery.cs │ │ │ │ │ └── GetCarAdCategoryOutputModel.cs │ │ │ │ │ ├── Common │ │ │ │ │ ├── CarAdOutputModel.cs │ │ │ │ │ ├── CarAdsOutputModel.cs │ │ │ │ │ ├── CarAdsQuery.cs │ │ │ │ │ └── CarAdsSortOrder.cs │ │ │ │ │ ├── Details │ │ │ │ │ ├── CarAdDetailsOutputModel.cs │ │ │ │ │ └── CarAdDetailsQuery.cs │ │ │ │ │ ├── Mine │ │ │ │ │ ├── MineCarAdOutputModel.cs │ │ │ │ │ ├── MineCarAdsOutputModel.cs │ │ │ │ │ └── MineCarAdsQuery.cs │ │ │ │ │ └── Search │ │ │ │ │ ├── SearchCarAdsOutputModel.cs │ │ │ │ │ └── SearchCarAdsQuery.cs │ │ │ └── Dealers │ │ │ │ ├── Commands │ │ │ │ └── Edit │ │ │ │ │ ├── EditDealerCommand.cs │ │ │ │ │ └── EditDealerCommandValidator.cs │ │ │ │ ├── IDealerQueryRepository.cs │ │ │ │ └── Queries │ │ │ │ ├── Common │ │ │ │ └── DealerOutputModel.cs │ │ │ │ └── Details │ │ │ │ ├── DealerDetailsOutputModel.cs │ │ │ │ └── DealerDetailsQuery.cs │ │ ├── Identity │ │ │ ├── Commands │ │ │ │ ├── ChangePassword │ │ │ │ │ ├── ChangePasswordCommand.cs │ │ │ │ │ └── ChangePasswordInputModel.cs │ │ │ │ ├── CreateUser │ │ │ │ │ ├── CreateUserCommand.Fakes.cs │ │ │ │ │ ├── CreateUserCommand.cs │ │ │ │ │ └── CreateUserCommandValidator.cs │ │ │ │ ├── LoginUser │ │ │ │ │ ├── LoginOutputModel.cs │ │ │ │ │ ├── LoginSuccessModel.cs │ │ │ │ │ └── LoginUserCommand.cs │ │ │ │ └── UserInputModel.cs │ │ │ ├── IIdentity.cs │ │ │ └── IUser.cs │ │ └── Statistics │ │ │ ├── Handlers │ │ │ └── CarAdAddedEventHandler.cs │ │ │ ├── IStatisticsRepository.cs │ │ │ └── Queries │ │ │ ├── CarAdViews │ │ │ └── GetCarAdViewsQuery.cs │ │ │ └── Current │ │ │ ├── GetCurrentStatisticsOutputModel.cs │ │ │ └── GetCurrentStatisticsQuery.cs │ ├── CarRentalSystem.sln │ ├── Domain │ │ ├── CarRentalSystem.Domain.csproj │ │ ├── Common │ │ │ ├── BaseDomainException.cs │ │ │ ├── IAggregateRoot.cs │ │ │ ├── IDomainEvent.cs │ │ │ ├── IDomainRepository.cs │ │ │ ├── IFactory.cs │ │ │ ├── IInitialData.cs │ │ │ ├── Models │ │ │ │ ├── Entity.Specs.cs │ │ │ │ ├── Entity.cs │ │ │ │ ├── Enumeration.cs │ │ │ │ ├── Guard.cs │ │ │ │ ├── IEntity.cs │ │ │ │ ├── ValueObject.Specs.cs │ │ │ │ └── ValueObject.cs │ │ │ └── Specification.cs │ │ ├── Dealerships │ │ │ ├── Events │ │ │ │ └── Dealers │ │ │ │ │ └── CarAdAddedEvent.cs │ │ │ ├── Exceptions │ │ │ │ ├── InvalidCarAdException.cs │ │ │ │ ├── InvalidDealerException.cs │ │ │ │ ├── InvalidOptionsException.cs │ │ │ │ └── InvalidPhoneNumberException.cs │ │ │ ├── Factories │ │ │ │ ├── CarAds │ │ │ │ │ ├── CarAdFactory.Specs.cs │ │ │ │ │ ├── CarAdFactory.cs │ │ │ │ │ └── ICarAdFactory.cs │ │ │ │ └── Dealers │ │ │ │ │ ├── DealerFactory.cs │ │ │ │ │ └── IDealerFactory.cs │ │ │ ├── Models │ │ │ │ ├── CarAds │ │ │ │ │ ├── CarAd.Fakes.cs │ │ │ │ │ ├── CarAd.Specs.cs │ │ │ │ │ ├── CarAd.cs │ │ │ │ │ ├── Category.Data.cs │ │ │ │ │ ├── Category.Fakes.cs │ │ │ │ │ ├── Category.Specs.cs │ │ │ │ │ ├── Category.cs │ │ │ │ │ ├── Manufacturer.Fakes.cs │ │ │ │ │ ├── Manufacturer.cs │ │ │ │ │ ├── Options.Fakes.cs │ │ │ │ │ ├── Options.cs │ │ │ │ │ └── TransmissionType.cs │ │ │ │ ├── Dealers │ │ │ │ │ ├── Dealer.Fakes.cs │ │ │ │ │ ├── Dealer.Specs.cs │ │ │ │ │ ├── Dealer.cs │ │ │ │ │ └── PhoneNumber.cs │ │ │ │ └── ModelConstants.cs │ │ │ ├── Repositories │ │ │ │ ├── ICarAdDomainRepository.cs │ │ │ │ └── IDealerDomainRepository.cs │ │ │ └── Specifications │ │ │ │ ├── CarAds │ │ │ │ ├── CadAdOnlyAvailableSpecification.cs │ │ │ │ ├── CarAdByCategorySpecification.cs │ │ │ │ ├── CarAdByManufacturerSpecification.cs │ │ │ │ └── CarAdByPricePerDaySpecification.cs │ │ │ │ └── Dealers │ │ │ │ ├── DealerByIdSpecification.cs │ │ │ │ └── DealerByNameSpecification.cs │ │ ├── DomainConfiguration.Specs.cs │ │ ├── DomainConfiguration.cs │ │ └── Statistics │ │ │ └── Models │ │ │ ├── CarAdView.cs │ │ │ ├── Statistics.Data.cs │ │ │ └── Statistics.cs │ ├── Infrastructure │ │ ├── CarRentalSystem.Infrastructure.csproj │ │ ├── Common │ │ │ ├── Events │ │ │ │ ├── EventDispatcher.cs │ │ │ │ └── IEventDispatcher.cs │ │ │ ├── IInitializer.cs │ │ │ ├── Persistence │ │ │ │ ├── CarRentalDbContext.Specs.cs │ │ │ │ ├── CarRentalDbContext.cs │ │ │ │ ├── DataRepository.cs │ │ │ │ ├── DatabaseInitializer.cs │ │ │ │ ├── IDbContext.cs │ │ │ │ └── Migrations │ │ │ │ │ ├── 20200523125432_InitialDomainTables.Designer.cs │ │ │ │ │ ├── 20200523125432_InitialDomainTables.cs │ │ │ │ │ ├── 20200524151609_UserTable.Designer.cs │ │ │ │ │ ├── 20200524151609_UserTable.cs │ │ │ │ │ ├── 20200915173038_StatisticsTables.Designer.cs │ │ │ │ │ ├── 20200915173038_StatisticsTables.cs │ │ │ │ │ └── CarRentalDbContextModelSnapshot.cs │ │ │ └── QueryableExtensions.cs │ │ ├── Dealership │ │ │ ├── Configuration │ │ │ │ ├── CarAdConfiguration.cs │ │ │ │ ├── CategoryConfiguration.cs │ │ │ │ ├── DealerConfiguration.cs │ │ │ │ └── ManufacturerConfiguration.cs │ │ │ ├── IDealershipDbContext.cs │ │ │ └── Repositories │ │ │ │ ├── CarAdRepository.cs │ │ │ │ └── DealerRepository.cs │ │ ├── Identity │ │ │ ├── Configuration │ │ │ │ └── UserConfiguration.cs │ │ │ ├── IJwtTokenGenerator.cs │ │ │ ├── IdentityService.Fakes.cs │ │ │ ├── IdentityService.cs │ │ │ ├── JwtTokenGeneratorService.Fakes.cs │ │ │ ├── JwtTokenGeneratorService.cs │ │ │ └── User.cs │ │ ├── InfrastructureConfiguration.Specs.cs │ │ ├── InfrastructureConfiguration.cs │ │ └── Statistics │ │ │ ├── Configuration │ │ │ ├── CarAdViewConfiguration.cs │ │ │ └── StatisticsConfiguration.cs │ │ │ ├── IStatisticsDbContext.cs │ │ │ └── Repositories │ │ │ └── StatisticsRepository.cs │ ├── Startup │ │ ├── ApplicationInitialization.cs │ │ ├── CarRentalSystem.Startup.csproj │ │ ├── Program.cs │ │ ├── Properties │ │ │ └── launchSettings.json │ │ ├── Specs │ │ │ ├── CarAdsController.Specs.cs │ │ │ ├── DealersController.Specs.cs │ │ │ └── IdentityController.Specs.cs │ │ ├── Startup.Specs.cs │ │ ├── Startup.cs │ │ ├── appsettings.Development.json │ │ ├── appsettings.json │ │ └── testsettings.json │ └── Web │ │ ├── ApiController.cs │ │ ├── CarRentalSystem.Web.csproj │ │ ├── Common │ │ └── ResultExtensions.cs │ │ ├── Features │ │ ├── CarAdsController.cs │ │ ├── DealersController.cs │ │ └── IdentityController.cs │ │ ├── Middleware │ │ └── ValidationExceptionHandlerMiddleware.cs │ │ ├── Services │ │ └── CurrentUserService.cs │ │ └── WebConfiguration.cs └── 4. Multiple Bounded Contexts │ ├── Application │ ├── ApplicationConfiguration.cs │ ├── CarRentalSystem.Application.csproj │ ├── Common │ │ ├── ApplicationSettings.cs │ │ ├── Behaviours │ │ │ └── RequestValidationBehavior.cs │ │ ├── Contracts │ │ │ ├── ICurrentUser.cs │ │ │ └── IQueryRepository.cs │ │ ├── EntityCommand.cs │ │ ├── Exceptions │ │ │ ├── ModelValidationException.cs │ │ │ └── NotFoundException.cs │ │ ├── IEventHandler.cs │ │ ├── Mapping │ │ │ ├── IMapFrom.cs │ │ │ └── MappingProfile.cs │ │ ├── Result.cs │ │ └── SortOrder.cs │ ├── Dealerships │ │ ├── CarAds │ │ │ ├── Commands │ │ │ │ ├── ChangeAvailability │ │ │ │ │ └── ChangeAvailabilityCommand.cs │ │ │ │ ├── Common │ │ │ │ │ ├── CarAdCommand.cs │ │ │ │ │ ├── CarAdCommandValidator.cs │ │ │ │ │ └── ChangeCarAdCommand.cs │ │ │ │ ├── Create │ │ │ │ │ ├── CreateCarAdCommand.cs │ │ │ │ │ ├── CreateCarAdCommandValidator.cs │ │ │ │ │ └── CreateCarAdOutputModel.cs │ │ │ │ ├── Delete │ │ │ │ │ └── DeleteCarAdCommand.cs │ │ │ │ └── Edit │ │ │ │ │ ├── EditCarAdCommand.cs │ │ │ │ │ └── EditCarAdCommandValidator.cs │ │ │ ├── ICarAdQueryRepository.cs │ │ │ └── Queries │ │ │ │ ├── Categories │ │ │ │ ├── GetCarAdCategoriesQuery.cs │ │ │ │ └── GetCarAdCategoryOutputModel.cs │ │ │ │ ├── Common │ │ │ │ ├── CarAdOutputModel.cs │ │ │ │ ├── CarAdsOutputModel.cs │ │ │ │ ├── CarAdsQuery.cs │ │ │ │ └── CarAdsSortOrder.cs │ │ │ │ ├── Details │ │ │ │ ├── CarAdDetailsOutputModel.cs │ │ │ │ └── CarAdDetailsQuery.cs │ │ │ │ ├── Mine │ │ │ │ ├── MineCarAdOutputModel.cs │ │ │ │ ├── MineCarAdsOutputModel.cs │ │ │ │ └── MineCarAdsQuery.cs │ │ │ │ └── Search │ │ │ │ ├── SearchCarAdsOutputModel.cs │ │ │ │ └── SearchCarAdsQuery.cs │ │ └── Dealers │ │ │ ├── Commands │ │ │ └── Edit │ │ │ │ ├── EditDealerCommand.cs │ │ │ │ └── EditDealerCommandValidator.cs │ │ │ ├── IDealerQueryRepository.cs │ │ │ └── Queries │ │ │ ├── Common │ │ │ └── DealerOutputModel.cs │ │ │ └── Details │ │ │ ├── DealerDetailsOutputModel.cs │ │ │ └── DealerDetailsQuery.cs │ ├── Identity │ │ ├── Commands │ │ │ ├── ChangePassword │ │ │ │ ├── ChangePasswordCommand.cs │ │ │ │ └── ChangePasswordInputModel.cs │ │ │ ├── CreateUser │ │ │ │ ├── CreateUserCommand.Fakes.cs │ │ │ │ ├── CreateUserCommand.cs │ │ │ │ └── CreateUserCommandValidator.cs │ │ │ ├── LoginUser │ │ │ │ ├── LoginOutputModel.cs │ │ │ │ ├── LoginSuccessModel.cs │ │ │ │ └── LoginUserCommand.cs │ │ │ └── UserInputModel.cs │ │ ├── IIdentity.cs │ │ └── IUser.cs │ └── Statistics │ │ ├── Handlers │ │ └── CarAdAddedEventHandler.cs │ │ ├── IStatisticsQueryRepository.cs │ │ └── Queries │ │ ├── CarAdViews │ │ └── GetCarAdViewsQuery.cs │ │ └── Current │ │ ├── GetCurrentStatisticsOutputModel.cs │ │ └── GetCurrentStatisticsQuery.cs │ ├── CarRentalSystem.sln │ ├── Domain │ ├── CarRentalSystem.Domain.csproj │ ├── Common │ │ ├── BaseDomainException.cs │ │ ├── IAggregateRoot.cs │ │ ├── IDomainEvent.cs │ │ ├── IDomainRepository.cs │ │ ├── IFactory.cs │ │ ├── IInitialData.cs │ │ ├── Models │ │ │ ├── Entity.Specs.cs │ │ │ ├── Entity.cs │ │ │ ├── Enumeration.cs │ │ │ ├── Guard.cs │ │ │ ├── IEntity.cs │ │ │ ├── ValueObject.Specs.cs │ │ │ └── ValueObject.cs │ │ └── Specification.cs │ ├── Dealerships │ │ ├── Events │ │ │ └── Dealers │ │ │ │ └── CarAdAddedEvent.cs │ │ ├── Exceptions │ │ │ ├── InvalidCarAdException.cs │ │ │ ├── InvalidDealerException.cs │ │ │ ├── InvalidOptionsException.cs │ │ │ └── InvalidPhoneNumberException.cs │ │ ├── Factories │ │ │ ├── CarAds │ │ │ │ ├── CarAdFactory.Specs.cs │ │ │ │ ├── CarAdFactory.cs │ │ │ │ └── ICarAdFactory.cs │ │ │ └── Dealers │ │ │ │ ├── DealerFactory.cs │ │ │ │ └── IDealerFactory.cs │ │ ├── Models │ │ │ ├── CarAds │ │ │ │ ├── CarAd.Fakes.cs │ │ │ │ ├── CarAd.Specs.cs │ │ │ │ ├── CarAd.cs │ │ │ │ ├── Category.Data.cs │ │ │ │ ├── Category.Fakes.cs │ │ │ │ ├── Category.Specs.cs │ │ │ │ ├── Category.cs │ │ │ │ ├── Manufacturer.Fakes.cs │ │ │ │ ├── Manufacturer.cs │ │ │ │ ├── Options.Fakes.cs │ │ │ │ ├── Options.cs │ │ │ │ └── TransmissionType.cs │ │ │ ├── Dealers │ │ │ │ ├── Dealer.Fakes.cs │ │ │ │ ├── Dealer.Specs.cs │ │ │ │ ├── Dealer.cs │ │ │ │ └── PhoneNumber.cs │ │ │ └── ModelConstants.cs │ │ ├── Repositories │ │ │ ├── ICarAdDomainRepository.cs │ │ │ └── IDealerDomainRepository.cs │ │ └── Specifications │ │ │ ├── CarAds │ │ │ ├── CadAdOnlyAvailableSpecification.cs │ │ │ ├── CarAdByCategorySpecification.cs │ │ │ ├── CarAdByManufacturerSpecification.cs │ │ │ └── CarAdByPricePerDaySpecification.cs │ │ │ └── Dealers │ │ │ ├── DealerByIdSpecification.cs │ │ │ └── DealerByNameSpecification.cs │ ├── DomainConfiguration.Specs.cs │ ├── DomainConfiguration.cs │ ├── Renting │ │ ├── Models │ │ │ ├── DateTimeRange.cs │ │ │ ├── Driver.cs │ │ │ ├── Feedback.cs │ │ │ ├── RentedCar.cs │ │ │ └── Reservation.cs │ │ └── Services │ │ │ ├── IRentingScheduleService.cs │ │ │ └── RentingScheduleService.cs │ └── Statistics │ │ ├── Models │ │ ├── CarAdView.cs │ │ ├── Statistics.Data.cs │ │ └── Statistics.cs │ │ └── Repositories │ │ └── IStatisticsDomainRepository.cs │ ├── Infrastructure │ ├── CarRentalSystem.Infrastructure.csproj │ ├── Common │ │ ├── Events │ │ │ ├── EventDispatcher.cs │ │ │ └── IEventDispatcher.cs │ │ ├── IInitializer.cs │ │ ├── Persistence │ │ │ ├── CarRentalDbContext.Specs.cs │ │ │ ├── CarRentalDbContext.cs │ │ │ ├── DataRepository.cs │ │ │ ├── DatabaseInitializer.cs │ │ │ ├── IDbContext.cs │ │ │ ├── Migrations │ │ │ │ ├── 20200523125432_InitialDomainTables.Designer.cs │ │ │ │ ├── 20200523125432_InitialDomainTables.cs │ │ │ │ ├── 20200524151609_UserTable.Designer.cs │ │ │ │ ├── 20200524151609_UserTable.cs │ │ │ │ ├── 20200915173038_StatisticsTables.Designer.cs │ │ │ │ ├── 20200915173038_StatisticsTables.cs │ │ │ │ └── CarRentalDbContextModelSnapshot.cs │ │ │ └── Models │ │ │ │ ├── CarData.cs │ │ │ │ └── DealerData.cs │ │ └── QueryableExtensions.cs │ ├── Dealership │ │ ├── Configuration │ │ │ ├── CarAdConfiguration.cs │ │ │ ├── CategoryConfiguration.cs │ │ │ ├── DealerConfiguration.cs │ │ │ └── ManufacturerConfiguration.cs │ │ ├── IDealershipDbContext.cs │ │ └── Repositories │ │ │ ├── CarAdRepository.cs │ │ │ └── DealerRepository.cs │ ├── Identity │ │ ├── Configuration │ │ │ └── UserConfiguration.cs │ │ ├── IJwtTokenGenerator.cs │ │ ├── IdentityService.Fakes.cs │ │ ├── IdentityService.cs │ │ ├── JwtTokenGeneratorService.Fakes.cs │ │ ├── JwtTokenGeneratorService.cs │ │ └── User.cs │ ├── InfrastructureConfiguration.Specs.cs │ ├── InfrastructureConfiguration.cs │ └── Statistics │ │ ├── Configuration │ │ ├── CarAdViewConfiguration.cs │ │ └── StatisticsConfiguration.cs │ │ ├── IStatisticsDbContext.cs │ │ └── Repositories │ │ └── StatisticsRepository.cs │ ├── Startup │ ├── ApplicationInitialization.cs │ ├── CarRentalSystem.Startup.csproj │ ├── Program.cs │ ├── Properties │ │ └── launchSettings.json │ ├── Specs │ │ ├── CarAdsController.Specs.cs │ │ ├── DealersController.Specs.cs │ │ └── IdentityController.Specs.cs │ ├── Startup.Specs.cs │ ├── Startup.cs │ ├── appsettings.Development.json │ ├── appsettings.json │ └── testsettings.json │ └── Web │ ├── ApiController.cs │ ├── CarRentalSystem.Web.csproj │ ├── Common │ └── ResultExtensions.cs │ ├── Features │ ├── CarAdsController.cs │ ├── DealersController.cs │ └── IdentityController.cs │ ├── Middleware │ └── ValidationExceptionHandlerMiddleware.cs │ ├── Services │ └── CurrentUserService.cs │ └── WebConfiguration.cs ├── Domain Models ├── Blog │ ├── Blog.csproj │ ├── Factories │ │ ├── CourseFactory.cs │ │ ├── ICourseFactory.cs │ │ └── InnerFactories │ │ │ └── StudentFactory.cs │ ├── Models │ │ ├── Course.cs │ │ └── Student.cs │ └── Usage.cs ├── Common │ ├── BaseDomainException.cs │ ├── Common.csproj │ ├── IAggregateRoot.cs │ ├── IDomainEvent.cs │ ├── IFactory.cs │ ├── IInitialData.cs │ ├── Models │ │ ├── Entity.cs │ │ ├── Enumeration.cs │ │ ├── Guard.cs │ │ ├── IEntity.cs │ │ └── ValueObject.cs │ └── Specification.cs ├── DomainModels.sln ├── PetClinic │ ├── Application │ │ ├── AppointmentScheduling │ │ │ └── IDoctorsRepository.cs │ │ └── ClientPatientManagement │ │ │ └── IDoctorsRepository.cs │ ├── Infrastructure │ │ ├── AppointmentScheduling │ │ │ └── DoctorRepository.cs │ │ ├── ClientPatientManagement │ │ │ └── DoctorsRepository.cs │ │ └── Persistence │ │ │ ├── DbContext.cs │ │ │ └── Models │ │ │ └── DoctorData.cs │ ├── Models │ │ ├── AppointmentScheduling │ │ │ ├── Events │ │ │ │ ├── AppointmentConfirmedEvent.cs │ │ │ │ ├── AppointmentConflictingEvent.cs │ │ │ │ ├── AppointmentScheduledEvent.cs │ │ │ │ └── AppointmentUpdatedEvent.cs │ │ │ └── Models │ │ │ │ └── Schedule │ │ │ │ ├── Appointment.cs │ │ │ │ ├── AppointmentDetails.cs │ │ │ │ ├── Client.cs │ │ │ │ ├── Doctor.cs │ │ │ │ ├── Patient.cs │ │ │ │ ├── Room.cs │ │ │ │ └── Schedule.cs │ │ ├── ClientPatientManagement │ │ │ └── Models │ │ │ │ ├── Client.cs │ │ │ │ ├── Doctor.cs │ │ │ │ ├── Patient.cs │ │ │ │ ├── Room.cs │ │ │ │ └── Specialty.cs │ │ ├── MedicalRecords │ │ │ └── Models │ │ │ │ └── Patient.cs │ │ └── Shared │ │ │ ├── AnimalType.cs │ │ │ ├── DateTimeRange.cs │ │ │ └── Gender.cs │ └── PetClinic.csproj └── SnackMachine │ ├── Shared │ └── Money.cs │ ├── SnackMachine.csproj │ ├── SnackMachines │ ├── Slot.cs │ ├── SnackMachine.cs │ └── SnackPile.cs │ └── Snacks │ └── Snack.cs ├── LICENSE └── README.md /Blog/src/Application/Articles/Commands/Create/CreateArticleCommandValidator.cs: -------------------------------------------------------------------------------- 1 | namespace Blog.Application.Articles.Commands.Create 2 | { 3 | using FluentValidation; 4 | 5 | public class CreateArticleCommandValidator : AbstractValidator 6 | { 7 | public CreateArticleCommandValidator() 8 | { 9 | this.RuleFor(a => a.Title) 10 | .MaximumLength(40) 11 | .NotEmpty(); 12 | 13 | this.RuleFor(a => a.Content) 14 | .NotEmpty(); 15 | } 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /Blog/src/Application/Articles/Queries/Details/ArticleDetailsModel.cs: -------------------------------------------------------------------------------- 1 | namespace Blog.Application.Articles.Queries.Details 2 | { 3 | using System; 4 | using Common.Mappings; 5 | using Domain.Entities; 6 | 7 | public class ArticleDetailsModel : IMapFrom
8 | { 9 | public int Id { get; set; } 10 | 11 | public string Title { get; set; } 12 | 13 | public string Content { get; set; } 14 | 15 | public bool IsPublic { get; set; } 16 | 17 | public DateTime? PublishedOn { get; set; } 18 | 19 | public string CreatedBy { get; set; } 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /Blog/src/Application/Articles/Queries/Details/ArticleDetailsOutputModel.cs: -------------------------------------------------------------------------------- 1 | namespace Blog.Application.Articles.Queries.Details 2 | { 3 | using System; 4 | using Common.Mappings; 5 | 6 | public class ArticleDetailsOutputModel : IMapFrom 7 | { 8 | public int Id { get; set; } 9 | 10 | public string Title { get; set; } 11 | 12 | public string Content { get; set; } 13 | 14 | public bool IsPublic { get; set; } 15 | 16 | public DateTime? PublishedOn { get; set; } 17 | 18 | public string Author { get; set; } 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /Blog/src/Application/Common/Exceptions/NotFoundException.cs: -------------------------------------------------------------------------------- 1 | namespace Blog.Application.Common.Exceptions 2 | { 3 | using System; 4 | 5 | public class NotFoundException : Exception 6 | { 7 | public NotFoundException(string name, object key) 8 | : base($"Entity '{name}' ({key}) was not found.") 9 | { 10 | } 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /Blog/src/Application/Common/Interfaces/IBlogData.cs: -------------------------------------------------------------------------------- 1 | namespace Blog.Application.Common.Interfaces 2 | { 3 | using System.Threading; 4 | using System.Threading.Tasks; 5 | using Domain.Entities; 6 | using Microsoft.EntityFrameworkCore; 7 | 8 | public interface IBlogData 9 | { 10 | DbSet
Articles { get; set; } 11 | 12 | DbSet Comments { get; set; } 13 | 14 | Task SaveChanges(CancellationToken cancellationToken); 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /Blog/src/Application/Common/Interfaces/ICurrentUser.cs: -------------------------------------------------------------------------------- 1 | namespace Blog.Application.Common.Interfaces 2 | { 3 | using Services; 4 | 5 | public interface ICurrentUser : IScopedService 6 | { 7 | string UserId { get; } 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /Blog/src/Application/Common/Interfaces/IDateTime.cs: -------------------------------------------------------------------------------- 1 | namespace Blog.Application.Common.Interfaces 2 | { 3 | using System; 4 | using Services; 5 | 6 | public interface IDateTime : IService 7 | { 8 | DateTime Now { get; } 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /Blog/src/Application/Common/Interfaces/IIdentity.cs: -------------------------------------------------------------------------------- 1 | namespace Blog.Application.Common.Interfaces 2 | { 3 | using System.Threading.Tasks; 4 | using Models; 5 | using Services; 6 | 7 | public interface IIdentity : IService 8 | { 9 | Task GetUserName(string userId); 10 | 11 | Task<(Result Result, string UserId)> CreateUser(string userName, string password); 12 | 13 | Task DeleteUser(string userId); 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /Blog/src/Application/Common/Mappings/IMapFrom.cs: -------------------------------------------------------------------------------- 1 | namespace Blog.Application.Common.Mappings 2 | { 3 | using AutoMapper; 4 | 5 | public interface IMapFrom 6 | { 7 | void Mapping(Profile mapper) => mapper.CreateMap(typeof(T), this.GetType()); 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /Blog/src/Application/Common/Services/IScopedService.cs: -------------------------------------------------------------------------------- 1 | namespace Blog.Application.Common.Services 2 | { 3 | public interface IScopedService 4 | { 5 | } 6 | } 7 | -------------------------------------------------------------------------------- /Blog/src/Application/Common/Services/IService.cs: -------------------------------------------------------------------------------- 1 | namespace Blog.Application.Common.Services 2 | { 3 | public interface IService 4 | { 5 | } 6 | } 7 | -------------------------------------------------------------------------------- /Blog/src/Application/Common/Services/ISingletonService.cs: -------------------------------------------------------------------------------- 1 | namespace Blog.Application.Common.Services 2 | { 3 | public interface ISingletonService 4 | { 5 | } 6 | } 7 | -------------------------------------------------------------------------------- /Blog/src/Domain/Common/Entity.cs: -------------------------------------------------------------------------------- 1 | namespace Blog.Domain.Common 2 | { 3 | using System.Collections.Generic; 4 | using System.Linq; 5 | using Events; 6 | 7 | public abstract class Entity : IEntity 8 | { 9 | private readonly ICollection events; 10 | 11 | protected Entity() 12 | { 13 | this.events = new List(); 14 | } 15 | 16 | public virtual TKey Id { get; set; } 17 | 18 | // Add GetHashCode(), Equals(), etc. 19 | 20 | public IReadOnlyCollection Events => this.events.ToList(); 21 | 22 | public void ClearEvents() => this.events.Clear(); 23 | 24 | protected void AddEvent(IDomainEvent domainEvent) 25 | => this.events.Add(domainEvent); 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /Blog/src/Domain/Common/IAuditableEntity.cs: -------------------------------------------------------------------------------- 1 | namespace Blog.Domain.Common 2 | { 3 | using System; 4 | 5 | public interface IAuditableEntity 6 | { 7 | public string CreatedBy { get; set; } 8 | 9 | public DateTime CreatedOn { get; set; } 10 | 11 | public string ModifiedBy { get; set; } 12 | 13 | public DateTime? ModifiedOn { get; set; } 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /Blog/src/Domain/Common/IEntity.cs: -------------------------------------------------------------------------------- 1 | namespace Blog.Domain.Common 2 | { 3 | using System.Collections.Generic; 4 | using Events; 5 | 6 | public interface IEntity 7 | { 8 | IReadOnlyCollection Events { get; } 9 | 10 | void ClearEvents(); 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /Blog/src/Domain/Domain.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | netstandard2.1 5 | Blog.Domain 6 | Blog.Domain 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | -------------------------------------------------------------------------------- /Blog/src/Domain/Events/IDomainEvent.cs: -------------------------------------------------------------------------------- 1 | namespace Blog.Domain.Events 2 | { 3 | using System; 4 | 5 | public interface IDomainEvent 6 | { 7 | DateTime OccurredOn { get; } 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /Blog/src/Domain/Events/IEventDispatcher.cs: -------------------------------------------------------------------------------- 1 | namespace Blog.Domain.Events 2 | { 3 | using System.Threading.Tasks; 4 | 5 | public interface IEventDispatcher 6 | { 7 | Task Dispatch(IDomainEvent domainEvent); 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /Blog/src/Domain/Events/IEventHandler.cs: -------------------------------------------------------------------------------- 1 | namespace Blog.Domain.Events 2 | { 3 | using System.Threading.Tasks; 4 | 5 | public interface IEventHandler 6 | where TEvent : IDomainEvent 7 | { 8 | Task Handle(TEvent domainEvent); 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /Blog/src/Domain/Exceptions/InvalidArticleException.cs: -------------------------------------------------------------------------------- 1 | namespace Blog.Domain.Exceptions 2 | { 3 | using System; 4 | 5 | public class InvalidArticleException : Exception 6 | { 7 | public InvalidArticleException(string message) 8 | : base(message) 9 | { 10 | } 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /Blog/src/Domain/Exceptions/InvalidCommentException.cs: -------------------------------------------------------------------------------- 1 | namespace Blog.Domain.Exceptions 2 | { 3 | using System; 4 | 5 | public class InvalidCommentException : Exception 6 | { 7 | public InvalidCommentException(string message) 8 | : base(message) 9 | { 10 | } 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /Blog/src/Domain/Exceptions/InvalidEntityException.cs: -------------------------------------------------------------------------------- 1 | namespace Blog.Domain.Exceptions 2 | { 3 | using System; 4 | 5 | public class InvalidEntityException : Exception 6 | { 7 | public InvalidEntityException(string message) 8 | : base(message) 9 | { 10 | } 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /Blog/src/Infrastructure/Identity/IdentityResultExtensions.cs: -------------------------------------------------------------------------------- 1 | namespace Blog.Infrastructure.Identity 2 | { 3 | using System.Linq; 4 | using Application.Common.Models; 5 | using Microsoft.AspNetCore.Identity; 6 | 7 | public static class IdentityResultExtensions 8 | { 9 | public static Result ToApplicationResult(this IdentityResult result) 10 | => result.Succeeded 11 | ? Result.Success 12 | : Result.Failure(result.Errors.Select(e => e.Description)); 13 | } 14 | } -------------------------------------------------------------------------------- /Blog/src/Infrastructure/Identity/User.cs: -------------------------------------------------------------------------------- 1 | namespace Blog.Infrastructure.Identity 2 | { 3 | using System.Collections.Generic; 4 | using Domain.Entities; 5 | using Microsoft.AspNetCore.Identity; 6 | 7 | public class User : IdentityUser 8 | { 9 | public ICollection
Articles { get; } = new List
(); 10 | 11 | public ICollection Comments { get; } = new List(); 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /Blog/src/Infrastructure/Services/DateTimeService.cs: -------------------------------------------------------------------------------- 1 | namespace Blog.Infrastructure.Services 2 | { 3 | using System; 4 | using Application.Common.Interfaces; 5 | 6 | public class DateTimeService : IDateTime 7 | { 8 | public DateTime Now => DateTime.Now; 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /Blog/src/Startup/Program.cs: -------------------------------------------------------------------------------- 1 | namespace Blog 2 | { 3 | using Microsoft.AspNetCore; 4 | using Microsoft.AspNetCore.Hosting; 5 | 6 | public class Program 7 | { 8 | public static void Main(string[] args) 9 | => CreateWebHostBuilder(args) 10 | .Build() 11 | .Initialize() 12 | .Run(); 13 | 14 | public static IWebHostBuilder CreateWebHostBuilder(string[] args) 15 | => WebHost 16 | .CreateDefaultBuilder(args) 17 | .UseStartup(); 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /Blog/src/Startup/Properties/launchSettings.json: -------------------------------------------------------------------------------- 1 | { 2 | "iisSettings": { 3 | "windowsAuthentication": false, 4 | "anonymousAuthentication": true, 5 | "iisExpress": { 6 | "applicationUrl": "http://localhost:58415", 7 | "sslPort": 44342 8 | } 9 | }, 10 | "profiles": { 11 | "IIS Express": { 12 | "commandName": "IISExpress", 13 | "launchBrowser": true, 14 | "environmentVariables": { 15 | "ASPNETCORE_ENVIRONMENT": "Development" 16 | } 17 | }, 18 | "Startup": { 19 | "commandName": "Project", 20 | "launchBrowser": true, 21 | "applicationUrl": "https://localhost:5001;http://localhost:5000", 22 | "environmentVariables": { 23 | "ASPNETCORE_ENVIRONMENT": "Development" 24 | } 25 | } 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /Blog/src/Startup/appsettings.Development.json: -------------------------------------------------------------------------------- 1 | { 2 | "Logging": { 3 | "LogLevel": { 4 | "Default": "Information", 5 | "Microsoft": "Warning", 6 | "Microsoft.Hosting.Lifetime": "Information" 7 | } 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /Blog/src/Startup/appsettings.json: -------------------------------------------------------------------------------- 1 | { 2 | "ConnectionStrings": { 3 | "DefaultConnection": "Server=(localdb)\\mssqllocaldb;Database=BlogDb;Trusted_Connection=True;MultipleActiveResultSets=true" 4 | }, 5 | "Logging": { 6 | "LogLevel": { 7 | "Default": "Warning" 8 | } 9 | }, 10 | "IdentityServer": { 11 | "Key": { 12 | "Type": "Development" 13 | }, 14 | "Clients": { 15 | "Blog.Client": { 16 | "Profile": "IdentityServerSPA" 17 | } 18 | } 19 | }, 20 | "AllowedHosts": "*" 21 | } -------------------------------------------------------------------------------- /Blog/src/Web/Common/ApiController.cs: -------------------------------------------------------------------------------- 1 | namespace Blog.Web.Common 2 | { 3 | using MediatR; 4 | using Microsoft.AspNetCore.Mvc; 5 | using Microsoft.Extensions.DependencyInjection; 6 | 7 | [ApiController] 8 | [Route("api/[controller]")] 9 | public abstract class ApiController : ControllerBase 10 | { 11 | private IMediator mediator; 12 | 13 | protected IMediator Mediator 14 | => this.mediator ??= this.HttpContext 15 | .RequestServices 16 | .GetService(); 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /Blog/src/Web/ServiceRegistration.cs: -------------------------------------------------------------------------------- 1 | namespace Blog.Web 2 | { 3 | using Application; 4 | using Microsoft.Extensions.DependencyInjection; 5 | 6 | public static class ServiceRegistration 7 | { 8 | public static IServiceCollection AddWebComponents( 9 | this IServiceCollection services) 10 | => services 11 | // .AddScoped() 12 | .AddHttpContextAccessor() 13 | .AddConventionalServices(typeof(ServiceRegistration).Assembly); 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /Blog/src/Web/Services/CurrentUserService.cs: -------------------------------------------------------------------------------- 1 | namespace Blog.Web.Services 2 | { 3 | using System.Security.Claims; 4 | using Application.Common.Interfaces; 5 | using Microsoft.AspNetCore.Http; 6 | 7 | public class CurrentUserService : ICurrentUser 8 | { 9 | public CurrentUserService(IHttpContextAccessor httpContextAccessor) 10 | => this.UserId = httpContextAccessor 11 | .HttpContext? 12 | .User? 13 | .FindFirstValue(ClaimTypes.NameIdentifier); 14 | 15 | public string UserId { get; } 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /Blog/src/Web/Web.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | netcoreapp3.1 5 | Blog.Web 6 | Blog.Web 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | -------------------------------------------------------------------------------- /Blog/tests/Application.UnitTests/Common/Mappings/MappingTestsFixture.cs: -------------------------------------------------------------------------------- 1 | namespace Blog.Application.UnitTests.Common.Mappings 2 | { 3 | using AutoMapper; 4 | using Blog.Application.Common.Mappings; 5 | 6 | public class MappingTestsFixture 7 | { 8 | public MappingTestsFixture() 9 | { 10 | this.ConfigurationProvider = new MapperConfiguration(cfg => 11 | { 12 | cfg.AddProfile(); 13 | }); 14 | 15 | this.Mapper = this.ConfigurationProvider.CreateMapper(); 16 | } 17 | 18 | public IConfigurationProvider ConfigurationProvider { get; } 19 | 20 | public IMapper Mapper { get; } 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /Blog/tests/Domain.UnitTests/Entities/ArticleTests.cs: -------------------------------------------------------------------------------- 1 | namespace Blog.Domain.UnitTests.Entities 2 | { 3 | using Domain.Entities; 4 | using Exceptions; 5 | using Xunit; 6 | 7 | public class ArticleTests 8 | { 9 | [Fact] 10 | public void TitleShouldThrowExceptionWhenNull() 11 | { 12 | // Assert 13 | Assert.Throws( 14 | () => new Article(null, "Test Content", "Test Id")); 15 | } 16 | 17 | [Fact] 18 | public void UserIdShouldThrowExceptionWhenNull() 19 | { 20 | // Assert 21 | Assert.Throws( 22 | () => new Article("Test Title", "Test Content", null)); 23 | } 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /Blog/tests/Web.IntegrationTests/TestStartup.cs: -------------------------------------------------------------------------------- 1 | namespace Blog.Web.IntegrationTests 2 | { 3 | using Application.Common.Interfaces; 4 | using Microsoft.Extensions.Configuration; 5 | using Microsoft.Extensions.DependencyInjection; 6 | using MyTested.AspNetCore.Mvc; 7 | 8 | public class TestStartup : Startup 9 | { 10 | public TestStartup(IConfiguration configuration) 11 | : base(configuration) 12 | { 13 | } 14 | 15 | public void ConfigureTestServices(IServiceCollection services) 16 | { 17 | base.ConfigureServices(services); 18 | 19 | services 20 | .ReplaceTransient(_ => Mocks.CurrentUser) 21 | .ReplaceTransient(_ => Mocks.DateTime); 22 | } 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /Blog/tests/Web.IntegrationTests/testsettings.json: -------------------------------------------------------------------------------- 1 | { 2 | "ConnectionStrings": { 3 | "DefaultConnection": "Dummy Connection" 4 | }, 5 | "Logging": { 6 | "LogLevel": { 7 | "Default": "Warning" 8 | } 9 | }, 10 | "IdentityServer": { 11 | "Key": { 12 | "Type": "Development" 13 | }, 14 | "Clients": { 15 | "Blog.Client": { 16 | "Profile": "IdentityServerSPA" 17 | } 18 | } 19 | }, 20 | "AllowedHosts": "*" 21 | } -------------------------------------------------------------------------------- /Car Dealers/1. Initial Architecture/Application/ApplicationSettings.cs: -------------------------------------------------------------------------------- 1 | namespace CarRentalSystem.Application 2 | { 3 | public class ApplicationSettings 4 | { 5 | public ApplicationSettings() => this.Secret = default!; 6 | 7 | public string Secret { get; private set; } 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /Car Dealers/1. Initial Architecture/Application/Common/SortOrder.cs: -------------------------------------------------------------------------------- 1 | namespace CarRentalSystem.Application.Common 2 | { 3 | using System; 4 | using System.Linq.Expressions; 5 | 6 | public abstract class SortOrder 7 | { 8 | public const string Ascending = "asc"; 9 | public const string Descending = "desc"; 10 | 11 | protected SortOrder(string? sortBy, string? order) 12 | { 13 | this.SortBy = sortBy; 14 | this.Order = order; 15 | } 16 | 17 | public string? SortBy { get; } 18 | 19 | public string? Order { get; } 20 | 21 | public abstract Expression> ToExpression(); 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /Car Dealers/1. Initial Architecture/Application/Contracts/ICurrentUser.cs: -------------------------------------------------------------------------------- 1 | namespace CarRentalSystem.Application.Contracts 2 | { 3 | public interface ICurrentUser 4 | { 5 | string UserId { get; } 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /Car Dealers/1. Initial Architecture/Application/Contracts/IRepository.cs: -------------------------------------------------------------------------------- 1 | namespace CarRentalSystem.Application.Contracts 2 | { 3 | using System.Threading; 4 | using System.Threading.Tasks; 5 | using Domain.Common; 6 | 7 | public interface IRepository 8 | where TEntity : IAggregateRoot 9 | { 10 | Task Save(TEntity entity, CancellationToken cancellationToken = default); 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /Car Dealers/1. Initial Architecture/Application/Exceptions/NotFoundException.cs: -------------------------------------------------------------------------------- 1 | namespace CarRentalSystem.Application.Exceptions 2 | { 3 | using System; 4 | 5 | public class NotFoundException : Exception 6 | { 7 | public NotFoundException(string name, object key) 8 | : base($"Entity '{name}' ({key}) was not found.") 9 | { 10 | } 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /Car Dealers/1. Initial Architecture/Application/Features/CarAds/Commands/Common/CarAdCommand.cs: -------------------------------------------------------------------------------- 1 | namespace CarRentalSystem.Application.Features.CarAds.Commands.Common 2 | { 3 | public abstract class CarAdCommand : EntityCommand 4 | where TCommand : EntityCommand 5 | { 6 | public string Manufacturer { get; set; } = default!; 7 | 8 | public string Model { get; set; } = default!; 9 | 10 | public int Category { get; set; } 11 | 12 | public string ImageUrl { get; set; } = default!; 13 | 14 | public decimal PricePerDay { get; set; } 15 | 16 | public bool HasClimateControl { get; set; } 17 | 18 | public int NumberOfSeats { get; set; } 19 | 20 | public int TransmissionType { get; set; } 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /Car Dealers/1. Initial Architecture/Application/Features/CarAds/Commands/Create/CreateCarAdCommandValidator.cs: -------------------------------------------------------------------------------- 1 | namespace CarRentalSystem.Application.Features.CarAds.Commands.Create 2 | { 3 | using Common; 4 | using FluentValidation; 5 | 6 | public class CreateCarAdCommandValidator : AbstractValidator 7 | { 8 | public CreateCarAdCommandValidator(ICarAdRepository carAdRepository) 9 | => this.Include(new CarAdCommandValidator(carAdRepository)); 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /Car Dealers/1. Initial Architecture/Application/Features/CarAds/Commands/Create/CreateCarAdOutputModel.cs: -------------------------------------------------------------------------------- 1 | namespace CarRentalSystem.Application.Features.CarAds.Commands.Create 2 | { 3 | public class CreateCarAdOutputModel 4 | { 5 | public CreateCarAdOutputModel(int carAdId) 6 | => this.CarAdId = carAdId; 7 | 8 | public int CarAdId { get; } 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /Car Dealers/1. Initial Architecture/Application/Features/CarAds/Commands/Edit/EditCarAdCommandValidator.cs: -------------------------------------------------------------------------------- 1 | namespace CarRentalSystem.Application.Features.CarAds.Commands.Edit 2 | { 3 | using Common; 4 | using FluentValidation; 5 | 6 | public class EditCarAdCommandValidator : AbstractValidator 7 | { 8 | public EditCarAdCommandValidator(ICarAdRepository carAdRepository) 9 | => this.Include(new CarAdCommandValidator(carAdRepository)); 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /Car Dealers/1. Initial Architecture/Application/Features/CarAds/Queries/Categories/GetCarAdCategoryOutputModel.cs: -------------------------------------------------------------------------------- 1 | namespace CarRentalSystem.Application.Features.CarAds.Queries.Categories 2 | { 3 | using Domain.Models.CarAds; 4 | using Mapping; 5 | 6 | public class GetCarAdCategoryOutputModel : IMapFrom 7 | { 8 | public int Id { get; private set; } 9 | 10 | public string Name { get; private set; } = default!; 11 | 12 | public string Description { get; private set; } = default!; 13 | 14 | public int TotalCarAds { get; set; } 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /Car Dealers/1. Initial Architecture/Application/Features/CarAds/Queries/Common/CarAdsOutputModel.cs: -------------------------------------------------------------------------------- 1 | namespace CarRentalSystem.Application.Features.CarAds.Queries.Common 2 | { 3 | using System.Collections.Generic; 4 | 5 | public abstract class CarAdsOutputModel 6 | { 7 | internal CarAdsOutputModel( 8 | IEnumerable carAds, 9 | int page, 10 | int totalPages) 11 | { 12 | this.CarAds = carAds; 13 | this.Page = page; 14 | this.TotalPages = totalPages; 15 | } 16 | 17 | public IEnumerable CarAds { get; } 18 | 19 | public int Page { get; } 20 | 21 | public int TotalPages { get; } 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /Car Dealers/1. Initial Architecture/Application/Features/CarAds/Queries/Mine/MineCarAdOutputModel.cs: -------------------------------------------------------------------------------- 1 | namespace CarRentalSystem.Application.Features.CarAds.Queries.Mine 2 | { 3 | using AutoMapper; 4 | using Common; 5 | using Domain.Models.CarAds; 6 | 7 | public class MineCarAdOutputModel : CarAdOutputModel 8 | { 9 | public bool IsAvailable { get; private set; } 10 | 11 | public override void Mapping(Profile mapper) 12 | => mapper 13 | .CreateMap() 14 | .IncludeBase(); 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /Car Dealers/1. Initial Architecture/Application/Features/CarAds/Queries/Mine/MineCarAdsOutputModel.cs: -------------------------------------------------------------------------------- 1 | namespace CarRentalSystem.Application.Features.CarAds.Queries.Mine 2 | { 3 | using System.Collections.Generic; 4 | using Common; 5 | 6 | public class MineCarAdsOutputModel : CarAdsOutputModel 7 | { 8 | public MineCarAdsOutputModel( 9 | IEnumerable carAds, 10 | int page, 11 | int totalPages) 12 | : base(carAds, page, totalPages) 13 | { 14 | } 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /Car Dealers/1. Initial Architecture/Application/Features/CarAds/Queries/Search/SearchCarAdsOutputModel.cs: -------------------------------------------------------------------------------- 1 | namespace CarRentalSystem.Application.Features.CarAds.Queries.Search 2 | { 3 | using System.Collections.Generic; 4 | using Common; 5 | 6 | public class SearchCarAdsOutputModel : CarAdsOutputModel 7 | { 8 | public SearchCarAdsOutputModel( 9 | IEnumerable carAds, 10 | int page, 11 | int totalPages) 12 | : base(carAds, page, totalPages) 13 | { 14 | } 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /Car Dealers/1. Initial Architecture/Application/Features/Dealers/Queries/Common/DealerOutputModel.cs: -------------------------------------------------------------------------------- 1 | namespace CarRentalSystem.Application.Features.Dealers.Queries.Common 2 | { 3 | using AutoMapper; 4 | using Domain.Models.Dealers; 5 | using Mapping; 6 | 7 | public class DealerOutputModel : IMapFrom 8 | { 9 | public int Id { get; private set; } 10 | 11 | public string Name { get; private set; } = default!; 12 | 13 | public string PhoneNumber { get; private set; } = default!; 14 | 15 | public virtual void Mapping(Profile mapper) 16 | => mapper 17 | .CreateMap() 18 | .ForMember(d => d.PhoneNumber, cfg => cfg 19 | .MapFrom(d => d.PhoneNumber.Number)); 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /Car Dealers/1. Initial Architecture/Application/Features/Dealers/Queries/Details/DealerDetailsOutputModel.cs: -------------------------------------------------------------------------------- 1 | namespace CarRentalSystem.Application.Features.Dealers.Queries.Details 2 | { 3 | using AutoMapper; 4 | using Common; 5 | using Domain.Models.Dealers; 6 | 7 | public class DealerDetailsOutputModel : DealerOutputModel 8 | { 9 | public int TotalCarAds { get; private set; } 10 | 11 | public override void Mapping(Profile mapper) 12 | => mapper 13 | .CreateMap() 14 | .IncludeBase() 15 | .ForMember(d => d.TotalCarAds, cfg => cfg 16 | .MapFrom(d => d.CarAds.Count)); 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /Car Dealers/1. Initial Architecture/Application/Features/EntityCommand.cs: -------------------------------------------------------------------------------- 1 | namespace CarRentalSystem.Application.Features 2 | { 3 | public class EntityCommand 4 | { 5 | public TId Id { get; set; } = default!; 6 | } 7 | 8 | public static class EntityCommandExtensions 9 | { 10 | public static TCommand SetId(this TCommand command, TId id) 11 | where TCommand : EntityCommand 12 | { 13 | command.Id = id; 14 | return command; 15 | } 16 | } 17 | } -------------------------------------------------------------------------------- /Car Dealers/1. Initial Architecture/Application/Features/Identity/Commands/ChangePassword/ChangePasswordInputModel.cs: -------------------------------------------------------------------------------- 1 | namespace CarRentalSystem.Application.Features.Identity.Commands.ChangePassword 2 | { 3 | public class ChangePasswordInputModel 4 | { 5 | public ChangePasswordInputModel( 6 | string userId, 7 | string currentPassword, 8 | string newPassword) 9 | { 10 | this.UserId = userId; 11 | this.CurrentPassword = currentPassword; 12 | this.NewPassword = newPassword; 13 | } 14 | 15 | public string UserId { get; } 16 | 17 | public string CurrentPassword { get; } 18 | 19 | public string NewPassword { get; } 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /Car Dealers/1. Initial Architecture/Application/Features/Identity/Commands/CreateUser/CreateUserCommand.Fakes.cs: -------------------------------------------------------------------------------- 1 | namespace CarRentalSystem.Application.Features.Identity.Commands.CreateUser 2 | { 3 | using Bogus; 4 | 5 | public class CreateUserCommandFakes 6 | { 7 | public static class Data 8 | { 9 | public static CreateUserCommand GetCommand() 10 | => new Faker() 11 | .RuleFor(u => u.Email, f => f.Internet.Email()) 12 | .RuleFor(u => u.Password, f => f.Lorem.Letter(10)) 13 | .RuleFor(u => u.Name, f => f.Name.FullName()) 14 | .RuleFor(u => u.PhoneNumber, f => f.Phone.PhoneNumber("+#######")) 15 | .Generate(); 16 | } 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /Car Dealers/1. Initial Architecture/Application/Features/Identity/Commands/LoginUser/LoginOutputModel.cs: -------------------------------------------------------------------------------- 1 | namespace CarRentalSystem.Application.Features.Identity.Commands.LoginUser 2 | { 3 | public class LoginOutputModel 4 | { 5 | public LoginOutputModel(string token, int dealerId) 6 | { 7 | this.Token = token; 8 | this.DealerId = dealerId; 9 | } 10 | 11 | public int DealerId { get; } 12 | 13 | public string Token { get; } 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /Car Dealers/1. Initial Architecture/Application/Features/Identity/Commands/LoginUser/LoginSuccessModel.cs: -------------------------------------------------------------------------------- 1 | namespace CarRentalSystem.Application.Features.Identity.Commands.LoginUser 2 | { 3 | public class LoginSuccessModel 4 | { 5 | public LoginSuccessModel(string userId, string token) 6 | { 7 | this.UserId = userId; 8 | this.Token = token; 9 | } 10 | 11 | public string UserId { get; } 12 | 13 | public string Token { get; } 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /Car Dealers/1. Initial Architecture/Application/Features/Identity/Commands/UserInputModel.cs: -------------------------------------------------------------------------------- 1 | namespace CarRentalSystem.Application.Features.Identity.Commands 2 | { 3 | public abstract class UserInputModel 4 | { 5 | public string Email { get; set; } = default!; 6 | 7 | public string Password { get; set; } = default!; 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /Car Dealers/1. Initial Architecture/Application/Features/Identity/IIdentity.cs: -------------------------------------------------------------------------------- 1 | namespace CarRentalSystem.Application.Features.Identity 2 | { 3 | using System.Threading.Tasks; 4 | using Commands; 5 | using Commands.ChangePassword; 6 | using Commands.LoginUser; 7 | using Common; 8 | 9 | public interface IIdentity 10 | { 11 | Task> Register(UserInputModel userInput); 12 | 13 | Task> Login(UserInputModel userInput); 14 | 15 | Task ChangePassword(ChangePasswordInputModel changePasswordInput); 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /Car Dealers/1. Initial Architecture/Application/Features/Identity/IUser.cs: -------------------------------------------------------------------------------- 1 | namespace CarRentalSystem.Application.Features.Identity 2 | { 3 | using Domain.Models.Dealers; 4 | 5 | public interface IUser 6 | { 7 | void BecomeDealer(Dealer dealer); 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /Car Dealers/1. Initial Architecture/Application/Mapping/IMapFrom.cs: -------------------------------------------------------------------------------- 1 | namespace CarRentalSystem.Application.Mapping 2 | { 3 | using AutoMapper; 4 | 5 | public interface IMapFrom 6 | { 7 | void Mapping(Profile mapper) => mapper.CreateMap(typeof(T), this.GetType()); 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /Car Dealers/1. Initial Architecture/Domain/Common/IAggregateRoot.cs: -------------------------------------------------------------------------------- 1 | namespace CarRentalSystem.Domain.Common 2 | { 3 | public interface IAggregateRoot 4 | { 5 | } 6 | } 7 | -------------------------------------------------------------------------------- /Car Dealers/1. Initial Architecture/Domain/Common/IInitialData.cs: -------------------------------------------------------------------------------- 1 | namespace CarRentalSystem.Domain.Common 2 | { 3 | using System; 4 | using System.Collections.Generic; 5 | 6 | public interface IInitialData 7 | { 8 | Type EntityType { get; } 9 | 10 | IEnumerable GetData(); 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /Car Dealers/1. Initial Architecture/Domain/DomainConfiguration.cs: -------------------------------------------------------------------------------- 1 | namespace CarRentalSystem.Domain 2 | { 3 | using Common; 4 | using Factories; 5 | using Microsoft.Extensions.DependencyInjection; 6 | using Models.CarAds; 7 | 8 | public static class DomainConfiguration 9 | { 10 | public static IServiceCollection AddDomain(this IServiceCollection services) 11 | => services 12 | .Scan(scan => scan 13 | .FromCallingAssembly() 14 | .AddClasses(classes => classes 15 | .AssignableTo(typeof(IFactory<>))) 16 | .AsMatchingInterface() 17 | .WithTransientLifetime()) 18 | .AddTransient(); 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /Car Dealers/1. Initial Architecture/Domain/Exceptions/BaseDomainException.cs: -------------------------------------------------------------------------------- 1 | namespace CarRentalSystem.Domain.Exceptions 2 | { 3 | using System; 4 | 5 | public abstract class BaseDomainException : Exception 6 | { 7 | private string? error; 8 | 9 | public string Error 10 | { 11 | get => this.error ?? base.Message; 12 | set => this.error = value; 13 | } 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /Car Dealers/1. Initial Architecture/Domain/Exceptions/InvalidCarAdException.cs: -------------------------------------------------------------------------------- 1 | namespace CarRentalSystem.Domain.Exceptions 2 | { 3 | public class InvalidCarAdException : BaseDomainException 4 | { 5 | public InvalidCarAdException() 6 | { 7 | } 8 | 9 | public InvalidCarAdException(string error) => this.Error = error; 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /Car Dealers/1. Initial Architecture/Domain/Exceptions/InvalidDealerException.cs: -------------------------------------------------------------------------------- 1 | namespace CarRentalSystem.Domain.Exceptions 2 | { 3 | public class InvalidDealerException : BaseDomainException 4 | { 5 | public InvalidDealerException() 6 | { 7 | } 8 | 9 | public InvalidDealerException(string error) => this.Error = error; 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /Car Dealers/1. Initial Architecture/Domain/Exceptions/InvalidOptionsException.cs: -------------------------------------------------------------------------------- 1 | namespace CarRentalSystem.Domain.Exceptions 2 | { 3 | public class InvalidOptionsException : BaseDomainException 4 | { 5 | public InvalidOptionsException() 6 | { 7 | } 8 | 9 | public InvalidOptionsException(string error) => this.Error = error; 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /Car Dealers/1. Initial Architecture/Domain/Exceptions/InvalidPhoneNumberException.cs: -------------------------------------------------------------------------------- 1 | namespace CarRentalSystem.Domain.Exceptions 2 | { 3 | public class InvalidPhoneNumberException : BaseDomainException 4 | { 5 | public InvalidPhoneNumberException() 6 | { 7 | } 8 | 9 | public InvalidPhoneNumberException(string error) => this.Error = error; 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /Car Dealers/1. Initial Architecture/Domain/Factories/Dealers/IDealerFactory.cs: -------------------------------------------------------------------------------- 1 | namespace CarRentalSystem.Domain.Factories.Dealers 2 | { 3 | using Models.Dealers; 4 | 5 | public interface IDealerFactory : IFactory 6 | { 7 | IDealerFactory WithName(string name); 8 | 9 | IDealerFactory WithPhoneNumber(string phoneNumber); 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /Car Dealers/1. Initial Architecture/Domain/Factories/IFactory.cs: -------------------------------------------------------------------------------- 1 | namespace CarRentalSystem.Domain.Factories 2 | { 3 | using Common; 4 | 5 | public interface IFactory 6 | where TEntity : IAggregateRoot 7 | { 8 | TEntity Build(); 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /Car Dealers/1. Initial Architecture/Domain/Models/CarAds/CarAd.Specs.cs: -------------------------------------------------------------------------------- 1 | namespace CarRentalSystem.Domain.Models.CarAds 2 | { 3 | using FakeItEasy; 4 | using FluentAssertions; 5 | using Xunit; 6 | 7 | public class CarAdSpecs 8 | { 9 | [Fact] 10 | public void ChangeAvailabilityShouldMutateIsAvailable() 11 | { 12 | // Arrange 13 | var carAd = A.Dummy(); 14 | 15 | // Act 16 | carAd.ChangeAvailability(); 17 | 18 | // Assert 19 | carAd.IsAvailable.Should().BeFalse(); 20 | } 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /Car Dealers/1. Initial Architecture/Domain/Models/CarAds/Category.Fakes.cs: -------------------------------------------------------------------------------- 1 | namespace CarRentalSystem.Domain.Models.CarAds 2 | { 3 | using System; 4 | using FakeItEasy; 5 | 6 | public class CategoryFakes 7 | { 8 | public const string ValidCategoryName = "Economy"; 9 | 10 | public class CategoryDummyFactory : IDummyFactory 11 | { 12 | public bool CanCreate(Type type) => type == typeof(Category); 13 | 14 | public object? Create(Type type) 15 | => new Category(ValidCategoryName, "Valid description text"); 16 | 17 | public Priority Priority => Priority.Default; 18 | } 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /Car Dealers/1. Initial Architecture/Domain/Models/CarAds/Manufacturer.Fakes.cs: -------------------------------------------------------------------------------- 1 | namespace CarRentalSystem.Domain.Models.CarAds 2 | { 3 | using System; 4 | using FakeItEasy; 5 | 6 | public class ManufacturerFakes 7 | { 8 | public class ManufacturerDummyFactory : IDummyFactory 9 | { 10 | public bool CanCreate(Type type) => type == typeof(Manufacturer); 11 | 12 | public object? Create(Type type) 13 | => new Manufacturer("Valid manufacturer"); 14 | 15 | public Priority Priority => Priority.Default; 16 | } 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /Car Dealers/1. Initial Architecture/Domain/Models/CarAds/Options.Fakes.cs: -------------------------------------------------------------------------------- 1 | namespace CarRentalSystem.Domain.Models.CarAds 2 | { 3 | using System; 4 | using FakeItEasy; 5 | 6 | public class OptionsFakes 7 | { 8 | public class OptionsDummyFactory : IDummyFactory 9 | { 10 | public bool CanCreate(Type type) => type == typeof(Options); 11 | 12 | public object? Create(Type type) 13 | => new Options(true, 4, TransmissionType.Automatic); 14 | 15 | public Priority Priority => Priority.Default; 16 | } 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /Car Dealers/1. Initial Architecture/Domain/Models/CarAds/TransmissionType.cs: -------------------------------------------------------------------------------- 1 | namespace CarRentalSystem.Domain.Models.CarAds 2 | { 3 | using Common; 4 | 5 | public class TransmissionType : Enumeration 6 | { 7 | public static readonly TransmissionType Manual = new TransmissionType(1, nameof(Manual)); 8 | public static readonly TransmissionType Automatic = new TransmissionType(2, nameof(Automatic)); 9 | 10 | private TransmissionType(int value) 11 | : this(value, FromValue(value).Name) 12 | { 13 | } 14 | 15 | private TransmissionType(int value, string name) 16 | : base(value, name) 17 | { 18 | } 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /Car Dealers/1. Initial Architecture/Domain/Models/Dealers/Dealer.Specs.cs: -------------------------------------------------------------------------------- 1 | namespace CarRentalSystem.Domain.Models.Dealers 2 | { 3 | using CarAds; 4 | using FakeItEasy; 5 | using FluentAssertions; 6 | using Xunit; 7 | 8 | public class DealerSpecs 9 | { 10 | [Fact] 11 | public void AddCarAdShouldSaveCarAd() 12 | { 13 | // Arrange 14 | var dealer = new Dealer("Valid dealer", "+12345678"); 15 | var carAd = A.Dummy(); 16 | 17 | // Act 18 | dealer.AddCarAd(carAd); 19 | 20 | // Assert 21 | dealer.CarAds.Should().Contain(carAd); 22 | } 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /Car Dealers/1. Initial Architecture/Domain/Specifications/CarAds/CadAdOnlyAvailableSpecification.cs: -------------------------------------------------------------------------------- 1 | namespace CarRentalSystem.Domain.Specifications.CarAds 2 | { 3 | using System; 4 | using System.Linq.Expressions; 5 | using Models.CarAds; 6 | 7 | public class CadAdOnlyAvailableSpecification : Specification 8 | { 9 | private readonly bool onlyAvailable; 10 | 11 | public CadAdOnlyAvailableSpecification(bool onlyAvailable) 12 | => this.onlyAvailable = onlyAvailable; 13 | 14 | public override Expression> ToExpression() 15 | { 16 | if (this.onlyAvailable) 17 | { 18 | return carAd => carAd.IsAvailable; 19 | } 20 | 21 | return carAd => true; 22 | } 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /Car Dealers/1. Initial Architecture/Domain/Specifications/CarAds/CarAdByCategorySpecification.cs: -------------------------------------------------------------------------------- 1 | namespace CarRentalSystem.Domain.Specifications.CarAds 2 | { 3 | using System; 4 | using System.Linq.Expressions; 5 | using Models.CarAds; 6 | 7 | public class CarAdByCategorySpecification : Specification 8 | { 9 | private readonly int? category; 10 | 11 | public CarAdByCategorySpecification(int? category) 12 | => this.category = category; 13 | 14 | protected override bool Include => this.category != null; 15 | 16 | public override Expression> ToExpression() 17 | => carAd => carAd.Category.Id == this.category; 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /Car Dealers/1. Initial Architecture/Domain/Specifications/CarAds/CarAdByManufacturerSpecification.cs: -------------------------------------------------------------------------------- 1 | namespace CarRentalSystem.Domain.Specifications.CarAds 2 | { 3 | using System; 4 | using System.Linq.Expressions; 5 | using Models.CarAds; 6 | 7 | public class CarAdByManufacturerSpecification : Specification 8 | { 9 | private readonly string? manufacturer; 10 | 11 | public CarAdByManufacturerSpecification(string? manufacturer) 12 | => this.manufacturer = manufacturer; 13 | 14 | protected override bool Include => this.manufacturer != null; 15 | 16 | public override Expression> ToExpression() 17 | => carAd => carAd.Manufacturer.Name.ToLower() 18 | .Contains(this.manufacturer!.ToLower()); 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /Car Dealers/1. Initial Architecture/Domain/Specifications/Dealers/DealerByIdSpecification.cs: -------------------------------------------------------------------------------- 1 | namespace CarRentalSystem.Domain.Specifications.Dealers 2 | { 3 | using System; 4 | using System.Linq.Expressions; 5 | using Models.Dealers; 6 | 7 | public class DealerByIdSpecification : Specification 8 | { 9 | private readonly int? id; 10 | 11 | public DealerByIdSpecification(int? id) 12 | => this.id = id; 13 | 14 | protected override bool Include => this.id != null; 15 | 16 | public override Expression> ToExpression() 17 | => dealer => dealer.Id == this.id; 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /Car Dealers/1. Initial Architecture/Domain/Specifications/Dealers/DealerByNameSpecification.cs: -------------------------------------------------------------------------------- 1 | namespace CarRentalSystem.Domain.Specifications.Dealers 2 | { 3 | using System; 4 | using System.Linq.Expressions; 5 | using Models.Dealers; 6 | 7 | public class DealerByNameSpecification : Specification 8 | { 9 | private readonly string? name; 10 | 11 | public DealerByNameSpecification(string? name) 12 | => this.name = name; 13 | 14 | protected override bool Include => this.name != null; 15 | 16 | public override Expression> ToExpression() 17 | => dealer => dealer.Name.ToLower().Contains(this.name!.ToLower()); 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /Car Dealers/1. Initial Architecture/Infrastructure/Common/QueryableExtensions.cs: -------------------------------------------------------------------------------- 1 | namespace CarRentalSystem.Infrastructure.Common 2 | { 3 | using System.Linq; 4 | using Application.Common; 5 | 6 | public static class QueryableExtensions 7 | { 8 | public static IQueryable Sort( 9 | this IQueryable queryable, 10 | SortOrder sortOrder) 11 | => sortOrder.Order == SortOrder.Descending 12 | ? queryable.OrderByDescending(sortOrder.ToExpression()) 13 | : queryable.OrderBy(sortOrder.ToExpression()); 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /Car Dealers/1. Initial Architecture/Infrastructure/IInitializer.cs: -------------------------------------------------------------------------------- 1 | namespace CarRentalSystem.Infrastructure 2 | { 3 | public interface IInitializer 4 | { 5 | void Initialize(); 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /Car Dealers/1. Initial Architecture/Infrastructure/Identity/IJwtTokenGenerator.cs: -------------------------------------------------------------------------------- 1 | namespace CarRentalSystem.Infrastructure.Identity 2 | { 3 | public interface IJwtTokenGenerator 4 | { 5 | string GenerateToken(User user); 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /Car Dealers/1. Initial Architecture/Infrastructure/Identity/JwtTokenGeneratorService.Fakes.cs: -------------------------------------------------------------------------------- 1 | namespace CarRentalSystem.Infrastructure.Identity 2 | { 3 | using FakeItEasy; 4 | 5 | public class JwtTokenGeneratorFakes 6 | { 7 | public const string ValidToken = "ValidToken"; 8 | 9 | public static IJwtTokenGenerator FakeJwtTokenGenerator 10 | { 11 | get 12 | { 13 | var jwtTokenGenerator = A.Fake(); 14 | 15 | A 16 | .CallTo(() => jwtTokenGenerator.GenerateToken(A.Ignored)) 17 | .Returns(ValidToken); 18 | 19 | return jwtTokenGenerator; 20 | } 21 | } 22 | } 23 | } -------------------------------------------------------------------------------- /Car Dealers/1. Initial Architecture/Infrastructure/Identity/User.cs: -------------------------------------------------------------------------------- 1 | namespace CarRentalSystem.Infrastructure.Identity 2 | { 3 | using Application.Features.Identity; 4 | using Domain.Exceptions; 5 | using Domain.Models.Dealers; 6 | using Microsoft.AspNetCore.Identity; 7 | 8 | public class User : IdentityUser, IUser 9 | { 10 | internal User(string email) 11 | : base(email) 12 | => this.Email = email; 13 | 14 | public Dealer? Dealer { get; private set; } 15 | 16 | public void BecomeDealer(Dealer dealer) 17 | { 18 | if (this.Dealer != null) 19 | { 20 | throw new InvalidDealerException($"User '{this.UserName}' is already a dealer."); 21 | } 22 | 23 | this.Dealer = dealer; 24 | } 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /Car Dealers/1. Initial Architecture/Infrastructure/Persistence/Configurations/ManufacturerConfiguration.cs: -------------------------------------------------------------------------------- 1 | namespace CarRentalSystem.Infrastructure.Persistence.Configurations 2 | { 3 | using Domain.Models.CarAds; 4 | using Microsoft.EntityFrameworkCore; 5 | using Microsoft.EntityFrameworkCore.Metadata.Builders; 6 | 7 | using static Domain.Models.ModelConstants.Common; 8 | 9 | internal class ManufacturerConfiguration : IEntityTypeConfiguration 10 | { 11 | public void Configure(EntityTypeBuilder builder) 12 | { 13 | builder 14 | .HasKey(m => m.Id); 15 | 16 | builder 17 | .Property(m => m.Name) 18 | .IsRequired() 19 | .HasMaxLength(MaxNameLength); 20 | } 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /Car Dealers/1. Initial Architecture/Infrastructure/Persistence/Configurations/UserConfiguration.cs: -------------------------------------------------------------------------------- 1 | namespace CarRentalSystem.Infrastructure.Persistence.Configurations 2 | { 3 | using Identity; 4 | using Microsoft.EntityFrameworkCore; 5 | using Microsoft.EntityFrameworkCore.Metadata.Builders; 6 | 7 | public class UserConfiguration : IEntityTypeConfiguration 8 | { 9 | public void Configure(EntityTypeBuilder builder) 10 | { 11 | builder 12 | .HasOne(u => u.Dealer) 13 | .WithOne() 14 | .HasForeignKey("DealerId") 15 | .OnDelete(DeleteBehavior.Restrict); 16 | } 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /Car Dealers/1. Initial Architecture/Startup/ApplicationInitialization.cs: -------------------------------------------------------------------------------- 1 | namespace CarRentalSystem.Startup 2 | { 3 | using Infrastructure; 4 | using Microsoft.AspNetCore.Builder; 5 | using Microsoft.Extensions.DependencyInjection; 6 | 7 | public static class ApplicationInitialization 8 | { 9 | public static IApplicationBuilder Initialize(this IApplicationBuilder app) 10 | { 11 | using var serviceScope = app.ApplicationServices.CreateScope(); 12 | 13 | var initializers = serviceScope.ServiceProvider.GetServices(); 14 | 15 | foreach (var initializer in initializers) 16 | { 17 | initializer.Initialize(); 18 | } 19 | 20 | return app; 21 | } 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /Car Dealers/1. Initial Architecture/Startup/Program.cs: -------------------------------------------------------------------------------- 1 | namespace CarRentalSystem.Startup 2 | { 3 | using Microsoft.AspNetCore.Hosting; 4 | using Microsoft.Extensions.Hosting; 5 | 6 | public class Program 7 | { 8 | public static void Main(string[] args) => CreateHostBuilder(args).Build().Run(); 9 | 10 | public static IHostBuilder CreateHostBuilder(string[] args) 11 | => Host 12 | .CreateDefaultBuilder(args) 13 | .ConfigureWebHostDefaults(webBuilder => 14 | { 15 | webBuilder.UseStartup(); 16 | }); 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /Car Dealers/1. Initial Architecture/Startup/Specs/DealersController.Specs.cs: -------------------------------------------------------------------------------- 1 | namespace CarRentalSystem.Startup.Specs 2 | { 3 | using Application.Features.Dealers.Queries.Details; 4 | using MyTested.AspNetCore.Mvc; 5 | using Web; 6 | using Web.Features; 7 | using Xunit; 8 | 9 | public class DealersControllerSpecs 10 | { 11 | [Fact] 12 | public void DetailsShouldHaveCorrectAttributes() 13 | => MyController 14 | .Calling(c => c.Details(With.Default())) 15 | 16 | .ShouldHave() 17 | .ActionAttributes(attr => attr 18 | .RestrictingForHttpMethod(HttpMethod.Get) 19 | .SpecifyingRoute(ApiController.Id)); 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /Car Dealers/1. Initial Architecture/Startup/appsettings.Development.json: -------------------------------------------------------------------------------- 1 | { 2 | "Logging": { 3 | "LogLevel": { 4 | "Default": "Information", 5 | "Microsoft": "Warning", 6 | "Microsoft.Hosting.Lifetime": "Information" 7 | } 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /Car Dealers/1. Initial Architecture/Startup/appsettings.json: -------------------------------------------------------------------------------- 1 | { 2 | "ApplicationSettings": { 3 | "Secret": "S0M3 M4G1C UN1C0RNS G3N3R4T3D TH1S S3CR3T" 4 | }, 5 | "ConnectionStrings": { 6 | "DefaultConnection": "Server=.;Database=CarRentalSystem;Trusted_Connection=True;MultipleActiveResultSets=true" 7 | }, 8 | "Logging": { 9 | "LogLevel": { 10 | "Default": "Information", 11 | "Microsoft": "Warning", 12 | "Microsoft.Hosting.Lifetime": "Information" 13 | } 14 | }, 15 | "AllowedHosts": "*" 16 | } 17 | -------------------------------------------------------------------------------- /Car Dealers/1. Initial Architecture/Startup/testsettings.json: -------------------------------------------------------------------------------- 1 | { 2 | "General": { 3 | "WebAssemblyName": "CarRentalSystem.Startup" 4 | }, 5 | "ApplicationSettings": { 6 | "Secret": "My Fake Secret" 7 | }, 8 | "ConnectionStrings": { 9 | "DefaultConnection": "My Fake Connection String" 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /Car Dealers/1. Initial Architecture/Web/Services/CurrentUserService.cs: -------------------------------------------------------------------------------- 1 | namespace CarRentalSystem.Web.Services 2 | { 3 | using System; 4 | using System.Security.Claims; 5 | using Application.Contracts; 6 | using Microsoft.AspNetCore.Http; 7 | 8 | public class CurrentUserService : ICurrentUser 9 | { 10 | public CurrentUserService(IHttpContextAccessor httpContextAccessor) 11 | { 12 | var user = httpContextAccessor.HttpContext?.User; 13 | 14 | if (user == null) 15 | { 16 | throw new InvalidOperationException("This request does not have an authenticated user."); 17 | } 18 | 19 | this.UserId = user.FindFirstValue(ClaimTypes.NameIdentifier); 20 | } 21 | 22 | public string UserId { get; } 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /Car Dealers/2. Common Functionality/Application/Common/ApplicationSettings.cs: -------------------------------------------------------------------------------- 1 | namespace CarRentalSystem.Application.Common 2 | { 3 | public class ApplicationSettings 4 | { 5 | public ApplicationSettings() => this.Secret = default!; 6 | 7 | public string Secret { get; private set; } 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /Car Dealers/2. Common Functionality/Application/Common/Contracts/ICurrentUser.cs: -------------------------------------------------------------------------------- 1 | namespace CarRentalSystem.Application.Common.Contracts 2 | { 3 | public interface ICurrentUser 4 | { 5 | string UserId { get; } 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /Car Dealers/2. Common Functionality/Application/Common/Contracts/IRepository.cs: -------------------------------------------------------------------------------- 1 | namespace CarRentalSystem.Application.Common.Contracts 2 | { 3 | using System.Threading; 4 | using System.Threading.Tasks; 5 | using Domain.Common; 6 | 7 | public interface IRepository 8 | where TEntity : IAggregateRoot 9 | { 10 | Task Save(TEntity entity, CancellationToken cancellationToken = default); 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /Car Dealers/2. Common Functionality/Application/Common/EntityCommand.cs: -------------------------------------------------------------------------------- 1 | namespace CarRentalSystem.Application.Common 2 | { 3 | public class EntityCommand 4 | { 5 | public TId Id { get; set; } = default!; 6 | } 7 | 8 | public static class EntityCommandExtensions 9 | { 10 | public static TCommand SetId(this TCommand command, TId id) 11 | where TCommand : EntityCommand 12 | { 13 | command.Id = id; 14 | return command; 15 | } 16 | } 17 | } -------------------------------------------------------------------------------- /Car Dealers/2. Common Functionality/Application/Common/Exceptions/NotFoundException.cs: -------------------------------------------------------------------------------- 1 | namespace CarRentalSystem.Application.Common.Exceptions 2 | { 3 | using System; 4 | 5 | public class NotFoundException : Exception 6 | { 7 | public NotFoundException(string name, object key) 8 | : base($"Entity '{name}' ({key}) was not found.") 9 | { 10 | } 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /Car Dealers/2. Common Functionality/Application/Common/IEventHandler.cs: -------------------------------------------------------------------------------- 1 | namespace CarRentalSystem.Application.Common 2 | { 3 | using System.Threading.Tasks; 4 | using Domain.Common; 5 | 6 | public interface IEventHandler 7 | where TEvent : IDomainEvent 8 | { 9 | Task Handle(TEvent domainEvent); 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /Car Dealers/2. Common Functionality/Application/Common/Mapping/IMapFrom.cs: -------------------------------------------------------------------------------- 1 | namespace CarRentalSystem.Application.Common.Mapping 2 | { 3 | using AutoMapper; 4 | 5 | public interface IMapFrom 6 | { 7 | void Mapping(Profile mapper) => mapper.CreateMap(typeof(T), this.GetType()); 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /Car Dealers/2. Common Functionality/Application/Common/SortOrder.cs: -------------------------------------------------------------------------------- 1 | namespace CarRentalSystem.Application.Common 2 | { 3 | using System; 4 | using System.Linq.Expressions; 5 | 6 | public abstract class SortOrder 7 | { 8 | public const string Ascending = "asc"; 9 | public const string Descending = "desc"; 10 | 11 | protected SortOrder(string? sortBy, string? order) 12 | { 13 | this.SortBy = sortBy; 14 | this.Order = order; 15 | } 16 | 17 | public string? SortBy { get; } 18 | 19 | public string? Order { get; } 20 | 21 | public abstract Expression> ToExpression(); 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /Car Dealers/2. Common Functionality/Application/Dealerships/CarAds/Commands/Common/CarAdCommand.cs: -------------------------------------------------------------------------------- 1 | namespace CarRentalSystem.Application.Dealerships.CarAds.Commands.Common 2 | { 3 | using Application.Common; 4 | 5 | public abstract class CarAdCommand : EntityCommand 6 | where TCommand : EntityCommand 7 | { 8 | public string Manufacturer { get; set; } = default!; 9 | 10 | public string Model { get; set; } = default!; 11 | 12 | public int Category { get; set; } 13 | 14 | public string ImageUrl { get; set; } = default!; 15 | 16 | public decimal PricePerDay { get; set; } 17 | 18 | public bool HasClimateControl { get; set; } 19 | 20 | public int NumberOfSeats { get; set; } 21 | 22 | public int TransmissionType { get; set; } 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /Car Dealers/2. Common Functionality/Application/Dealerships/CarAds/Commands/Create/CreateCarAdCommandValidator.cs: -------------------------------------------------------------------------------- 1 | namespace CarRentalSystem.Application.Dealerships.CarAds.Commands.Create 2 | { 3 | using Common; 4 | using FluentValidation; 5 | 6 | public class CreateCarAdCommandValidator : AbstractValidator 7 | { 8 | public CreateCarAdCommandValidator(ICarAdRepository carAdRepository) 9 | => this.Include(new CarAdCommandValidator(carAdRepository)); 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /Car Dealers/2. Common Functionality/Application/Dealerships/CarAds/Commands/Create/CreateCarAdOutputModel.cs: -------------------------------------------------------------------------------- 1 | namespace CarRentalSystem.Application.Dealerships.CarAds.Commands.Create 2 | { 3 | public class CreateCarAdOutputModel 4 | { 5 | public CreateCarAdOutputModel(int carAdId) 6 | => this.CarAdId = carAdId; 7 | 8 | public int CarAdId { get; } 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /Car Dealers/2. Common Functionality/Application/Dealerships/CarAds/Commands/Edit/EditCarAdCommandValidator.cs: -------------------------------------------------------------------------------- 1 | namespace CarRentalSystem.Application.Dealerships.CarAds.Commands.Edit 2 | { 3 | using Common; 4 | using FluentValidation; 5 | 6 | public class EditCarAdCommandValidator : AbstractValidator 7 | { 8 | public EditCarAdCommandValidator(ICarAdRepository carAdRepository) 9 | => this.Include(new CarAdCommandValidator(carAdRepository)); 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /Car Dealers/2. Common Functionality/Application/Dealerships/CarAds/Queries/Categories/GetCarAdCategoryOutputModel.cs: -------------------------------------------------------------------------------- 1 | namespace CarRentalSystem.Application.Dealerships.CarAds.Queries.Categories 2 | { 3 | using Application.Common.Mapping; 4 | using Domain.Dealerships.Models.CarAds; 5 | 6 | public class GetCarAdCategoryOutputModel : IMapFrom 7 | { 8 | public int Id { get; private set; } 9 | 10 | public string Name { get; private set; } = default!; 11 | 12 | public string Description { get; private set; } = default!; 13 | 14 | public int TotalCarAds { get; set; } 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /Car Dealers/2. Common Functionality/Application/Dealerships/CarAds/Queries/Common/CarAdsOutputModel.cs: -------------------------------------------------------------------------------- 1 | namespace CarRentalSystem.Application.Dealerships.CarAds.Queries.Common 2 | { 3 | using System.Collections.Generic; 4 | 5 | public abstract class CarAdsOutputModel 6 | { 7 | internal CarAdsOutputModel( 8 | IEnumerable carAds, 9 | int page, 10 | int totalPages) 11 | { 12 | this.CarAds = carAds; 13 | this.Page = page; 14 | this.TotalPages = totalPages; 15 | } 16 | 17 | public IEnumerable CarAds { get; } 18 | 19 | public int Page { get; } 20 | 21 | public int TotalPages { get; } 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /Car Dealers/2. Common Functionality/Application/Dealerships/CarAds/Queries/Mine/MineCarAdOutputModel.cs: -------------------------------------------------------------------------------- 1 | namespace CarRentalSystem.Application.Dealerships.CarAds.Queries.Mine 2 | { 3 | using AutoMapper; 4 | using Common; 5 | using Domain.Dealerships.Models.CarAds; 6 | 7 | public class MineCarAdOutputModel : CarAdOutputModel 8 | { 9 | public bool IsAvailable { get; private set; } 10 | 11 | public override void Mapping(Profile mapper) 12 | => mapper 13 | .CreateMap() 14 | .IncludeBase(); 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /Car Dealers/2. Common Functionality/Application/Dealerships/CarAds/Queries/Mine/MineCarAdsOutputModel.cs: -------------------------------------------------------------------------------- 1 | namespace CarRentalSystem.Application.Dealerships.CarAds.Queries.Mine 2 | { 3 | using System.Collections.Generic; 4 | using Common; 5 | 6 | public class MineCarAdsOutputModel : CarAdsOutputModel 7 | { 8 | public MineCarAdsOutputModel( 9 | IEnumerable carAds, 10 | int page, 11 | int totalPages) 12 | : base(carAds, page, totalPages) 13 | { 14 | } 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /Car Dealers/2. Common Functionality/Application/Dealerships/CarAds/Queries/Search/SearchCarAdsOutputModel.cs: -------------------------------------------------------------------------------- 1 | namespace CarRentalSystem.Application.Dealerships.CarAds.Queries.Search 2 | { 3 | using System.Collections.Generic; 4 | using Common; 5 | 6 | public class SearchCarAdsOutputModel : CarAdsOutputModel 7 | { 8 | public SearchCarAdsOutputModel( 9 | IEnumerable carAds, 10 | int page, 11 | int totalPages) 12 | : base(carAds, page, totalPages) 13 | { 14 | } 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /Car Dealers/2. Common Functionality/Application/Dealerships/Dealers/Queries/Details/DealerDetailsOutputModel.cs: -------------------------------------------------------------------------------- 1 | namespace CarRentalSystem.Application.Dealerships.Dealers.Queries.Details 2 | { 3 | using AutoMapper; 4 | using Common; 5 | using Domain.Dealerships.Models.Dealers; 6 | 7 | public class DealerDetailsOutputModel : DealerOutputModel 8 | { 9 | public int TotalCarAds { get; private set; } 10 | 11 | public override void Mapping(Profile mapper) 12 | => mapper 13 | .CreateMap() 14 | .IncludeBase() 15 | .ForMember(d => d.TotalCarAds, cfg => cfg 16 | .MapFrom(d => d.CarAds.Count)); 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /Car Dealers/2. Common Functionality/Application/Identity/Commands/ChangePassword/ChangePasswordInputModel.cs: -------------------------------------------------------------------------------- 1 | namespace CarRentalSystem.Application.Identity.Commands.ChangePassword 2 | { 3 | public class ChangePasswordInputModel 4 | { 5 | public ChangePasswordInputModel( 6 | string userId, 7 | string currentPassword, 8 | string newPassword) 9 | { 10 | this.UserId = userId; 11 | this.CurrentPassword = currentPassword; 12 | this.NewPassword = newPassword; 13 | } 14 | 15 | public string UserId { get; } 16 | 17 | public string CurrentPassword { get; } 18 | 19 | public string NewPassword { get; } 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /Car Dealers/2. Common Functionality/Application/Identity/Commands/CreateUser/CreateUserCommand.Fakes.cs: -------------------------------------------------------------------------------- 1 | namespace CarRentalSystem.Application.Identity.Commands.CreateUser 2 | { 3 | using Bogus; 4 | 5 | public class CreateUserCommandFakes 6 | { 7 | public static class Data 8 | { 9 | public static CreateUserCommand GetCommand() 10 | => new Faker() 11 | .RuleFor(u => u.Email, f => f.Internet.Email()) 12 | .RuleFor(u => u.Password, f => f.Lorem.Letter(10)) 13 | .RuleFor(u => u.Name, f => f.Name.FullName()) 14 | .RuleFor(u => u.PhoneNumber, f => f.Phone.PhoneNumber("+#######")) 15 | .Generate(); 16 | } 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /Car Dealers/2. Common Functionality/Application/Identity/Commands/LoginUser/LoginOutputModel.cs: -------------------------------------------------------------------------------- 1 | namespace CarRentalSystem.Application.Identity.Commands.LoginUser 2 | { 3 | public class LoginOutputModel 4 | { 5 | public LoginOutputModel(string token, int dealerId) 6 | { 7 | this.Token = token; 8 | this.DealerId = dealerId; 9 | } 10 | 11 | public int DealerId { get; } 12 | 13 | public string Token { get; } 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /Car Dealers/2. Common Functionality/Application/Identity/Commands/LoginUser/LoginSuccessModel.cs: -------------------------------------------------------------------------------- 1 | namespace CarRentalSystem.Application.Identity.Commands.LoginUser 2 | { 3 | public class LoginSuccessModel 4 | { 5 | public LoginSuccessModel(string userId, string token) 6 | { 7 | this.UserId = userId; 8 | this.Token = token; 9 | } 10 | 11 | public string UserId { get; } 12 | 13 | public string Token { get; } 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /Car Dealers/2. Common Functionality/Application/Identity/Commands/UserInputModel.cs: -------------------------------------------------------------------------------- 1 | namespace CarRentalSystem.Application.Identity.Commands 2 | { 3 | public abstract class UserInputModel 4 | { 5 | public string Email { get; set; } = default!; 6 | 7 | public string Password { get; set; } = default!; 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /Car Dealers/2. Common Functionality/Application/Identity/IIdentity.cs: -------------------------------------------------------------------------------- 1 | namespace CarRentalSystem.Application.Identity 2 | { 3 | using System.Threading.Tasks; 4 | using Commands; 5 | using Commands.ChangePassword; 6 | using Commands.LoginUser; 7 | using Common; 8 | 9 | public interface IIdentity 10 | { 11 | Task> Register(UserInputModel userInput); 12 | 13 | Task> Login(UserInputModel userInput); 14 | 15 | Task ChangePassword(ChangePasswordInputModel changePasswordInput); 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /Car Dealers/2. Common Functionality/Application/Identity/IUser.cs: -------------------------------------------------------------------------------- 1 | namespace CarRentalSystem.Application.Identity 2 | { 3 | using Domain.Dealerships.Models.Dealers; 4 | 5 | public interface IUser 6 | { 7 | void BecomeDealer(Dealer dealer); 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /Car Dealers/2. Common Functionality/Application/Statistics/Handlers/CarAdAddedEventHandler.cs: -------------------------------------------------------------------------------- 1 | namespace CarRentalSystem.Application.Statistics.Handlers 2 | { 3 | using System.Threading.Tasks; 4 | using Common; 5 | using Domain.Dealerships.Events.Dealers; 6 | 7 | public class CarAdAddedEventHandler : IEventHandler 8 | { 9 | private readonly IStatisticsRepository statistics; 10 | 11 | public CarAdAddedEventHandler(IStatisticsRepository statistics) 12 | => this.statistics = statistics; 13 | 14 | public Task Handle(CarAdAddedEvent domainEvent) 15 | => this.statistics.IncrementCarAds(); 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /Car Dealers/2. Common Functionality/Application/Statistics/IStatisticsRepository.cs: -------------------------------------------------------------------------------- 1 | namespace CarRentalSystem.Application.Statistics 2 | { 3 | using System.Threading; 4 | using System.Threading.Tasks; 5 | using Common.Contracts; 6 | using Domain.Statistics.Models; 7 | using Queries.Current; 8 | 9 | public interface IStatisticsRepository : IRepository 10 | { 11 | Task GetCurrent(CancellationToken cancellationToken = default); 12 | 13 | Task GetCarAdViews(int carAdId, CancellationToken cancellationToken = default); 14 | 15 | Task IncrementCarAds(CancellationToken cancellationToken = default); 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /Car Dealers/2. Common Functionality/Application/Statistics/Queries/Current/GetCurrentStatisticsOutputModel.cs: -------------------------------------------------------------------------------- 1 | namespace CarRentalSystem.Application.Statistics.Queries.Current 2 | { 3 | using AutoMapper; 4 | using Common.Mapping; 5 | using Domain.Statistics.Models; 6 | 7 | public class GetCurrentStatisticsOutputModel : IMapFrom 8 | { 9 | public int TotalCarAds { get; private set; } 10 | 11 | public int TotalCarAdViews { get; private set; } 12 | 13 | public void Mapping(Profile mapper) 14 | => mapper 15 | .CreateMap() 16 | .ForMember(cs => cs.TotalCarAds, cfg => cfg 17 | .MapFrom(s => s.CarAdViews.Count)); 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /Car Dealers/2. Common Functionality/Domain/Common/BaseDomainException.cs: -------------------------------------------------------------------------------- 1 | namespace CarRentalSystem.Domain.Common 2 | { 3 | using System; 4 | 5 | public abstract class BaseDomainException : Exception 6 | { 7 | private string? error; 8 | 9 | public string Error 10 | { 11 | get => this.error ?? base.Message; 12 | set => this.error = value; 13 | } 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /Car Dealers/2. Common Functionality/Domain/Common/IAggregateRoot.cs: -------------------------------------------------------------------------------- 1 | namespace CarRentalSystem.Domain.Common 2 | { 3 | public interface IAggregateRoot 4 | { 5 | } 6 | } 7 | -------------------------------------------------------------------------------- /Car Dealers/2. Common Functionality/Domain/Common/IDomainEvent.cs: -------------------------------------------------------------------------------- 1 | namespace CarRentalSystem.Domain.Common 2 | { 3 | public interface IDomainEvent 4 | { 5 | } 6 | } 7 | -------------------------------------------------------------------------------- /Car Dealers/2. Common Functionality/Domain/Common/IFactory.cs: -------------------------------------------------------------------------------- 1 | namespace CarRentalSystem.Domain.Common 2 | { 3 | public interface IFactory 4 | where TEntity : IAggregateRoot 5 | { 6 | TEntity Build(); 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /Car Dealers/2. Common Functionality/Domain/Common/IInitialData.cs: -------------------------------------------------------------------------------- 1 | namespace CarRentalSystem.Domain.Common 2 | { 3 | using System; 4 | using System.Collections.Generic; 5 | 6 | public interface IInitialData 7 | { 8 | Type EntityType { get; } 9 | 10 | IEnumerable GetData(); 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /Car Dealers/2. Common Functionality/Domain/Common/Models/IEntity.cs: -------------------------------------------------------------------------------- 1 | namespace CarRentalSystem.Domain.Common.Models 2 | { 3 | using System.Collections.Generic; 4 | 5 | public interface IEntity 6 | { 7 | IReadOnlyCollection Events { get; } 8 | 9 | void ClearEvents(); 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /Car Dealers/2. Common Functionality/Domain/Dealerships/Events/Dealers/CarAdAddedEvent.cs: -------------------------------------------------------------------------------- 1 | namespace CarRentalSystem.Domain.Dealerships.Events.Dealers 2 | { 3 | using Common; 4 | 5 | public class CarAdAddedEvent : IDomainEvent 6 | { 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /Car Dealers/2. Common Functionality/Domain/Dealerships/Exceptions/InvalidCarAdException.cs: -------------------------------------------------------------------------------- 1 | namespace CarRentalSystem.Domain.Dealerships.Exceptions 2 | { 3 | using Common; 4 | 5 | public class InvalidCarAdException : BaseDomainException 6 | { 7 | public InvalidCarAdException() 8 | { 9 | } 10 | 11 | public InvalidCarAdException(string error) => this.Error = error; 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /Car Dealers/2. Common Functionality/Domain/Dealerships/Exceptions/InvalidDealerException.cs: -------------------------------------------------------------------------------- 1 | namespace CarRentalSystem.Domain.Dealerships.Exceptions 2 | { 3 | using Common; 4 | 5 | public class InvalidDealerException : BaseDomainException 6 | { 7 | public InvalidDealerException() 8 | { 9 | } 10 | 11 | public InvalidDealerException(string error) => this.Error = error; 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /Car Dealers/2. Common Functionality/Domain/Dealerships/Exceptions/InvalidOptionsException.cs: -------------------------------------------------------------------------------- 1 | namespace CarRentalSystem.Domain.Dealerships.Exceptions 2 | { 3 | using Common; 4 | 5 | public class InvalidOptionsException : BaseDomainException 6 | { 7 | public InvalidOptionsException() 8 | { 9 | } 10 | 11 | public InvalidOptionsException(string error) => this.Error = error; 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /Car Dealers/2. Common Functionality/Domain/Dealerships/Exceptions/InvalidPhoneNumberException.cs: -------------------------------------------------------------------------------- 1 | namespace CarRentalSystem.Domain.Dealerships.Exceptions 2 | { 3 | using Common; 4 | 5 | public class InvalidPhoneNumberException : BaseDomainException 6 | { 7 | public InvalidPhoneNumberException() 8 | { 9 | } 10 | 11 | public InvalidPhoneNumberException(string error) => this.Error = error; 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /Car Dealers/2. Common Functionality/Domain/Dealerships/Factories/Dealers/IDealerFactory.cs: -------------------------------------------------------------------------------- 1 | namespace CarRentalSystem.Domain.Dealerships.Factories.Dealers 2 | { 3 | using Common; 4 | using Models.Dealers; 5 | 6 | public interface IDealerFactory : IFactory 7 | { 8 | IDealerFactory WithName(string name); 9 | 10 | IDealerFactory WithPhoneNumber(string phoneNumber); 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /Car Dealers/2. Common Functionality/Domain/Dealerships/Models/CarAds/CarAd.Specs.cs: -------------------------------------------------------------------------------- 1 | namespace CarRentalSystem.Domain.Dealerships.Models.CarAds 2 | { 3 | using FakeItEasy; 4 | using FluentAssertions; 5 | using Xunit; 6 | 7 | public class CarAdSpecs 8 | { 9 | [Fact] 10 | public void ChangeAvailabilityShouldMutateIsAvailable() 11 | { 12 | // Arrange 13 | var carAd = A.Dummy(); 14 | 15 | // Act 16 | carAd.ChangeAvailability(); 17 | 18 | // Assert 19 | carAd.IsAvailable.Should().BeFalse(); 20 | } 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /Car Dealers/2. Common Functionality/Domain/Dealerships/Models/CarAds/Category.Fakes.cs: -------------------------------------------------------------------------------- 1 | namespace CarRentalSystem.Domain.Dealerships.Models.CarAds 2 | { 3 | using System; 4 | using FakeItEasy; 5 | 6 | public class CategoryFakes 7 | { 8 | public const string ValidCategoryName = "Economy"; 9 | 10 | public class CategoryDummyFactory : IDummyFactory 11 | { 12 | public bool CanCreate(Type type) => type == typeof(Category); 13 | 14 | public object? Create(Type type) 15 | => new Category(ValidCategoryName, "Valid description text"); 16 | 17 | public Priority Priority => Priority.Default; 18 | } 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /Car Dealers/2. Common Functionality/Domain/Dealerships/Models/CarAds/Manufacturer.Fakes.cs: -------------------------------------------------------------------------------- 1 | namespace CarRentalSystem.Domain.Dealerships.Models.CarAds 2 | { 3 | using System; 4 | using FakeItEasy; 5 | 6 | public class ManufacturerFakes 7 | { 8 | public class ManufacturerDummyFactory : IDummyFactory 9 | { 10 | public bool CanCreate(Type type) => type == typeof(Manufacturer); 11 | 12 | public object? Create(Type type) 13 | => new Manufacturer("Valid manufacturer"); 14 | 15 | public Priority Priority => Priority.Default; 16 | } 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /Car Dealers/2. Common Functionality/Domain/Dealerships/Models/CarAds/Options.Fakes.cs: -------------------------------------------------------------------------------- 1 | namespace CarRentalSystem.Domain.Dealerships.Models.CarAds 2 | { 3 | using System; 4 | using FakeItEasy; 5 | 6 | public class OptionsFakes 7 | { 8 | public class OptionsDummyFactory : IDummyFactory 9 | { 10 | public bool CanCreate(Type type) => type == typeof(Options); 11 | 12 | public object? Create(Type type) 13 | => new Options(true, 4, TransmissionType.Automatic); 14 | 15 | public Priority Priority => Priority.Default; 16 | } 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /Car Dealers/2. Common Functionality/Domain/Dealerships/Models/CarAds/TransmissionType.cs: -------------------------------------------------------------------------------- 1 | namespace CarRentalSystem.Domain.Dealerships.Models.CarAds 2 | { 3 | using Common; 4 | using Common.Models; 5 | 6 | public class TransmissionType : Enumeration 7 | { 8 | public static readonly TransmissionType Manual = new TransmissionType(1, nameof(Manual)); 9 | public static readonly TransmissionType Automatic = new TransmissionType(2, nameof(Automatic)); 10 | 11 | private TransmissionType(int value) 12 | : this(value, FromValue(value).Name) 13 | { 14 | } 15 | 16 | private TransmissionType(int value, string name) 17 | : base(value, name) 18 | { 19 | } 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /Car Dealers/2. Common Functionality/Domain/Dealerships/Models/Dealers/Dealer.Specs.cs: -------------------------------------------------------------------------------- 1 | namespace CarRentalSystem.Domain.Dealerships.Models.Dealers 2 | { 3 | using CarAds; 4 | using FakeItEasy; 5 | using FluentAssertions; 6 | using Xunit; 7 | 8 | public class DealerSpecs 9 | { 10 | [Fact] 11 | public void AddCarAdShouldSaveCarAd() 12 | { 13 | // Arrange 14 | var dealer = new Dealer("Valid dealer", "+12345678"); 15 | var carAd = A.Dummy(); 16 | 17 | // Act 18 | dealer.AddCarAd(carAd); 19 | 20 | // Assert 21 | dealer.CarAds.Should().Contain(carAd); 22 | } 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /Car Dealers/2. Common Functionality/Domain/Dealerships/Specifications/CarAds/CarAdByCategorySpecification.cs: -------------------------------------------------------------------------------- 1 | namespace CarRentalSystem.Domain.Dealerships.Specifications.CarAds 2 | { 3 | using System; 4 | using System.Linq.Expressions; 5 | using Common; 6 | using Models.CarAds; 7 | 8 | public class CarAdByCategorySpecification : Specification 9 | { 10 | private readonly int? category; 11 | 12 | public CarAdByCategorySpecification(int? category) 13 | => this.category = category; 14 | 15 | protected override bool Include => this.category != null; 16 | 17 | public override Expression> ToExpression() 18 | => carAd => carAd.Category.Id == this.category; 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /Car Dealers/2. Common Functionality/Domain/Dealerships/Specifications/Dealers/DealerByIdSpecification.cs: -------------------------------------------------------------------------------- 1 | namespace CarRentalSystem.Domain.Dealerships.Specifications.Dealers 2 | { 3 | using System; 4 | using System.Linq.Expressions; 5 | using Common; 6 | using Models.Dealers; 7 | 8 | public class DealerByIdSpecification : Specification 9 | { 10 | private readonly int? id; 11 | 12 | public DealerByIdSpecification(int? id) 13 | => this.id = id; 14 | 15 | protected override bool Include => this.id != null; 16 | 17 | public override Expression> ToExpression() 18 | => dealer => dealer.Id == this.id; 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /Car Dealers/2. Common Functionality/Domain/Dealerships/Specifications/Dealers/DealerByNameSpecification.cs: -------------------------------------------------------------------------------- 1 | namespace CarRentalSystem.Domain.Dealerships.Specifications.Dealers 2 | { 3 | using System; 4 | using System.Linq.Expressions; 5 | using Common; 6 | using Models.Dealers; 7 | 8 | public class DealerByNameSpecification : Specification 9 | { 10 | private readonly string? name; 11 | 12 | public DealerByNameSpecification(string? name) 13 | => this.name = name; 14 | 15 | protected override bool Include => this.name != null; 16 | 17 | public override Expression> ToExpression() 18 | => dealer => dealer.Name.ToLower().Contains(this.name!.ToLower()); 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /Car Dealers/2. Common Functionality/Domain/Statistics/Models/CarAdView.cs: -------------------------------------------------------------------------------- 1 | namespace CarRentalSystem.Domain.Statistics.Models 2 | { 3 | using Common.Models; 4 | 5 | public class CarAdView : Entity 6 | { 7 | internal CarAdView(int carAdId, string? userId) 8 | { 9 | this.CarAdId = carAdId; 10 | this.UserId = userId; 11 | } 12 | 13 | public int CarAdId { get; } 14 | 15 | public string? UserId { get; } 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /Car Dealers/2. Common Functionality/Domain/Statistics/Models/Statistics.Data.cs: -------------------------------------------------------------------------------- 1 | namespace CarRentalSystem.Domain.Statistics.Models 2 | { 3 | using System; 4 | using System.Collections.Generic; 5 | using Common; 6 | 7 | public class StatisticsData : IInitialData 8 | { 9 | public Type EntityType => typeof(Statistics); 10 | 11 | public IEnumerable GetData() 12 | => new List 13 | { 14 | new Statistics() 15 | }; 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /Car Dealers/2. Common Functionality/Infrastructure/Common/Events/IEventDispatcher.cs: -------------------------------------------------------------------------------- 1 | namespace CarRentalSystem.Infrastructure.Common.Events 2 | { 3 | using System.Threading.Tasks; 4 | using Domain.Common; 5 | 6 | public interface IEventDispatcher 7 | { 8 | Task Dispatch(IDomainEvent domainEvent); 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /Car Dealers/2. Common Functionality/Infrastructure/Common/IInitializer.cs: -------------------------------------------------------------------------------- 1 | namespace CarRentalSystem.Infrastructure.Common 2 | { 3 | public interface IInitializer 4 | { 5 | void Initialize(); 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /Car Dealers/2. Common Functionality/Infrastructure/Common/Persistence/IDbContext.cs: -------------------------------------------------------------------------------- 1 | namespace CarRentalSystem.Infrastructure.Common.Persistence 2 | { 3 | using System.Threading; 4 | using System.Threading.Tasks; 5 | using Microsoft.EntityFrameworkCore; 6 | using Microsoft.EntityFrameworkCore.ChangeTracking; 7 | 8 | public interface IDbContext 9 | { 10 | DbSet Set() where TEntity : class; 11 | 12 | EntityEntry Update(TEntity entity) where TEntity : class; 13 | 14 | Task SaveChangesAsync(CancellationToken cancellationToken = default); 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /Car Dealers/2. Common Functionality/Infrastructure/Common/QueryableExtensions.cs: -------------------------------------------------------------------------------- 1 | namespace CarRentalSystem.Infrastructure.Common 2 | { 3 | using System.Linq; 4 | using Application.Common; 5 | 6 | public static class QueryableExtensions 7 | { 8 | public static IQueryable Sort( 9 | this IQueryable queryable, 10 | SortOrder sortOrder) 11 | => sortOrder.Order == SortOrder.Descending 12 | ? queryable.OrderByDescending(sortOrder.ToExpression()) 13 | : queryable.OrderBy(sortOrder.ToExpression()); 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /Car Dealers/2. Common Functionality/Infrastructure/Dealership/IDealershipDbContext.cs: -------------------------------------------------------------------------------- 1 | namespace CarRentalSystem.Infrastructure.Dealership 2 | { 3 | using Common.Persistence; 4 | using Domain.Dealerships.Models.CarAds; 5 | using Domain.Dealerships.Models.Dealers; 6 | using Identity; 7 | using Microsoft.EntityFrameworkCore; 8 | 9 | public interface IDealershipDbContext : IDbContext 10 | { 11 | DbSet CarAds { get; } 12 | 13 | DbSet Categories { get; } 14 | 15 | DbSet Manufacturers { get; } 16 | 17 | DbSet Dealers { get; } 18 | 19 | DbSet Users { get; } // TODO: Temporary workaround 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /Car Dealers/2. Common Functionality/Infrastructure/Identity/Configuration/UserConfiguration.cs: -------------------------------------------------------------------------------- 1 | namespace CarRentalSystem.Infrastructure.Identity.Configuration 2 | { 3 | using Identity; 4 | using Microsoft.EntityFrameworkCore; 5 | using Microsoft.EntityFrameworkCore.Metadata.Builders; 6 | 7 | public class UserConfiguration : IEntityTypeConfiguration 8 | { 9 | public void Configure(EntityTypeBuilder builder) 10 | { 11 | builder 12 | .HasOne(u => u.Dealer) 13 | .WithOne() 14 | .HasForeignKey("DealerId") 15 | .OnDelete(DeleteBehavior.Restrict); 16 | } 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /Car Dealers/2. Common Functionality/Infrastructure/Identity/IJwtTokenGenerator.cs: -------------------------------------------------------------------------------- 1 | namespace CarRentalSystem.Infrastructure.Identity 2 | { 3 | public interface IJwtTokenGenerator 4 | { 5 | string GenerateToken(User user); 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /Car Dealers/2. Common Functionality/Infrastructure/Identity/JwtTokenGeneratorService.Fakes.cs: -------------------------------------------------------------------------------- 1 | namespace CarRentalSystem.Infrastructure.Identity 2 | { 3 | using FakeItEasy; 4 | 5 | public class JwtTokenGeneratorFakes 6 | { 7 | public const string ValidToken = "ValidToken"; 8 | 9 | public static IJwtTokenGenerator FakeJwtTokenGenerator 10 | { 11 | get 12 | { 13 | var jwtTokenGenerator = A.Fake(); 14 | 15 | A 16 | .CallTo(() => jwtTokenGenerator.GenerateToken(A.Ignored)) 17 | .Returns(ValidToken); 18 | 19 | return jwtTokenGenerator; 20 | } 21 | } 22 | } 23 | } -------------------------------------------------------------------------------- /Car Dealers/2. Common Functionality/Infrastructure/Statistics/IStatisticsDbContext.cs: -------------------------------------------------------------------------------- 1 | namespace CarRentalSystem.Infrastructure.Statistics 2 | { 3 | using Common.Persistence; 4 | using Domain.Statistics.Models; 5 | using Microsoft.EntityFrameworkCore; 6 | 7 | public interface IStatisticsDbContext : IDbContext 8 | { 9 | DbSet Statistics { get; } 10 | 11 | DbSet CarAdViews { get; } 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /Car Dealers/2. Common Functionality/Startup/ApplicationInitialization.cs: -------------------------------------------------------------------------------- 1 | namespace CarRentalSystem.Startup 2 | { 3 | using Infrastructure.Common; 4 | using Microsoft.AspNetCore.Builder; 5 | using Microsoft.Extensions.DependencyInjection; 6 | 7 | public static class ApplicationInitialization 8 | { 9 | public static IApplicationBuilder Initialize(this IApplicationBuilder app) 10 | { 11 | using var serviceScope = app.ApplicationServices.CreateScope(); 12 | 13 | var initializers = serviceScope.ServiceProvider.GetServices(); 14 | 15 | foreach (var initializer in initializers) 16 | { 17 | initializer.Initialize(); 18 | } 19 | 20 | return app; 21 | } 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /Car Dealers/2. Common Functionality/Startup/Program.cs: -------------------------------------------------------------------------------- 1 | namespace CarRentalSystem.Startup 2 | { 3 | using Microsoft.AspNetCore.Hosting; 4 | using Microsoft.Extensions.Hosting; 5 | 6 | public class Program 7 | { 8 | public static void Main(string[] args) => CreateHostBuilder(args).Build().Run(); 9 | 10 | public static IHostBuilder CreateHostBuilder(string[] args) 11 | => Host 12 | .CreateDefaultBuilder(args) 13 | .ConfigureWebHostDefaults(webBuilder => 14 | { 15 | webBuilder.UseStartup(); 16 | }); 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /Car Dealers/2. Common Functionality/Startup/Specs/DealersController.Specs.cs: -------------------------------------------------------------------------------- 1 | namespace CarRentalSystem.Startup.Specs 2 | { 3 | using Application.Dealerships.Dealers.Queries.Details; 4 | using MyTested.AspNetCore.Mvc; 5 | using Web; 6 | using Web.Features; 7 | using Xunit; 8 | 9 | public class DealersControllerSpecs 10 | { 11 | [Fact] 12 | public void DetailsShouldHaveCorrectAttributes() 13 | => MyController 14 | .Calling(c => c.Details(With.Default())) 15 | 16 | .ShouldHave() 17 | .ActionAttributes(attr => attr 18 | .RestrictingForHttpMethod(HttpMethod.Get) 19 | .SpecifyingRoute(ApiController.Id)); 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /Car Dealers/2. Common Functionality/Startup/appsettings.Development.json: -------------------------------------------------------------------------------- 1 | { 2 | "Logging": { 3 | "LogLevel": { 4 | "Default": "Information", 5 | "Microsoft": "Warning", 6 | "Microsoft.Hosting.Lifetime": "Information" 7 | } 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /Car Dealers/2. Common Functionality/Startup/appsettings.json: -------------------------------------------------------------------------------- 1 | { 2 | "ApplicationSettings": { 3 | "Secret": "S0M3 M4G1C UN1C0RNS G3N3R4T3D TH1S S3CR3T" 4 | }, 5 | "ConnectionStrings": { 6 | "DefaultConnection": "Server=.;Database=CarRentalSystem;Trusted_Connection=True;MultipleActiveResultSets=true" 7 | }, 8 | "Logging": { 9 | "LogLevel": { 10 | "Default": "Information", 11 | "Microsoft": "Warning", 12 | "Microsoft.Hosting.Lifetime": "Information" 13 | } 14 | }, 15 | "AllowedHosts": "*" 16 | } 17 | -------------------------------------------------------------------------------- /Car Dealers/2. Common Functionality/Startup/testsettings.json: -------------------------------------------------------------------------------- 1 | { 2 | "General": { 3 | "WebAssemblyName": "CarRentalSystem.Startup" 4 | }, 5 | "ApplicationSettings": { 6 | "Secret": "My Fake Secret" 7 | }, 8 | "ConnectionStrings": { 9 | "DefaultConnection": "My Fake Connection String" 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /Car Dealers/2. Common Functionality/Web/Services/CurrentUserService.cs: -------------------------------------------------------------------------------- 1 | namespace CarRentalSystem.Web.Services 2 | { 3 | using System; 4 | using System.Security.Claims; 5 | using Application.Common.Contracts; 6 | using Microsoft.AspNetCore.Http; 7 | 8 | public class CurrentUserService : ICurrentUser 9 | { 10 | public CurrentUserService(IHttpContextAccessor httpContextAccessor) 11 | { 12 | var user = httpContextAccessor.HttpContext?.User; 13 | 14 | if (user == null) 15 | { 16 | throw new InvalidOperationException("This request does not have an authenticated user."); 17 | } 18 | 19 | this.UserId = user.FindFirstValue(ClaimTypes.NameIdentifier); 20 | } 21 | 22 | public string UserId { get; } 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /Car Dealers/3. Separation of Concerns/Application/Common/ApplicationSettings.cs: -------------------------------------------------------------------------------- 1 | namespace CarRentalSystem.Application.Common 2 | { 3 | public class ApplicationSettings 4 | { 5 | public ApplicationSettings() => this.Secret = default!; 6 | 7 | public string Secret { get; private set; } 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /Car Dealers/3. Separation of Concerns/Application/Common/Contracts/ICurrentUser.cs: -------------------------------------------------------------------------------- 1 | namespace CarRentalSystem.Application.Common.Contracts 2 | { 3 | public interface ICurrentUser 4 | { 5 | string UserId { get; } 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /Car Dealers/3. Separation of Concerns/Application/Common/Contracts/IQueryRepository.cs: -------------------------------------------------------------------------------- 1 | namespace CarRentalSystem.Application.Common.Contracts 2 | { 3 | using Domain.Common; 4 | 5 | public interface IQueryRepository 6 | where TEntity : IAggregateRoot 7 | { 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /Car Dealers/3. Separation of Concerns/Application/Common/EntityCommand.cs: -------------------------------------------------------------------------------- 1 | namespace CarRentalSystem.Application.Common 2 | { 3 | public class EntityCommand 4 | { 5 | public TId Id { get; set; } = default!; 6 | } 7 | 8 | public static class EntityCommandExtensions 9 | { 10 | public static TCommand SetId(this TCommand command, TId id) 11 | where TCommand : EntityCommand 12 | { 13 | command.Id = id; 14 | return command; 15 | } 16 | } 17 | } -------------------------------------------------------------------------------- /Car Dealers/3. Separation of Concerns/Application/Common/Exceptions/NotFoundException.cs: -------------------------------------------------------------------------------- 1 | namespace CarRentalSystem.Application.Common.Exceptions 2 | { 3 | using System; 4 | 5 | public class NotFoundException : Exception 6 | { 7 | public NotFoundException(string name, object key) 8 | : base($"Entity '{name}' ({key}) was not found.") 9 | { 10 | } 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /Car Dealers/3. Separation of Concerns/Application/Common/IEventHandler.cs: -------------------------------------------------------------------------------- 1 | namespace CarRentalSystem.Application.Common 2 | { 3 | using System.Threading.Tasks; 4 | using Domain.Common; 5 | 6 | public interface IEventHandler 7 | where TEvent : IDomainEvent 8 | { 9 | Task Handle(TEvent domainEvent); 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /Car Dealers/3. Separation of Concerns/Application/Common/Mapping/IMapFrom.cs: -------------------------------------------------------------------------------- 1 | namespace CarRentalSystem.Application.Common.Mapping 2 | { 3 | using AutoMapper; 4 | 5 | public interface IMapFrom 6 | { 7 | void Mapping(Profile mapper) => mapper.CreateMap(typeof(T), this.GetType()); 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /Car Dealers/3. Separation of Concerns/Application/Common/SortOrder.cs: -------------------------------------------------------------------------------- 1 | namespace CarRentalSystem.Application.Common 2 | { 3 | using System; 4 | using System.Linq.Expressions; 5 | 6 | public abstract class SortOrder 7 | { 8 | public const string Ascending = "asc"; 9 | public const string Descending = "desc"; 10 | 11 | protected SortOrder(string? sortBy, string? order) 12 | { 13 | this.SortBy = sortBy; 14 | this.Order = order; 15 | } 16 | 17 | public string? SortBy { get; } 18 | 19 | public string? Order { get; } 20 | 21 | public abstract Expression> ToExpression(); 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /Car Dealers/3. Separation of Concerns/Application/Dealerships/CarAds/Commands/Create/CreateCarAdCommandValidator.cs: -------------------------------------------------------------------------------- 1 | namespace CarRentalSystem.Application.Dealerships.CarAds.Commands.Create 2 | { 3 | using Common; 4 | using FluentValidation; 5 | 6 | public class CreateCarAdCommandValidator : AbstractValidator 7 | { 8 | public CreateCarAdCommandValidator(ICarAdQueryRepository carAdRepository) 9 | => this.Include(new CarAdCommandValidator(carAdRepository)); 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /Car Dealers/3. Separation of Concerns/Application/Dealerships/CarAds/Commands/Create/CreateCarAdOutputModel.cs: -------------------------------------------------------------------------------- 1 | namespace CarRentalSystem.Application.Dealerships.CarAds.Commands.Create 2 | { 3 | public class CreateCarAdOutputModel 4 | { 5 | public CreateCarAdOutputModel(int carAdId) 6 | => this.CarAdId = carAdId; 7 | 8 | public int CarAdId { get; } 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /Car Dealers/3. Separation of Concerns/Application/Dealerships/CarAds/Commands/Edit/EditCarAdCommandValidator.cs: -------------------------------------------------------------------------------- 1 | namespace CarRentalSystem.Application.Dealerships.CarAds.Commands.Edit 2 | { 3 | using Common; 4 | using FluentValidation; 5 | 6 | public class EditCarAdCommandValidator : AbstractValidator 7 | { 8 | public EditCarAdCommandValidator(ICarAdQueryRepository carAdRepository) 9 | => this.Include(new CarAdCommandValidator(carAdRepository)); 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /Car Dealers/3. Separation of Concerns/Application/Dealerships/CarAds/Queries/Categories/GetCarAdCategoryOutputModel.cs: -------------------------------------------------------------------------------- 1 | namespace CarRentalSystem.Application.Dealerships.CarAds.Queries.Categories 2 | { 3 | using Application.Common.Mapping; 4 | using Domain.Dealerships.Models.CarAds; 5 | 6 | public class GetCarAdCategoryOutputModel : IMapFrom 7 | { 8 | public int Id { get; private set; } 9 | 10 | public string Name { get; private set; } = default!; 11 | 12 | public string Description { get; private set; } = default!; 13 | 14 | public int TotalCarAds { get; set; } 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /Car Dealers/3. Separation of Concerns/Application/Dealerships/CarAds/Queries/Common/CarAdsOutputModel.cs: -------------------------------------------------------------------------------- 1 | namespace CarRentalSystem.Application.Dealerships.CarAds.Queries.Common 2 | { 3 | using System.Collections.Generic; 4 | 5 | public abstract class CarAdsOutputModel 6 | { 7 | internal CarAdsOutputModel( 8 | IEnumerable carAds, 9 | int page, 10 | int totalPages) 11 | { 12 | this.CarAds = carAds; 13 | this.Page = page; 14 | this.TotalPages = totalPages; 15 | } 16 | 17 | public IEnumerable CarAds { get; } 18 | 19 | public int Page { get; } 20 | 21 | public int TotalPages { get; } 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /Car Dealers/3. Separation of Concerns/Application/Dealerships/CarAds/Queries/Mine/MineCarAdOutputModel.cs: -------------------------------------------------------------------------------- 1 | namespace CarRentalSystem.Application.Dealerships.CarAds.Queries.Mine 2 | { 3 | using AutoMapper; 4 | using Common; 5 | using Domain.Dealerships.Models.CarAds; 6 | 7 | public class MineCarAdOutputModel : CarAdOutputModel 8 | { 9 | public bool IsAvailable { get; private set; } 10 | 11 | public override void Mapping(Profile mapper) 12 | => mapper 13 | .CreateMap() 14 | .IncludeBase(); 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /Car Dealers/3. Separation of Concerns/Application/Dealerships/CarAds/Queries/Mine/MineCarAdsOutputModel.cs: -------------------------------------------------------------------------------- 1 | namespace CarRentalSystem.Application.Dealerships.CarAds.Queries.Mine 2 | { 3 | using System.Collections.Generic; 4 | using Common; 5 | 6 | public class MineCarAdsOutputModel : CarAdsOutputModel 7 | { 8 | public MineCarAdsOutputModel( 9 | IEnumerable carAds, 10 | int page, 11 | int totalPages) 12 | : base(carAds, page, totalPages) 13 | { 14 | } 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /Car Dealers/3. Separation of Concerns/Application/Dealerships/CarAds/Queries/Search/SearchCarAdsOutputModel.cs: -------------------------------------------------------------------------------- 1 | namespace CarRentalSystem.Application.Dealerships.CarAds.Queries.Search 2 | { 3 | using System.Collections.Generic; 4 | using Common; 5 | 6 | public class SearchCarAdsOutputModel : CarAdsOutputModel 7 | { 8 | public SearchCarAdsOutputModel( 9 | IEnumerable carAds, 10 | int page, 11 | int totalPages) 12 | : base(carAds, page, totalPages) 13 | { 14 | } 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /Car Dealers/3. Separation of Concerns/Application/Dealerships/Dealers/IDealerQueryRepository.cs: -------------------------------------------------------------------------------- 1 | namespace CarRentalSystem.Application.Dealerships.Dealers 2 | { 3 | using System.Threading; 4 | using System.Threading.Tasks; 5 | using Common.Contracts; 6 | using Domain.Dealerships.Models.Dealers; 7 | using Queries.Common; 8 | using Queries.Details; 9 | 10 | public interface IDealerQueryRepository : IQueryRepository 11 | { 12 | Task GetDetails(int id, CancellationToken cancellationToken = default); 13 | 14 | Task GetDetailsByCarId(int carAdId, CancellationToken cancellationToken = default); 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /Car Dealers/3. Separation of Concerns/Application/Dealerships/Dealers/Queries/Details/DealerDetailsOutputModel.cs: -------------------------------------------------------------------------------- 1 | namespace CarRentalSystem.Application.Dealerships.Dealers.Queries.Details 2 | { 3 | using AutoMapper; 4 | using Common; 5 | using Domain.Dealerships.Models.Dealers; 6 | 7 | public class DealerDetailsOutputModel : DealerOutputModel 8 | { 9 | public int TotalCarAds { get; private set; } 10 | 11 | public override void Mapping(Profile mapper) 12 | => mapper 13 | .CreateMap() 14 | .IncludeBase() 15 | .ForMember(d => d.TotalCarAds, cfg => cfg 16 | .MapFrom(d => d.CarAds.Count)); 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /Car Dealers/3. Separation of Concerns/Application/Identity/Commands/ChangePassword/ChangePasswordInputModel.cs: -------------------------------------------------------------------------------- 1 | namespace CarRentalSystem.Application.Identity.Commands.ChangePassword 2 | { 3 | public class ChangePasswordInputModel 4 | { 5 | public ChangePasswordInputModel( 6 | string userId, 7 | string currentPassword, 8 | string newPassword) 9 | { 10 | this.UserId = userId; 11 | this.CurrentPassword = currentPassword; 12 | this.NewPassword = newPassword; 13 | } 14 | 15 | public string UserId { get; } 16 | 17 | public string CurrentPassword { get; } 18 | 19 | public string NewPassword { get; } 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /Car Dealers/3. Separation of Concerns/Application/Identity/Commands/CreateUser/CreateUserCommand.Fakes.cs: -------------------------------------------------------------------------------- 1 | namespace CarRentalSystem.Application.Identity.Commands.CreateUser 2 | { 3 | using Bogus; 4 | 5 | public class CreateUserCommandFakes 6 | { 7 | public static class Data 8 | { 9 | public static CreateUserCommand GetCommand() 10 | => new Faker() 11 | .RuleFor(u => u.Email, f => f.Internet.Email()) 12 | .RuleFor(u => u.Password, f => f.Lorem.Letter(10)) 13 | .RuleFor(u => u.Name, f => f.Name.FullName()) 14 | .RuleFor(u => u.PhoneNumber, f => f.Phone.PhoneNumber("+#######")) 15 | .Generate(); 16 | } 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /Car Dealers/3. Separation of Concerns/Application/Identity/Commands/LoginUser/LoginOutputModel.cs: -------------------------------------------------------------------------------- 1 | namespace CarRentalSystem.Application.Identity.Commands.LoginUser 2 | { 3 | public class LoginOutputModel 4 | { 5 | public LoginOutputModel(string token, int dealerId) 6 | { 7 | this.Token = token; 8 | this.DealerId = dealerId; 9 | } 10 | 11 | public int DealerId { get; } 12 | 13 | public string Token { get; } 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /Car Dealers/3. Separation of Concerns/Application/Identity/Commands/LoginUser/LoginSuccessModel.cs: -------------------------------------------------------------------------------- 1 | namespace CarRentalSystem.Application.Identity.Commands.LoginUser 2 | { 3 | public class LoginSuccessModel 4 | { 5 | public LoginSuccessModel(string userId, string token) 6 | { 7 | this.UserId = userId; 8 | this.Token = token; 9 | } 10 | 11 | public string UserId { get; } 12 | 13 | public string Token { get; } 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /Car Dealers/3. Separation of Concerns/Application/Identity/Commands/UserInputModel.cs: -------------------------------------------------------------------------------- 1 | namespace CarRentalSystem.Application.Identity.Commands 2 | { 3 | public abstract class UserInputModel 4 | { 5 | public string Email { get; set; } = default!; 6 | 7 | public string Password { get; set; } = default!; 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /Car Dealers/3. Separation of Concerns/Application/Identity/IIdentity.cs: -------------------------------------------------------------------------------- 1 | namespace CarRentalSystem.Application.Identity 2 | { 3 | using System.Threading.Tasks; 4 | using Commands; 5 | using Commands.ChangePassword; 6 | using Commands.LoginUser; 7 | using Common; 8 | 9 | public interface IIdentity 10 | { 11 | Task> Register(UserInputModel userInput); 12 | 13 | Task> Login(UserInputModel userInput); 14 | 15 | Task ChangePassword(ChangePasswordInputModel changePasswordInput); 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /Car Dealers/3. Separation of Concerns/Application/Identity/IUser.cs: -------------------------------------------------------------------------------- 1 | namespace CarRentalSystem.Application.Identity 2 | { 3 | using Domain.Dealerships.Models.Dealers; 4 | 5 | public interface IUser 6 | { 7 | void BecomeDealer(Dealer dealer); 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /Car Dealers/3. Separation of Concerns/Application/Statistics/Handlers/CarAdAddedEventHandler.cs: -------------------------------------------------------------------------------- 1 | namespace CarRentalSystem.Application.Statistics.Handlers 2 | { 3 | using System.Threading.Tasks; 4 | using Common; 5 | using Domain.Dealerships.Events.Dealers; 6 | 7 | public class CarAdAddedEventHandler : IEventHandler 8 | { 9 | private readonly IStatisticsRepository statistics; 10 | 11 | public CarAdAddedEventHandler(IStatisticsRepository statistics) 12 | => this.statistics = statistics; 13 | 14 | public Task Handle(CarAdAddedEvent domainEvent) 15 | => this.statistics.IncrementCarAds(); 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /Car Dealers/3. Separation of Concerns/Application/Statistics/IStatisticsRepository.cs: -------------------------------------------------------------------------------- 1 | namespace CarRentalSystem.Application.Statistics 2 | { 3 | using System.Threading; 4 | using System.Threading.Tasks; 5 | using Common.Contracts; 6 | using Domain.Common; 7 | using Domain.Statistics.Models; 8 | using Queries.Current; 9 | 10 | public interface IStatisticsRepository : IQueryRepository 11 | { 12 | Task GetCurrent(CancellationToken cancellationToken = default); 13 | 14 | Task GetCarAdViews(int carAdId, CancellationToken cancellationToken = default); 15 | 16 | Task IncrementCarAds(CancellationToken cancellationToken = default); 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /Car Dealers/3. Separation of Concerns/Application/Statistics/Queries/Current/GetCurrentStatisticsOutputModel.cs: -------------------------------------------------------------------------------- 1 | namespace CarRentalSystem.Application.Statistics.Queries.Current 2 | { 3 | using AutoMapper; 4 | using Common.Mapping; 5 | using Domain.Statistics.Models; 6 | 7 | public class GetCurrentStatisticsOutputModel : IMapFrom 8 | { 9 | public int TotalCarAds { get; private set; } 10 | 11 | public int TotalCarAdViews { get; private set; } 12 | 13 | public void Mapping(Profile mapper) 14 | => mapper 15 | .CreateMap() 16 | .ForMember(cs => cs.TotalCarAds, cfg => cfg 17 | .MapFrom(s => s.CarAdViews.Count)); 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /Car Dealers/3. Separation of Concerns/Domain/Common/BaseDomainException.cs: -------------------------------------------------------------------------------- 1 | namespace CarRentalSystem.Domain.Common 2 | { 3 | using System; 4 | 5 | public abstract class BaseDomainException : Exception 6 | { 7 | private string? error; 8 | 9 | public string Error 10 | { 11 | get => this.error ?? base.Message; 12 | set => this.error = value; 13 | } 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /Car Dealers/3. Separation of Concerns/Domain/Common/IAggregateRoot.cs: -------------------------------------------------------------------------------- 1 | namespace CarRentalSystem.Domain.Common 2 | { 3 | public interface IAggregateRoot 4 | { 5 | } 6 | } 7 | -------------------------------------------------------------------------------- /Car Dealers/3. Separation of Concerns/Domain/Common/IDomainEvent.cs: -------------------------------------------------------------------------------- 1 | namespace CarRentalSystem.Domain.Common 2 | { 3 | public interface IDomainEvent 4 | { 5 | } 6 | } 7 | -------------------------------------------------------------------------------- /Car Dealers/3. Separation of Concerns/Domain/Common/IDomainRepository.cs: -------------------------------------------------------------------------------- 1 | namespace CarRentalSystem.Domain.Common 2 | { 3 | using System.Threading; 4 | using System.Threading.Tasks; 5 | 6 | public interface IDomainRepository 7 | where TEntity : IAggregateRoot 8 | { 9 | Task Save(TEntity entity, CancellationToken cancellationToken = default); 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /Car Dealers/3. Separation of Concerns/Domain/Common/IFactory.cs: -------------------------------------------------------------------------------- 1 | namespace CarRentalSystem.Domain.Common 2 | { 3 | public interface IFactory 4 | where TEntity : IAggregateRoot 5 | { 6 | TEntity Build(); 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /Car Dealers/3. Separation of Concerns/Domain/Common/IInitialData.cs: -------------------------------------------------------------------------------- 1 | namespace CarRentalSystem.Domain.Common 2 | { 3 | using System; 4 | using System.Collections.Generic; 5 | 6 | public interface IInitialData 7 | { 8 | Type EntityType { get; } 9 | 10 | IEnumerable GetData(); 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /Car Dealers/3. Separation of Concerns/Domain/Common/Models/IEntity.cs: -------------------------------------------------------------------------------- 1 | namespace CarRentalSystem.Domain.Common.Models 2 | { 3 | using System.Collections.Generic; 4 | 5 | public interface IEntity 6 | { 7 | IReadOnlyCollection Events { get; } 8 | 9 | void ClearEvents(); 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /Car Dealers/3. Separation of Concerns/Domain/Dealerships/Events/Dealers/CarAdAddedEvent.cs: -------------------------------------------------------------------------------- 1 | namespace CarRentalSystem.Domain.Dealerships.Events.Dealers 2 | { 3 | using Common; 4 | 5 | public class CarAdAddedEvent : IDomainEvent 6 | { 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /Car Dealers/3. Separation of Concerns/Domain/Dealerships/Exceptions/InvalidCarAdException.cs: -------------------------------------------------------------------------------- 1 | namespace CarRentalSystem.Domain.Dealerships.Exceptions 2 | { 3 | using Common; 4 | 5 | public class InvalidCarAdException : BaseDomainException 6 | { 7 | public InvalidCarAdException() 8 | { 9 | } 10 | 11 | public InvalidCarAdException(string error) => this.Error = error; 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /Car Dealers/3. Separation of Concerns/Domain/Dealerships/Exceptions/InvalidDealerException.cs: -------------------------------------------------------------------------------- 1 | namespace CarRentalSystem.Domain.Dealerships.Exceptions 2 | { 3 | using Common; 4 | 5 | public class InvalidDealerException : BaseDomainException 6 | { 7 | public InvalidDealerException() 8 | { 9 | } 10 | 11 | public InvalidDealerException(string error) => this.Error = error; 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /Car Dealers/3. Separation of Concerns/Domain/Dealerships/Exceptions/InvalidOptionsException.cs: -------------------------------------------------------------------------------- 1 | namespace CarRentalSystem.Domain.Dealerships.Exceptions 2 | { 3 | using Common; 4 | 5 | public class InvalidOptionsException : BaseDomainException 6 | { 7 | public InvalidOptionsException() 8 | { 9 | } 10 | 11 | public InvalidOptionsException(string error) => this.Error = error; 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /Car Dealers/3. Separation of Concerns/Domain/Dealerships/Exceptions/InvalidPhoneNumberException.cs: -------------------------------------------------------------------------------- 1 | namespace CarRentalSystem.Domain.Dealerships.Exceptions 2 | { 3 | using Common; 4 | 5 | public class InvalidPhoneNumberException : BaseDomainException 6 | { 7 | public InvalidPhoneNumberException() 8 | { 9 | } 10 | 11 | public InvalidPhoneNumberException(string error) => this.Error = error; 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /Car Dealers/3. Separation of Concerns/Domain/Dealerships/Factories/Dealers/IDealerFactory.cs: -------------------------------------------------------------------------------- 1 | namespace CarRentalSystem.Domain.Dealerships.Factories.Dealers 2 | { 3 | using Common; 4 | using Models.Dealers; 5 | 6 | public interface IDealerFactory : IFactory 7 | { 8 | IDealerFactory WithName(string name); 9 | 10 | IDealerFactory WithPhoneNumber(string phoneNumber); 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /Car Dealers/3. Separation of Concerns/Domain/Dealerships/Models/CarAds/CarAd.Specs.cs: -------------------------------------------------------------------------------- 1 | namespace CarRentalSystem.Domain.Dealerships.Models.CarAds 2 | { 3 | using FakeItEasy; 4 | using FluentAssertions; 5 | using Xunit; 6 | 7 | public class CarAdSpecs 8 | { 9 | [Fact] 10 | public void ChangeAvailabilityShouldMutateIsAvailable() 11 | { 12 | // Arrange 13 | var carAd = A.Dummy(); 14 | 15 | // Act 16 | carAd.ChangeAvailability(); 17 | 18 | // Assert 19 | carAd.IsAvailable.Should().BeFalse(); 20 | } 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /Car Dealers/3. Separation of Concerns/Domain/Dealerships/Models/CarAds/Category.Fakes.cs: -------------------------------------------------------------------------------- 1 | namespace CarRentalSystem.Domain.Dealerships.Models.CarAds 2 | { 3 | using System; 4 | using FakeItEasy; 5 | 6 | public class CategoryFakes 7 | { 8 | public const string ValidCategoryName = "Economy"; 9 | 10 | public class CategoryDummyFactory : IDummyFactory 11 | { 12 | public bool CanCreate(Type type) => type == typeof(Category); 13 | 14 | public object? Create(Type type) 15 | => new Category(ValidCategoryName, "Valid description text"); 16 | 17 | public Priority Priority => Priority.Default; 18 | } 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /Car Dealers/3. Separation of Concerns/Domain/Dealerships/Models/CarAds/Manufacturer.Fakes.cs: -------------------------------------------------------------------------------- 1 | namespace CarRentalSystem.Domain.Dealerships.Models.CarAds 2 | { 3 | using System; 4 | using FakeItEasy; 5 | 6 | public class ManufacturerFakes 7 | { 8 | public class ManufacturerDummyFactory : IDummyFactory 9 | { 10 | public bool CanCreate(Type type) => type == typeof(Manufacturer); 11 | 12 | public object? Create(Type type) 13 | => new Manufacturer("Valid manufacturer"); 14 | 15 | public Priority Priority => Priority.Default; 16 | } 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /Car Dealers/3. Separation of Concerns/Domain/Dealerships/Models/CarAds/Options.Fakes.cs: -------------------------------------------------------------------------------- 1 | namespace CarRentalSystem.Domain.Dealerships.Models.CarAds 2 | { 3 | using System; 4 | using FakeItEasy; 5 | 6 | public class OptionsFakes 7 | { 8 | public class OptionsDummyFactory : IDummyFactory 9 | { 10 | public bool CanCreate(Type type) => type == typeof(Options); 11 | 12 | public object? Create(Type type) 13 | => new Options(true, 4, TransmissionType.Automatic); 14 | 15 | public Priority Priority => Priority.Default; 16 | } 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /Car Dealers/3. Separation of Concerns/Domain/Dealerships/Models/CarAds/TransmissionType.cs: -------------------------------------------------------------------------------- 1 | namespace CarRentalSystem.Domain.Dealerships.Models.CarAds 2 | { 3 | using Common; 4 | using Common.Models; 5 | 6 | public class TransmissionType : Enumeration 7 | { 8 | public static readonly TransmissionType Manual = new TransmissionType(1, nameof(Manual)); 9 | public static readonly TransmissionType Automatic = new TransmissionType(2, nameof(Automatic)); 10 | 11 | private TransmissionType(int value) 12 | : this(value, FromValue(value).Name) 13 | { 14 | } 15 | 16 | private TransmissionType(int value, string name) 17 | : base(value, name) 18 | { 19 | } 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /Car Dealers/3. Separation of Concerns/Domain/Dealerships/Models/Dealers/Dealer.Specs.cs: -------------------------------------------------------------------------------- 1 | namespace CarRentalSystem.Domain.Dealerships.Models.Dealers 2 | { 3 | using CarAds; 4 | using FakeItEasy; 5 | using FluentAssertions; 6 | using Xunit; 7 | 8 | public class DealerSpecs 9 | { 10 | [Fact] 11 | public void AddCarAdShouldSaveCarAd() 12 | { 13 | // Arrange 14 | var dealer = new Dealer("Valid dealer", "+12345678"); 15 | var carAd = A.Dummy(); 16 | 17 | // Act 18 | dealer.AddCarAd(carAd); 19 | 20 | // Assert 21 | dealer.CarAds.Should().Contain(carAd); 22 | } 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /Car Dealers/3. Separation of Concerns/Domain/Dealerships/Repositories/ICarAdDomainRepository.cs: -------------------------------------------------------------------------------- 1 | namespace CarRentalSystem.Domain.Dealerships.Repositories 2 | { 3 | using System.Threading; 4 | using System.Threading.Tasks; 5 | using Models.CarAds; 6 | 7 | public interface ICarAdDomainRepository 8 | { 9 | Task Find(int id, CancellationToken cancellationToken = default); 10 | 11 | Task Delete(int id, CancellationToken cancellationToken = default); 12 | 13 | Task GetCategory( 14 | int categoryId, 15 | CancellationToken cancellationToken = default); 16 | 17 | Task GetManufacturer( 18 | string manufacturer, 19 | CancellationToken cancellationToken = default); 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /Car Dealers/3. Separation of Concerns/Domain/Dealerships/Repositories/IDealerDomainRepository.cs: -------------------------------------------------------------------------------- 1 | namespace CarRentalSystem.Domain.Dealerships.Repositories 2 | { 3 | using System.Threading; 4 | using System.Threading.Tasks; 5 | using Models.Dealers; 6 | 7 | public interface IDealerDomainRepository 8 | { 9 | Task FindByUser(string userId, CancellationToken cancellationToken = default); 10 | 11 | Task GetDealerId(string userId, CancellationToken cancellationToken = default); 12 | 13 | Task HasCarAd(int dealerId, int carAdId, CancellationToken cancellationToken = default); 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /Car Dealers/3. Separation of Concerns/Domain/Dealerships/Specifications/CarAds/CarAdByCategorySpecification.cs: -------------------------------------------------------------------------------- 1 | namespace CarRentalSystem.Domain.Dealerships.Specifications.CarAds 2 | { 3 | using System; 4 | using System.Linq.Expressions; 5 | using Common; 6 | using Models.CarAds; 7 | 8 | public class CarAdByCategorySpecification : Specification 9 | { 10 | private readonly int? category; 11 | 12 | public CarAdByCategorySpecification(int? category) 13 | => this.category = category; 14 | 15 | protected override bool Include => this.category != null; 16 | 17 | public override Expression> ToExpression() 18 | => carAd => carAd.Category.Id == this.category; 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /Car Dealers/3. Separation of Concerns/Domain/Dealerships/Specifications/Dealers/DealerByIdSpecification.cs: -------------------------------------------------------------------------------- 1 | namespace CarRentalSystem.Domain.Dealerships.Specifications.Dealers 2 | { 3 | using System; 4 | using System.Linq.Expressions; 5 | using Common; 6 | using Models.Dealers; 7 | 8 | public class DealerByIdSpecification : Specification 9 | { 10 | private readonly int? id; 11 | 12 | public DealerByIdSpecification(int? id) 13 | => this.id = id; 14 | 15 | protected override bool Include => this.id != null; 16 | 17 | public override Expression> ToExpression() 18 | => dealer => dealer.Id == this.id; 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /Car Dealers/3. Separation of Concerns/Domain/Dealerships/Specifications/Dealers/DealerByNameSpecification.cs: -------------------------------------------------------------------------------- 1 | namespace CarRentalSystem.Domain.Dealerships.Specifications.Dealers 2 | { 3 | using System; 4 | using System.Linq.Expressions; 5 | using Common; 6 | using Models.Dealers; 7 | 8 | public class DealerByNameSpecification : Specification 9 | { 10 | private readonly string? name; 11 | 12 | public DealerByNameSpecification(string? name) 13 | => this.name = name; 14 | 15 | protected override bool Include => this.name != null; 16 | 17 | public override Expression> ToExpression() 18 | => dealer => dealer.Name.ToLower().Contains(this.name!.ToLower()); 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /Car Dealers/3. Separation of Concerns/Domain/Statistics/Models/CarAdView.cs: -------------------------------------------------------------------------------- 1 | namespace CarRentalSystem.Domain.Statistics.Models 2 | { 3 | using Common.Models; 4 | 5 | public class CarAdView : Entity 6 | { 7 | internal CarAdView(int carAdId, string? userId) 8 | { 9 | this.CarAdId = carAdId; 10 | this.UserId = userId; 11 | } 12 | 13 | public int CarAdId { get; } 14 | 15 | public string? UserId { get; } 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /Car Dealers/3. Separation of Concerns/Domain/Statistics/Models/Statistics.Data.cs: -------------------------------------------------------------------------------- 1 | namespace CarRentalSystem.Domain.Statistics.Models 2 | { 3 | using System; 4 | using System.Collections.Generic; 5 | using Common; 6 | 7 | public class StatisticsData : IInitialData 8 | { 9 | public Type EntityType => typeof(Statistics); 10 | 11 | public IEnumerable GetData() 12 | => new List 13 | { 14 | new Statistics() 15 | }; 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /Car Dealers/3. Separation of Concerns/Infrastructure/Common/Events/IEventDispatcher.cs: -------------------------------------------------------------------------------- 1 | namespace CarRentalSystem.Infrastructure.Common.Events 2 | { 3 | using System.Threading.Tasks; 4 | using Domain.Common; 5 | 6 | internal interface IEventDispatcher 7 | { 8 | Task Dispatch(IDomainEvent domainEvent); 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /Car Dealers/3. Separation of Concerns/Infrastructure/Common/IInitializer.cs: -------------------------------------------------------------------------------- 1 | namespace CarRentalSystem.Infrastructure.Common 2 | { 3 | public interface IInitializer 4 | { 5 | void Initialize(); 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /Car Dealers/3. Separation of Concerns/Infrastructure/Common/Persistence/IDbContext.cs: -------------------------------------------------------------------------------- 1 | namespace CarRentalSystem.Infrastructure.Common.Persistence 2 | { 3 | using System.Threading; 4 | using System.Threading.Tasks; 5 | using Microsoft.EntityFrameworkCore; 6 | using Microsoft.EntityFrameworkCore.ChangeTracking; 7 | 8 | internal interface IDbContext 9 | { 10 | DbSet Set() where TEntity : class; 11 | 12 | EntityEntry Update(TEntity entity) where TEntity : class; 13 | 14 | Task SaveChangesAsync(CancellationToken cancellationToken = default); 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /Car Dealers/3. Separation of Concerns/Infrastructure/Common/QueryableExtensions.cs: -------------------------------------------------------------------------------- 1 | namespace CarRentalSystem.Infrastructure.Common 2 | { 3 | using System.Linq; 4 | using Application.Common; 5 | 6 | public static class QueryableExtensions 7 | { 8 | public static IQueryable Sort( 9 | this IQueryable queryable, 10 | SortOrder sortOrder) 11 | => sortOrder.Order == SortOrder.Descending 12 | ? queryable.OrderByDescending(sortOrder.ToExpression()) 13 | : queryable.OrderBy(sortOrder.ToExpression()); 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /Car Dealers/3. Separation of Concerns/Infrastructure/Dealership/IDealershipDbContext.cs: -------------------------------------------------------------------------------- 1 | namespace CarRentalSystem.Infrastructure.Dealership 2 | { 3 | using Common.Persistence; 4 | using Domain.Dealerships.Models.CarAds; 5 | using Domain.Dealerships.Models.Dealers; 6 | using Identity; 7 | using Microsoft.EntityFrameworkCore; 8 | 9 | internal interface IDealershipDbContext : IDbContext 10 | { 11 | DbSet CarAds { get; } 12 | 13 | DbSet Categories { get; } 14 | 15 | DbSet Manufacturers { get; } 16 | 17 | DbSet Dealers { get; } 18 | 19 | DbSet Users { get; } // TODO: Temporary workaround 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /Car Dealers/3. Separation of Concerns/Infrastructure/Identity/Configuration/UserConfiguration.cs: -------------------------------------------------------------------------------- 1 | namespace CarRentalSystem.Infrastructure.Identity.Configuration 2 | { 3 | using Identity; 4 | using Microsoft.EntityFrameworkCore; 5 | using Microsoft.EntityFrameworkCore.Metadata.Builders; 6 | 7 | public class UserConfiguration : IEntityTypeConfiguration 8 | { 9 | public void Configure(EntityTypeBuilder builder) 10 | { 11 | builder 12 | .HasOne(u => u.Dealer) 13 | .WithOne() 14 | .HasForeignKey("DealerId") 15 | .OnDelete(DeleteBehavior.Restrict); 16 | } 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /Car Dealers/3. Separation of Concerns/Infrastructure/Identity/IJwtTokenGenerator.cs: -------------------------------------------------------------------------------- 1 | namespace CarRentalSystem.Infrastructure.Identity 2 | { 3 | public interface IJwtTokenGenerator 4 | { 5 | string GenerateToken(User user); 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /Car Dealers/3. Separation of Concerns/Infrastructure/Identity/JwtTokenGeneratorService.Fakes.cs: -------------------------------------------------------------------------------- 1 | namespace CarRentalSystem.Infrastructure.Identity 2 | { 3 | using FakeItEasy; 4 | 5 | public class JwtTokenGeneratorFakes 6 | { 7 | public const string ValidToken = "ValidToken"; 8 | 9 | public static IJwtTokenGenerator FakeJwtTokenGenerator 10 | { 11 | get 12 | { 13 | var jwtTokenGenerator = A.Fake(); 14 | 15 | A 16 | .CallTo(() => jwtTokenGenerator.GenerateToken(A.Ignored)) 17 | .Returns(ValidToken); 18 | 19 | return jwtTokenGenerator; 20 | } 21 | } 22 | } 23 | } -------------------------------------------------------------------------------- /Car Dealers/3. Separation of Concerns/Infrastructure/Statistics/IStatisticsDbContext.cs: -------------------------------------------------------------------------------- 1 | namespace CarRentalSystem.Infrastructure.Statistics 2 | { 3 | using Common.Persistence; 4 | using Domain.Statistics.Models; 5 | using Microsoft.EntityFrameworkCore; 6 | 7 | internal interface IStatisticsDbContext : IDbContext 8 | { 9 | DbSet Statistics { get; } 10 | 11 | DbSet CarAdViews { get; } 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /Car Dealers/3. Separation of Concerns/Startup/ApplicationInitialization.cs: -------------------------------------------------------------------------------- 1 | namespace CarRentalSystem.Startup 2 | { 3 | using Infrastructure.Common; 4 | using Microsoft.AspNetCore.Builder; 5 | using Microsoft.Extensions.DependencyInjection; 6 | 7 | public static class ApplicationInitialization 8 | { 9 | public static IApplicationBuilder Initialize(this IApplicationBuilder app) 10 | { 11 | using var serviceScope = app.ApplicationServices.CreateScope(); 12 | 13 | var initializers = serviceScope.ServiceProvider.GetServices(); 14 | 15 | foreach (var initializer in initializers) 16 | { 17 | initializer.Initialize(); 18 | } 19 | 20 | return app; 21 | } 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /Car Dealers/3. Separation of Concerns/Startup/Program.cs: -------------------------------------------------------------------------------- 1 | namespace CarRentalSystem.Startup 2 | { 3 | using Microsoft.AspNetCore.Hosting; 4 | using Microsoft.Extensions.Hosting; 5 | 6 | public class Program 7 | { 8 | public static void Main(string[] args) => CreateHostBuilder(args).Build().Run(); 9 | 10 | public static IHostBuilder CreateHostBuilder(string[] args) 11 | => Host 12 | .CreateDefaultBuilder(args) 13 | .ConfigureWebHostDefaults(webBuilder => 14 | { 15 | webBuilder.UseStartup(); 16 | }); 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /Car Dealers/3. Separation of Concerns/Startup/Specs/DealersController.Specs.cs: -------------------------------------------------------------------------------- 1 | namespace CarRentalSystem.Startup.Specs 2 | { 3 | using Application.Dealerships.Dealers.Queries.Details; 4 | using MyTested.AspNetCore.Mvc; 5 | using Web; 6 | using Web.Features; 7 | using Xunit; 8 | 9 | public class DealersControllerSpecs 10 | { 11 | [Fact] 12 | public void DetailsShouldHaveCorrectAttributes() 13 | => MyController 14 | .Calling(c => c.Details(With.Default())) 15 | 16 | .ShouldHave() 17 | .ActionAttributes(attr => attr 18 | .RestrictingForHttpMethod(HttpMethod.Get) 19 | .SpecifyingRoute(ApiController.Id)); 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /Car Dealers/3. Separation of Concerns/Startup/appsettings.Development.json: -------------------------------------------------------------------------------- 1 | { 2 | "Logging": { 3 | "LogLevel": { 4 | "Default": "Information", 5 | "Microsoft": "Warning", 6 | "Microsoft.Hosting.Lifetime": "Information" 7 | } 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /Car Dealers/3. Separation of Concerns/Startup/appsettings.json: -------------------------------------------------------------------------------- 1 | { 2 | "ApplicationSettings": { 3 | "Secret": "S0M3 M4G1C UN1C0RNS G3N3R4T3D TH1S S3CR3T" 4 | }, 5 | "ConnectionStrings": { 6 | "DefaultConnection": "Server=.;Database=CarRentalSystem;Trusted_Connection=True;MultipleActiveResultSets=true" 7 | }, 8 | "Logging": { 9 | "LogLevel": { 10 | "Default": "Information", 11 | "Microsoft": "Warning", 12 | "Microsoft.Hosting.Lifetime": "Information" 13 | } 14 | }, 15 | "AllowedHosts": "*" 16 | } 17 | -------------------------------------------------------------------------------- /Car Dealers/3. Separation of Concerns/Startup/testsettings.json: -------------------------------------------------------------------------------- 1 | { 2 | "General": { 3 | "WebAssemblyName": "CarRentalSystem.Startup" 4 | }, 5 | "ApplicationSettings": { 6 | "Secret": "My Fake Secret" 7 | }, 8 | "ConnectionStrings": { 9 | "DefaultConnection": "My Fake Connection String" 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /Car Dealers/3. Separation of Concerns/Web/Services/CurrentUserService.cs: -------------------------------------------------------------------------------- 1 | namespace CarRentalSystem.Web.Services 2 | { 3 | using System; 4 | using System.Security.Claims; 5 | using Application.Common.Contracts; 6 | using Microsoft.AspNetCore.Http; 7 | 8 | public class CurrentUserService : ICurrentUser 9 | { 10 | public CurrentUserService(IHttpContextAccessor httpContextAccessor) 11 | { 12 | var user = httpContextAccessor.HttpContext?.User; 13 | 14 | if (user == null) 15 | { 16 | throw new InvalidOperationException("This request does not have an authenticated user."); 17 | } 18 | 19 | this.UserId = user.FindFirstValue(ClaimTypes.NameIdentifier); 20 | } 21 | 22 | public string UserId { get; } 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /Car Dealers/4. Multiple Bounded Contexts/Application/Common/ApplicationSettings.cs: -------------------------------------------------------------------------------- 1 | namespace CarRentalSystem.Application.Common 2 | { 3 | public class ApplicationSettings 4 | { 5 | public ApplicationSettings() => this.Secret = default!; 6 | 7 | public string Secret { get; private set; } 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /Car Dealers/4. Multiple Bounded Contexts/Application/Common/Contracts/ICurrentUser.cs: -------------------------------------------------------------------------------- 1 | namespace CarRentalSystem.Application.Common.Contracts 2 | { 3 | public interface ICurrentUser 4 | { 5 | string UserId { get; } 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /Car Dealers/4. Multiple Bounded Contexts/Application/Common/Contracts/IQueryRepository.cs: -------------------------------------------------------------------------------- 1 | namespace CarRentalSystem.Application.Common.Contracts 2 | { 3 | using Domain.Common; 4 | 5 | public interface IQueryRepository 6 | where TEntity : IAggregateRoot 7 | { 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /Car Dealers/4. Multiple Bounded Contexts/Application/Common/EntityCommand.cs: -------------------------------------------------------------------------------- 1 | namespace CarRentalSystem.Application.Common 2 | { 3 | public class EntityCommand 4 | { 5 | public TId Id { get; set; } = default!; 6 | } 7 | 8 | public static class EntityCommandExtensions 9 | { 10 | public static TCommand SetId(this TCommand command, TId id) 11 | where TCommand : EntityCommand 12 | { 13 | command.Id = id; 14 | return command; 15 | } 16 | } 17 | } -------------------------------------------------------------------------------- /Car Dealers/4. Multiple Bounded Contexts/Application/Common/Exceptions/NotFoundException.cs: -------------------------------------------------------------------------------- 1 | namespace CarRentalSystem.Application.Common.Exceptions 2 | { 3 | using System; 4 | 5 | public class NotFoundException : Exception 6 | { 7 | public NotFoundException(string name, object key) 8 | : base($"Entity '{name}' ({key}) was not found.") 9 | { 10 | } 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /Car Dealers/4. Multiple Bounded Contexts/Application/Common/IEventHandler.cs: -------------------------------------------------------------------------------- 1 | namespace CarRentalSystem.Application.Common 2 | { 3 | using System.Threading.Tasks; 4 | using Domain.Common; 5 | 6 | public interface IEventHandler 7 | where TEvent : IDomainEvent 8 | { 9 | Task Handle(TEvent domainEvent); 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /Car Dealers/4. Multiple Bounded Contexts/Application/Common/Mapping/IMapFrom.cs: -------------------------------------------------------------------------------- 1 | namespace CarRentalSystem.Application.Common.Mapping 2 | { 3 | using AutoMapper; 4 | 5 | public interface IMapFrom 6 | { 7 | void Mapping(Profile mapper) => mapper.CreateMap(typeof(T), this.GetType()); 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /Car Dealers/4. Multiple Bounded Contexts/Application/Common/SortOrder.cs: -------------------------------------------------------------------------------- 1 | namespace CarRentalSystem.Application.Common 2 | { 3 | using System; 4 | using System.Linq.Expressions; 5 | 6 | public abstract class SortOrder 7 | { 8 | public const string Ascending = "asc"; 9 | public const string Descending = "desc"; 10 | 11 | protected SortOrder(string? sortBy, string? order) 12 | { 13 | this.SortBy = sortBy; 14 | this.Order = order; 15 | } 16 | 17 | public string? SortBy { get; } 18 | 19 | public string? Order { get; } 20 | 21 | public abstract Expression> ToExpression(); 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /Car Dealers/4. Multiple Bounded Contexts/Application/Dealerships/CarAds/Commands/Create/CreateCarAdCommandValidator.cs: -------------------------------------------------------------------------------- 1 | namespace CarRentalSystem.Application.Dealerships.CarAds.Commands.Create 2 | { 3 | using Common; 4 | using Domain.Dealerships.Repositories; 5 | using FluentValidation; 6 | 7 | public class CreateCarAdCommandValidator : AbstractValidator 8 | { 9 | public CreateCarAdCommandValidator(ICarAdDomainRepository carAdRepository) 10 | => this.Include(new CarAdCommandValidator(carAdRepository)); 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /Car Dealers/4. Multiple Bounded Contexts/Application/Dealerships/CarAds/Commands/Create/CreateCarAdOutputModel.cs: -------------------------------------------------------------------------------- 1 | namespace CarRentalSystem.Application.Dealerships.CarAds.Commands.Create 2 | { 3 | public class CreateCarAdOutputModel 4 | { 5 | public CreateCarAdOutputModel(int carAdId) 6 | => this.CarAdId = carAdId; 7 | 8 | public int CarAdId { get; } 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /Car Dealers/4. Multiple Bounded Contexts/Application/Dealerships/CarAds/Commands/Edit/EditCarAdCommandValidator.cs: -------------------------------------------------------------------------------- 1 | namespace CarRentalSystem.Application.Dealerships.CarAds.Commands.Edit 2 | { 3 | using Common; 4 | using Domain.Dealerships.Repositories; 5 | using FluentValidation; 6 | 7 | public class EditCarAdCommandValidator : AbstractValidator 8 | { 9 | public EditCarAdCommandValidator(ICarAdDomainRepository carAdRepository) 10 | => this.Include(new CarAdCommandValidator(carAdRepository)); 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /Car Dealers/4. Multiple Bounded Contexts/Application/Dealerships/CarAds/Queries/Categories/GetCarAdCategoryOutputModel.cs: -------------------------------------------------------------------------------- 1 | namespace CarRentalSystem.Application.Dealerships.CarAds.Queries.Categories 2 | { 3 | using Application.Common.Mapping; 4 | using Domain.Dealerships.Models.CarAds; 5 | 6 | public class GetCarAdCategoryOutputModel : IMapFrom 7 | { 8 | public int Id { get; private set; } 9 | 10 | public string Name { get; private set; } = default!; 11 | 12 | public string Description { get; private set; } = default!; 13 | 14 | public int TotalCarAds { get; set; } 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /Car Dealers/4. Multiple Bounded Contexts/Application/Dealerships/CarAds/Queries/Common/CarAdsOutputModel.cs: -------------------------------------------------------------------------------- 1 | namespace CarRentalSystem.Application.Dealerships.CarAds.Queries.Common 2 | { 3 | using System.Collections.Generic; 4 | 5 | public abstract class CarAdsOutputModel 6 | { 7 | internal CarAdsOutputModel( 8 | IEnumerable carAds, 9 | int page, 10 | int totalPages) 11 | { 12 | this.CarAds = carAds; 13 | this.Page = page; 14 | this.TotalPages = totalPages; 15 | } 16 | 17 | public IEnumerable CarAds { get; } 18 | 19 | public int Page { get; } 20 | 21 | public int TotalPages { get; } 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /Car Dealers/4. Multiple Bounded Contexts/Application/Dealerships/CarAds/Queries/Mine/MineCarAdOutputModel.cs: -------------------------------------------------------------------------------- 1 | namespace CarRentalSystem.Application.Dealerships.CarAds.Queries.Mine 2 | { 3 | using AutoMapper; 4 | using Common; 5 | using Domain.Dealerships.Models.CarAds; 6 | 7 | public class MineCarAdOutputModel : CarAdOutputModel 8 | { 9 | public bool IsAvailable { get; private set; } 10 | 11 | public override void Mapping(Profile mapper) 12 | => mapper 13 | .CreateMap() 14 | .IncludeBase(); 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /Car Dealers/4. Multiple Bounded Contexts/Application/Dealerships/CarAds/Queries/Mine/MineCarAdsOutputModel.cs: -------------------------------------------------------------------------------- 1 | namespace CarRentalSystem.Application.Dealerships.CarAds.Queries.Mine 2 | { 3 | using System.Collections.Generic; 4 | using Common; 5 | 6 | public class MineCarAdsOutputModel : CarAdsOutputModel 7 | { 8 | public MineCarAdsOutputModel( 9 | IEnumerable carAds, 10 | int page, 11 | int totalPages) 12 | : base(carAds, page, totalPages) 13 | { 14 | } 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /Car Dealers/4. Multiple Bounded Contexts/Application/Dealerships/CarAds/Queries/Search/SearchCarAdsOutputModel.cs: -------------------------------------------------------------------------------- 1 | namespace CarRentalSystem.Application.Dealerships.CarAds.Queries.Search 2 | { 3 | using System.Collections.Generic; 4 | using Common; 5 | 6 | public class SearchCarAdsOutputModel : CarAdsOutputModel 7 | { 8 | public SearchCarAdsOutputModel( 9 | IEnumerable carAds, 10 | int page, 11 | int totalPages) 12 | : base(carAds, page, totalPages) 13 | { 14 | } 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /Car Dealers/4. Multiple Bounded Contexts/Application/Dealerships/Dealers/IDealerQueryRepository.cs: -------------------------------------------------------------------------------- 1 | namespace CarRentalSystem.Application.Dealerships.Dealers 2 | { 3 | using System.Threading; 4 | using System.Threading.Tasks; 5 | using Common.Contracts; 6 | using Domain.Dealerships.Models.Dealers; 7 | using Queries.Common; 8 | using Queries.Details; 9 | 10 | public interface IDealerQueryRepository : IQueryRepository 11 | { 12 | Task GetDetails(int id, CancellationToken cancellationToken = default); 13 | 14 | Task GetDetailsByCarId(int carAdId, CancellationToken cancellationToken = default); 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /Car Dealers/4. Multiple Bounded Contexts/Application/Dealerships/Dealers/Queries/Details/DealerDetailsOutputModel.cs: -------------------------------------------------------------------------------- 1 | namespace CarRentalSystem.Application.Dealerships.Dealers.Queries.Details 2 | { 3 | using AutoMapper; 4 | using Common; 5 | using Domain.Dealerships.Models.Dealers; 6 | 7 | public class DealerDetailsOutputModel : DealerOutputModel 8 | { 9 | public int TotalCarAds { get; private set; } 10 | 11 | public override void Mapping(Profile mapper) 12 | => mapper 13 | .CreateMap() 14 | .IncludeBase() 15 | .ForMember(d => d.TotalCarAds, cfg => cfg 16 | .MapFrom(d => d.CarAds.Count)); 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /Car Dealers/4. Multiple Bounded Contexts/Application/Identity/Commands/ChangePassword/ChangePasswordInputModel.cs: -------------------------------------------------------------------------------- 1 | namespace CarRentalSystem.Application.Identity.Commands.ChangePassword 2 | { 3 | public class ChangePasswordInputModel 4 | { 5 | public ChangePasswordInputModel( 6 | string userId, 7 | string currentPassword, 8 | string newPassword) 9 | { 10 | this.UserId = userId; 11 | this.CurrentPassword = currentPassword; 12 | this.NewPassword = newPassword; 13 | } 14 | 15 | public string UserId { get; } 16 | 17 | public string CurrentPassword { get; } 18 | 19 | public string NewPassword { get; } 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /Car Dealers/4. Multiple Bounded Contexts/Application/Identity/Commands/CreateUser/CreateUserCommand.Fakes.cs: -------------------------------------------------------------------------------- 1 | namespace CarRentalSystem.Application.Identity.Commands.CreateUser 2 | { 3 | using Bogus; 4 | 5 | public class CreateUserCommandFakes 6 | { 7 | public static class Data 8 | { 9 | public static CreateUserCommand GetCommand() 10 | => new Faker() 11 | .RuleFor(u => u.Email, f => f.Internet.Email()) 12 | .RuleFor(u => u.Password, f => f.Lorem.Letter(10)) 13 | .RuleFor(u => u.Name, f => f.Name.FullName()) 14 | .RuleFor(u => u.PhoneNumber, f => f.Phone.PhoneNumber("+#######")) 15 | .Generate(); 16 | } 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /Car Dealers/4. Multiple Bounded Contexts/Application/Identity/Commands/LoginUser/LoginOutputModel.cs: -------------------------------------------------------------------------------- 1 | namespace CarRentalSystem.Application.Identity.Commands.LoginUser 2 | { 3 | public class LoginOutputModel 4 | { 5 | public LoginOutputModel(string token, int dealerId) 6 | { 7 | this.Token = token; 8 | this.DealerId = dealerId; 9 | } 10 | 11 | public int DealerId { get; } 12 | 13 | public string Token { get; } 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /Car Dealers/4. Multiple Bounded Contexts/Application/Identity/Commands/LoginUser/LoginSuccessModel.cs: -------------------------------------------------------------------------------- 1 | namespace CarRentalSystem.Application.Identity.Commands.LoginUser 2 | { 3 | public class LoginSuccessModel 4 | { 5 | public LoginSuccessModel(string userId, string token) 6 | { 7 | this.UserId = userId; 8 | this.Token = token; 9 | } 10 | 11 | public string UserId { get; } 12 | 13 | public string Token { get; } 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /Car Dealers/4. Multiple Bounded Contexts/Application/Identity/Commands/UserInputModel.cs: -------------------------------------------------------------------------------- 1 | namespace CarRentalSystem.Application.Identity.Commands 2 | { 3 | public abstract class UserInputModel 4 | { 5 | public string Email { get; set; } = default!; 6 | 7 | public string Password { get; set; } = default!; 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /Car Dealers/4. Multiple Bounded Contexts/Application/Identity/IIdentity.cs: -------------------------------------------------------------------------------- 1 | namespace CarRentalSystem.Application.Identity 2 | { 3 | using System.Threading.Tasks; 4 | using Commands; 5 | using Commands.ChangePassword; 6 | using Commands.LoginUser; 7 | using Common; 8 | 9 | public interface IIdentity 10 | { 11 | Task> Register(UserInputModel userInput); 12 | 13 | Task> Login(UserInputModel userInput); 14 | 15 | Task ChangePassword(ChangePasswordInputModel changePasswordInput); 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /Car Dealers/4. Multiple Bounded Contexts/Application/Identity/IUser.cs: -------------------------------------------------------------------------------- 1 | namespace CarRentalSystem.Application.Identity 2 | { 3 | using Domain.Dealerships.Models.Dealers; 4 | 5 | public interface IUser 6 | { 7 | void BecomeDealer(Dealer dealer); 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /Car Dealers/4. Multiple Bounded Contexts/Application/Statistics/Handlers/CarAdAddedEventHandler.cs: -------------------------------------------------------------------------------- 1 | namespace CarRentalSystem.Application.Statistics.Handlers 2 | { 3 | using System.Threading.Tasks; 4 | using Common; 5 | using Domain.Dealerships.Events.Dealers; 6 | using Domain.Statistics.Repositories; 7 | 8 | public class CarAdAddedEventHandler : IEventHandler 9 | { 10 | private readonly IStatisticsDomainRepository statistics; 11 | 12 | public CarAdAddedEventHandler(IStatisticsDomainRepository statistics) 13 | => this.statistics = statistics; 14 | 15 | public Task Handle(CarAdAddedEvent domainEvent) 16 | => this.statistics.IncrementCarAds(); 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /Car Dealers/4. Multiple Bounded Contexts/Application/Statistics/IStatisticsQueryRepository.cs: -------------------------------------------------------------------------------- 1 | namespace CarRentalSystem.Application.Statistics 2 | { 3 | using System.Threading; 4 | using System.Threading.Tasks; 5 | using Common.Contracts; 6 | using Domain.Statistics.Models; 7 | using Queries.Current; 8 | 9 | public interface IStatisticsQueryRepository : IQueryRepository 10 | { 11 | Task GetCurrent(CancellationToken cancellationToken = default); 12 | 13 | Task GetCarAdViews(int carAdId, CancellationToken cancellationToken = default); 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /Car Dealers/4. Multiple Bounded Contexts/Application/Statistics/Queries/Current/GetCurrentStatisticsOutputModel.cs: -------------------------------------------------------------------------------- 1 | namespace CarRentalSystem.Application.Statistics.Queries.Current 2 | { 3 | using AutoMapper; 4 | using Common.Mapping; 5 | using Domain.Statistics.Models; 6 | 7 | public class GetCurrentStatisticsOutputModel : IMapFrom 8 | { 9 | public int TotalCarAds { get; private set; } 10 | 11 | public int TotalCarAdViews { get; private set; } 12 | 13 | public void Mapping(Profile mapper) 14 | => mapper 15 | .CreateMap() 16 | .ForMember(cs => cs.TotalCarAds, cfg => cfg 17 | .MapFrom(s => s.CarAdViews.Count)); 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /Car Dealers/4. Multiple Bounded Contexts/Domain/Common/BaseDomainException.cs: -------------------------------------------------------------------------------- 1 | namespace CarRentalSystem.Domain.Common 2 | { 3 | using System; 4 | 5 | public abstract class BaseDomainException : Exception 6 | { 7 | private string? error; 8 | 9 | public string Error 10 | { 11 | get => this.error ?? base.Message; 12 | set => this.error = value; 13 | } 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /Car Dealers/4. Multiple Bounded Contexts/Domain/Common/IAggregateRoot.cs: -------------------------------------------------------------------------------- 1 | namespace CarRentalSystem.Domain.Common 2 | { 3 | public interface IAggregateRoot 4 | { 5 | } 6 | } 7 | -------------------------------------------------------------------------------- /Car Dealers/4. Multiple Bounded Contexts/Domain/Common/IDomainEvent.cs: -------------------------------------------------------------------------------- 1 | namespace CarRentalSystem.Domain.Common 2 | { 3 | public interface IDomainEvent 4 | { 5 | } 6 | } 7 | -------------------------------------------------------------------------------- /Car Dealers/4. Multiple Bounded Contexts/Domain/Common/IDomainRepository.cs: -------------------------------------------------------------------------------- 1 | namespace CarRentalSystem.Domain.Common 2 | { 3 | using System.Threading; 4 | using System.Threading.Tasks; 5 | 6 | public interface IDomainRepository 7 | where TEntity : IAggregateRoot 8 | { 9 | Task Save(TEntity entity, CancellationToken cancellationToken = default); 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /Car Dealers/4. Multiple Bounded Contexts/Domain/Common/IFactory.cs: -------------------------------------------------------------------------------- 1 | namespace CarRentalSystem.Domain.Common 2 | { 3 | public interface IFactory 4 | where TEntity : IAggregateRoot 5 | { 6 | TEntity Build(); 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /Car Dealers/4. Multiple Bounded Contexts/Domain/Common/IInitialData.cs: -------------------------------------------------------------------------------- 1 | namespace CarRentalSystem.Domain.Common 2 | { 3 | using System; 4 | using System.Collections.Generic; 5 | 6 | public interface IInitialData 7 | { 8 | Type EntityType { get; } 9 | 10 | IEnumerable GetData(); 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /Car Dealers/4. Multiple Bounded Contexts/Domain/Common/Models/IEntity.cs: -------------------------------------------------------------------------------- 1 | namespace CarRentalSystem.Domain.Common.Models 2 | { 3 | using System.Collections.Generic; 4 | 5 | public interface IEntity 6 | { 7 | IReadOnlyCollection Events { get; } 8 | 9 | void ClearEvents(); 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /Car Dealers/4. Multiple Bounded Contexts/Domain/Dealerships/Events/Dealers/CarAdAddedEvent.cs: -------------------------------------------------------------------------------- 1 | namespace CarRentalSystem.Domain.Dealerships.Events.Dealers 2 | { 3 | using Common; 4 | 5 | public class CarAdAddedEvent : IDomainEvent 6 | { 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /Car Dealers/4. Multiple Bounded Contexts/Domain/Dealerships/Exceptions/InvalidCarAdException.cs: -------------------------------------------------------------------------------- 1 | namespace CarRentalSystem.Domain.Dealerships.Exceptions 2 | { 3 | using Common; 4 | 5 | public class InvalidCarAdException : BaseDomainException 6 | { 7 | public InvalidCarAdException() 8 | { 9 | } 10 | 11 | public InvalidCarAdException(string error) => this.Error = error; 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /Car Dealers/4. Multiple Bounded Contexts/Domain/Dealerships/Exceptions/InvalidDealerException.cs: -------------------------------------------------------------------------------- 1 | namespace CarRentalSystem.Domain.Dealerships.Exceptions 2 | { 3 | using Common; 4 | 5 | public class InvalidDealerException : BaseDomainException 6 | { 7 | public InvalidDealerException() 8 | { 9 | } 10 | 11 | public InvalidDealerException(string error) => this.Error = error; 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /Car Dealers/4. Multiple Bounded Contexts/Domain/Dealerships/Exceptions/InvalidOptionsException.cs: -------------------------------------------------------------------------------- 1 | namespace CarRentalSystem.Domain.Dealerships.Exceptions 2 | { 3 | using Common; 4 | 5 | public class InvalidOptionsException : BaseDomainException 6 | { 7 | public InvalidOptionsException() 8 | { 9 | } 10 | 11 | public InvalidOptionsException(string error) => this.Error = error; 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /Car Dealers/4. Multiple Bounded Contexts/Domain/Dealerships/Exceptions/InvalidPhoneNumberException.cs: -------------------------------------------------------------------------------- 1 | namespace CarRentalSystem.Domain.Dealerships.Exceptions 2 | { 3 | using Common; 4 | 5 | public class InvalidPhoneNumberException : BaseDomainException 6 | { 7 | public InvalidPhoneNumberException() 8 | { 9 | } 10 | 11 | public InvalidPhoneNumberException(string error) => this.Error = error; 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /Car Dealers/4. Multiple Bounded Contexts/Domain/Dealerships/Factories/Dealers/DealerFactory.cs: -------------------------------------------------------------------------------- 1 | namespace CarRentalSystem.Domain.Dealerships.Factories.Dealers 2 | { 3 | using Models.Dealers; 4 | 5 | internal class DealerFactory : IDealerFactory 6 | { 7 | private string dealerName = default!; 8 | private string dealerPhoneNumber = default!; 9 | 10 | public IDealerFactory WithName(string name) 11 | { 12 | this.dealerName = name; 13 | return this; 14 | } 15 | 16 | public IDealerFactory WithPhoneNumber(string phoneNumber) 17 | { 18 | this.dealerPhoneNumber = phoneNumber; 19 | return this; 20 | } 21 | 22 | public Dealer Build() => new Dealer(this.dealerName, this.dealerPhoneNumber); 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /Car Dealers/4. Multiple Bounded Contexts/Domain/Dealerships/Factories/Dealers/IDealerFactory.cs: -------------------------------------------------------------------------------- 1 | namespace CarRentalSystem.Domain.Dealerships.Factories.Dealers 2 | { 3 | using Common; 4 | using Models.Dealers; 5 | 6 | public interface IDealerFactory : IFactory 7 | { 8 | IDealerFactory WithName(string name); 9 | 10 | IDealerFactory WithPhoneNumber(string phoneNumber); 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /Car Dealers/4. Multiple Bounded Contexts/Domain/Dealerships/Models/CarAds/CarAd.Specs.cs: -------------------------------------------------------------------------------- 1 | namespace CarRentalSystem.Domain.Dealerships.Models.CarAds 2 | { 3 | using FakeItEasy; 4 | using FluentAssertions; 5 | using Xunit; 6 | 7 | public class CarAdSpecs 8 | { 9 | [Fact] 10 | public void ChangeAvailabilityShouldMutateIsAvailable() 11 | { 12 | // Arrange 13 | var carAd = A.Dummy(); 14 | 15 | // Act 16 | carAd.ChangeAvailability(); 17 | 18 | // Assert 19 | carAd.IsAvailable.Should().BeFalse(); 20 | } 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /Car Dealers/4. Multiple Bounded Contexts/Domain/Dealerships/Models/CarAds/Category.Fakes.cs: -------------------------------------------------------------------------------- 1 | namespace CarRentalSystem.Domain.Dealerships.Models.CarAds 2 | { 3 | using System; 4 | using FakeItEasy; 5 | 6 | public class CategoryFakes 7 | { 8 | public const string ValidCategoryName = "Economy"; 9 | 10 | public class CategoryDummyFactory : IDummyFactory 11 | { 12 | public bool CanCreate(Type type) => type == typeof(Category); 13 | 14 | public object? Create(Type type) 15 | => new Category(ValidCategoryName, "Valid description text"); 16 | 17 | public Priority Priority => Priority.Default; 18 | } 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /Car Dealers/4. Multiple Bounded Contexts/Domain/Dealerships/Models/CarAds/Manufacturer.Fakes.cs: -------------------------------------------------------------------------------- 1 | namespace CarRentalSystem.Domain.Dealerships.Models.CarAds 2 | { 3 | using System; 4 | using FakeItEasy; 5 | 6 | public class ManufacturerFakes 7 | { 8 | public class ManufacturerDummyFactory : IDummyFactory 9 | { 10 | public bool CanCreate(Type type) => type == typeof(Manufacturer); 11 | 12 | public object? Create(Type type) 13 | => new Manufacturer("Valid manufacturer"); 14 | 15 | public Priority Priority => Priority.Default; 16 | } 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /Car Dealers/4. Multiple Bounded Contexts/Domain/Dealerships/Models/CarAds/Options.Fakes.cs: -------------------------------------------------------------------------------- 1 | namespace CarRentalSystem.Domain.Dealerships.Models.CarAds 2 | { 3 | using System; 4 | using FakeItEasy; 5 | 6 | public class OptionsFakes 7 | { 8 | public class OptionsDummyFactory : IDummyFactory 9 | { 10 | public bool CanCreate(Type type) => type == typeof(Options); 11 | 12 | public object? Create(Type type) 13 | => new Options(true, 4, TransmissionType.Automatic); 14 | 15 | public Priority Priority => Priority.Default; 16 | } 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /Car Dealers/4. Multiple Bounded Contexts/Domain/Dealerships/Models/CarAds/TransmissionType.cs: -------------------------------------------------------------------------------- 1 | namespace CarRentalSystem.Domain.Dealerships.Models.CarAds 2 | { 3 | using Common.Models; 4 | 5 | public class TransmissionType : Enumeration 6 | { 7 | public static readonly TransmissionType Manual = new TransmissionType(1, nameof(Manual)); 8 | public static readonly TransmissionType Automatic = new TransmissionType(2, nameof(Automatic)); 9 | 10 | private TransmissionType(int value) 11 | : this(value, FromValue(value).Name) 12 | { 13 | } 14 | 15 | private TransmissionType(int value, string name) 16 | : base(value, name) 17 | { 18 | } 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /Car Dealers/4. Multiple Bounded Contexts/Domain/Dealerships/Models/Dealers/Dealer.Specs.cs: -------------------------------------------------------------------------------- 1 | namespace CarRentalSystem.Domain.Dealerships.Models.Dealers 2 | { 3 | using CarAds; 4 | using FakeItEasy; 5 | using FluentAssertions; 6 | using Xunit; 7 | 8 | public class DealerSpecs 9 | { 10 | [Fact] 11 | public void AddCarAdShouldSaveCarAd() 12 | { 13 | // Arrange 14 | var dealer = new Dealer("Valid dealer", "+12345678"); 15 | var carAd = A.Dummy(); 16 | 17 | // Act 18 | dealer.AddCarAd(carAd); 19 | 20 | // Assert 21 | dealer.CarAds.Should().Contain(carAd); 22 | } 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /Car Dealers/4. Multiple Bounded Contexts/Domain/Dealerships/Repositories/IDealerDomainRepository.cs: -------------------------------------------------------------------------------- 1 | namespace CarRentalSystem.Domain.Dealerships.Repositories 2 | { 3 | using System.Threading; 4 | using System.Threading.Tasks; 5 | using Common; 6 | using Models.Dealers; 7 | 8 | public interface IDealerDomainRepository : IDomainRepository 9 | { 10 | Task FindByUser(string userId, CancellationToken cancellationToken = default); 11 | 12 | Task GetDealerId(string userId, CancellationToken cancellationToken = default); 13 | 14 | Task HasCarAd(int dealerId, int carAdId, CancellationToken cancellationToken = default); 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /Car Dealers/4. Multiple Bounded Contexts/Domain/Dealerships/Specifications/CarAds/CarAdByCategorySpecification.cs: -------------------------------------------------------------------------------- 1 | namespace CarRentalSystem.Domain.Dealerships.Specifications.CarAds 2 | { 3 | using System; 4 | using System.Linq.Expressions; 5 | using Common; 6 | using Models.CarAds; 7 | 8 | public class CarAdByCategorySpecification : Specification 9 | { 10 | private readonly int? category; 11 | 12 | public CarAdByCategorySpecification(int? category) 13 | => this.category = category; 14 | 15 | protected override bool Include => this.category != null; 16 | 17 | public override Expression> ToExpression() 18 | => carAd => carAd.Category.Id == this.category; 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /Car Dealers/4. Multiple Bounded Contexts/Domain/Dealerships/Specifications/Dealers/DealerByIdSpecification.cs: -------------------------------------------------------------------------------- 1 | namespace CarRentalSystem.Domain.Dealerships.Specifications.Dealers 2 | { 3 | using System; 4 | using System.Linq.Expressions; 5 | using Common; 6 | using Models.Dealers; 7 | 8 | public class DealerByIdSpecification : Specification 9 | { 10 | private readonly int? id; 11 | 12 | public DealerByIdSpecification(int? id) 13 | => this.id = id; 14 | 15 | protected override bool Include => this.id != null; 16 | 17 | public override Expression> ToExpression() 18 | => dealer => dealer.Id == this.id; 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /Car Dealers/4. Multiple Bounded Contexts/Domain/Dealerships/Specifications/Dealers/DealerByNameSpecification.cs: -------------------------------------------------------------------------------- 1 | namespace CarRentalSystem.Domain.Dealerships.Specifications.Dealers 2 | { 3 | using System; 4 | using System.Linq.Expressions; 5 | using Common; 6 | using Models.Dealers; 7 | 8 | public class DealerByNameSpecification : Specification 9 | { 10 | private readonly string? name; 11 | 12 | public DealerByNameSpecification(string? name) 13 | => this.name = name; 14 | 15 | protected override bool Include => this.name != null; 16 | 17 | public override Expression> ToExpression() 18 | => dealer => dealer.Name.ToLower().Contains(this.name!.ToLower()); 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /Car Dealers/4. Multiple Bounded Contexts/Domain/Renting/Models/Feedback.cs: -------------------------------------------------------------------------------- 1 | namespace CarRentalSystem.Domain.Renting.Models 2 | { 3 | using Common.Models; 4 | 5 | public class Feedback : Entity 6 | { 7 | internal Feedback(string content, Reservation reservation) 8 | { 9 | // TODO: Add validation. 10 | // this.Validate(content); 11 | 12 | this.Content = content; 13 | this.Reservation = reservation; 14 | } 15 | 16 | public string Content { get; private set; } 17 | 18 | public Reservation Reservation { get; private set; } 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /Car Dealers/4. Multiple Bounded Contexts/Domain/Renting/Models/Reservation.cs: -------------------------------------------------------------------------------- 1 | namespace CarRentalSystem.Domain.Renting.Models 2 | { 3 | using Common.Models; 4 | 5 | public class Reservation : Entity 6 | { 7 | internal Reservation(DateTimeRange timeRange, RentedCar rentedCar) 8 | { 9 | // TODO: Add validation. 10 | 11 | this.TimeRange = timeRange; 12 | this.RentedCar = rentedCar; 13 | } 14 | 15 | public DateTimeRange TimeRange { get; private set; } 16 | 17 | public RentedCar RentedCar { get; private set; } 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /Car Dealers/4. Multiple Bounded Contexts/Domain/Renting/Services/IRentingScheduleService.cs: -------------------------------------------------------------------------------- 1 | namespace CarRentalSystem.Domain.Renting.Services 2 | { 3 | using System.Collections.Generic; 4 | using Models; 5 | 6 | public interface IRentingScheduleService 7 | { 8 | bool IsOverlapping(DateTimeRange timeRange, IEnumerable reservations); 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /Car Dealers/4. Multiple Bounded Contexts/Domain/Renting/Services/RentingScheduleService.cs: -------------------------------------------------------------------------------- 1 | namespace CarRentalSystem.Domain.Renting.Services 2 | { 3 | using System.Collections.Generic; 4 | using System.Linq; 5 | using Models; 6 | 7 | public class RentingScheduleService : IRentingScheduleService 8 | { 9 | public bool IsOverlapping(DateTimeRange timeRange, IEnumerable reservations) 10 | => reservations.Any(reservation => reservation 11 | .TimeRange.Overlaps(timeRange)); 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /Car Dealers/4. Multiple Bounded Contexts/Domain/Statistics/Models/CarAdView.cs: -------------------------------------------------------------------------------- 1 | namespace CarRentalSystem.Domain.Statistics.Models 2 | { 3 | using Common.Models; 4 | 5 | public class CarAdView : Entity 6 | { 7 | internal CarAdView(int carAdId, string? userId) 8 | { 9 | this.CarAdId = carAdId; 10 | this.UserId = userId; 11 | } 12 | 13 | public int CarAdId { get; } 14 | 15 | public string? UserId { get; } 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /Car Dealers/4. Multiple Bounded Contexts/Domain/Statistics/Models/Statistics.Data.cs: -------------------------------------------------------------------------------- 1 | namespace CarRentalSystem.Domain.Statistics.Models 2 | { 3 | using System; 4 | using System.Collections.Generic; 5 | using Common; 6 | 7 | public class StatisticsData : IInitialData 8 | { 9 | public Type EntityType => typeof(Statistics); 10 | 11 | public IEnumerable GetData() 12 | => new List 13 | { 14 | new Statistics() 15 | }; 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /Car Dealers/4. Multiple Bounded Contexts/Domain/Statistics/Repositories/IStatisticsDomainRepository.cs: -------------------------------------------------------------------------------- 1 | namespace CarRentalSystem.Domain.Statistics.Repositories 2 | { 3 | using System.Threading; 4 | using System.Threading.Tasks; 5 | using Common; 6 | using Models; 7 | 8 | public interface IStatisticsDomainRepository : IDomainRepository 9 | { 10 | Task IncrementCarAds(CancellationToken cancellationToken = default); 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /Car Dealers/4. Multiple Bounded Contexts/Infrastructure/Common/Events/IEventDispatcher.cs: -------------------------------------------------------------------------------- 1 | namespace CarRentalSystem.Infrastructure.Common.Events 2 | { 3 | using System.Threading.Tasks; 4 | using Domain.Common; 5 | 6 | internal interface IEventDispatcher 7 | { 8 | Task Dispatch(IDomainEvent domainEvent); 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /Car Dealers/4. Multiple Bounded Contexts/Infrastructure/Common/IInitializer.cs: -------------------------------------------------------------------------------- 1 | namespace CarRentalSystem.Infrastructure.Common 2 | { 3 | public interface IInitializer 4 | { 5 | void Initialize(); 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /Car Dealers/4. Multiple Bounded Contexts/Infrastructure/Common/Persistence/IDbContext.cs: -------------------------------------------------------------------------------- 1 | namespace CarRentalSystem.Infrastructure.Common.Persistence 2 | { 3 | using System.Threading; 4 | using System.Threading.Tasks; 5 | using Microsoft.EntityFrameworkCore; 6 | using Microsoft.EntityFrameworkCore.ChangeTracking; 7 | 8 | internal interface IDbContext 9 | { 10 | DbSet Set() where TEntity : class; 11 | 12 | EntityEntry Update(TEntity entity) where TEntity : class; 13 | 14 | Task SaveChangesAsync(CancellationToken cancellationToken = default); 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /Car Dealers/4. Multiple Bounded Contexts/Infrastructure/Common/Persistence/Models/DealerData.cs: -------------------------------------------------------------------------------- 1 | namespace CarRentalSystem.Infrastructure.Common.Persistence.Models 2 | { 3 | using System.Collections.Generic; 4 | using Domain.Dealerships.Models.Dealers; 5 | 6 | internal class DealerData 7 | { 8 | public int Id { get; set; } 9 | 10 | public string Name { get; set; } = default!; 11 | 12 | public PhoneNumber PhoneNumber { get; set; } = default!; 13 | 14 | public ICollection CarAds { get; set; } = new HashSet(); 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /Car Dealers/4. Multiple Bounded Contexts/Infrastructure/Common/QueryableExtensions.cs: -------------------------------------------------------------------------------- 1 | namespace CarRentalSystem.Infrastructure.Common 2 | { 3 | using System.Linq; 4 | using Application.Common; 5 | 6 | public static class QueryableExtensions 7 | { 8 | public static IQueryable Sort( 9 | this IQueryable queryable, 10 | SortOrder sortOrder) 11 | => sortOrder.Order == SortOrder.Descending 12 | ? queryable.OrderByDescending(sortOrder.ToExpression()) 13 | : queryable.OrderBy(sortOrder.ToExpression()); 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /Car Dealers/4. Multiple Bounded Contexts/Infrastructure/Dealership/IDealershipDbContext.cs: -------------------------------------------------------------------------------- 1 | namespace CarRentalSystem.Infrastructure.Dealership 2 | { 3 | using Common.Persistence; 4 | using Domain.Dealerships.Models.CarAds; 5 | using Domain.Dealerships.Models.Dealers; 6 | using Identity; 7 | using Microsoft.EntityFrameworkCore; 8 | 9 | internal interface IDealershipDbContext : IDbContext 10 | { 11 | DbSet CarAds { get; } 12 | 13 | DbSet Categories { get; } 14 | 15 | DbSet Manufacturers { get; } 16 | 17 | DbSet Dealers { get; } 18 | 19 | DbSet Users { get; } // TODO: Temporary workaround 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /Car Dealers/4. Multiple Bounded Contexts/Infrastructure/Identity/Configuration/UserConfiguration.cs: -------------------------------------------------------------------------------- 1 | namespace CarRentalSystem.Infrastructure.Identity.Configuration 2 | { 3 | using Identity; 4 | using Microsoft.EntityFrameworkCore; 5 | using Microsoft.EntityFrameworkCore.Metadata.Builders; 6 | 7 | public class UserConfiguration : IEntityTypeConfiguration 8 | { 9 | public void Configure(EntityTypeBuilder builder) 10 | { 11 | builder 12 | .HasOne(u => u.Dealer) 13 | .WithOne() 14 | .HasForeignKey("DealerId") 15 | .OnDelete(DeleteBehavior.Restrict); 16 | } 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /Car Dealers/4. Multiple Bounded Contexts/Infrastructure/Identity/IJwtTokenGenerator.cs: -------------------------------------------------------------------------------- 1 | namespace CarRentalSystem.Infrastructure.Identity 2 | { 3 | public interface IJwtTokenGenerator 4 | { 5 | string GenerateToken(User user); 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /Car Dealers/4. Multiple Bounded Contexts/Infrastructure/Identity/JwtTokenGeneratorService.Fakes.cs: -------------------------------------------------------------------------------- 1 | namespace CarRentalSystem.Infrastructure.Identity 2 | { 3 | using FakeItEasy; 4 | 5 | public class JwtTokenGeneratorFakes 6 | { 7 | public const string ValidToken = "ValidToken"; 8 | 9 | public static IJwtTokenGenerator FakeJwtTokenGenerator 10 | { 11 | get 12 | { 13 | var jwtTokenGenerator = A.Fake(); 14 | 15 | A 16 | .CallTo(() => jwtTokenGenerator.GenerateToken(A.Ignored)) 17 | .Returns(ValidToken); 18 | 19 | return jwtTokenGenerator; 20 | } 21 | } 22 | } 23 | } -------------------------------------------------------------------------------- /Car Dealers/4. Multiple Bounded Contexts/Infrastructure/Statistics/IStatisticsDbContext.cs: -------------------------------------------------------------------------------- 1 | namespace CarRentalSystem.Infrastructure.Statistics 2 | { 3 | using Common.Persistence; 4 | using Domain.Statistics.Models; 5 | using Microsoft.EntityFrameworkCore; 6 | 7 | internal interface IStatisticsDbContext : IDbContext 8 | { 9 | DbSet Statistics { get; } 10 | 11 | DbSet CarAdViews { get; } 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /Car Dealers/4. Multiple Bounded Contexts/Startup/ApplicationInitialization.cs: -------------------------------------------------------------------------------- 1 | namespace CarRentalSystem.Startup 2 | { 3 | using Infrastructure.Common; 4 | using Microsoft.AspNetCore.Builder; 5 | using Microsoft.Extensions.DependencyInjection; 6 | 7 | public static class ApplicationInitialization 8 | { 9 | public static IApplicationBuilder Initialize(this IApplicationBuilder app) 10 | { 11 | using var serviceScope = app.ApplicationServices.CreateScope(); 12 | 13 | var initializers = serviceScope.ServiceProvider.GetServices(); 14 | 15 | foreach (var initializer in initializers) 16 | { 17 | initializer.Initialize(); 18 | } 19 | 20 | return app; 21 | } 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /Car Dealers/4. Multiple Bounded Contexts/Startup/Program.cs: -------------------------------------------------------------------------------- 1 | namespace CarRentalSystem.Startup 2 | { 3 | using Microsoft.AspNetCore.Hosting; 4 | using Microsoft.Extensions.Hosting; 5 | 6 | public class Program 7 | { 8 | public static void Main(string[] args) => CreateHostBuilder(args).Build().Run(); 9 | 10 | public static IHostBuilder CreateHostBuilder(string[] args) 11 | => Host 12 | .CreateDefaultBuilder(args) 13 | .ConfigureWebHostDefaults(webBuilder => 14 | { 15 | webBuilder.UseStartup(); 16 | }); 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /Car Dealers/4. Multiple Bounded Contexts/Startup/Specs/DealersController.Specs.cs: -------------------------------------------------------------------------------- 1 | namespace CarRentalSystem.Startup.Specs 2 | { 3 | using Application.Dealerships.Dealers.Queries.Details; 4 | using MyTested.AspNetCore.Mvc; 5 | using Web; 6 | using Web.Features; 7 | using Xunit; 8 | 9 | public class DealersControllerSpecs 10 | { 11 | [Fact] 12 | public void DetailsShouldHaveCorrectAttributes() 13 | => MyController 14 | .Calling(c => c.Details(With.Default())) 15 | 16 | .ShouldHave() 17 | .ActionAttributes(attr => attr 18 | .RestrictingForHttpMethod(HttpMethod.Get) 19 | .SpecifyingRoute(ApiController.Id)); 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /Car Dealers/4. Multiple Bounded Contexts/Startup/appsettings.Development.json: -------------------------------------------------------------------------------- 1 | { 2 | "Logging": { 3 | "LogLevel": { 4 | "Default": "Information", 5 | "Microsoft": "Warning", 6 | "Microsoft.Hosting.Lifetime": "Information" 7 | } 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /Car Dealers/4. Multiple Bounded Contexts/Startup/appsettings.json: -------------------------------------------------------------------------------- 1 | { 2 | "ApplicationSettings": { 3 | "Secret": "S0M3 M4G1C UN1C0RNS G3N3R4T3D TH1S S3CR3T" 4 | }, 5 | "ConnectionStrings": { 6 | "DefaultConnection": "Server=.;Database=CarRentalSystem;Trusted_Connection=True;MultipleActiveResultSets=true" 7 | }, 8 | "Logging": { 9 | "LogLevel": { 10 | "Default": "Information", 11 | "Microsoft": "Warning", 12 | "Microsoft.Hosting.Lifetime": "Information" 13 | } 14 | }, 15 | "AllowedHosts": "*" 16 | } 17 | -------------------------------------------------------------------------------- /Car Dealers/4. Multiple Bounded Contexts/Startup/testsettings.json: -------------------------------------------------------------------------------- 1 | { 2 | "General": { 3 | "WebAssemblyName": "CarRentalSystem.Startup" 4 | }, 5 | "ApplicationSettings": { 6 | "Secret": "My Fake Secret" 7 | }, 8 | "ConnectionStrings": { 9 | "DefaultConnection": "My Fake Connection String" 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /Domain Models/Blog/Blog.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | netstandard2.0 5 | 8.0 6 | enable 7 | true 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | -------------------------------------------------------------------------------- /Domain Models/Blog/Factories/ICourseFactory.cs: -------------------------------------------------------------------------------- 1 | namespace Blog.Factories 2 | { 3 | using System; 4 | using Common; 5 | using InnerFactories; 6 | using Models; 7 | 8 | public interface ICourseFactory : IFactory 9 | { 10 | ICourseFactory WithName(string name); 11 | 12 | ICourseFactory WithStudent(Action student); 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /Domain Models/Blog/Models/Course.cs: -------------------------------------------------------------------------------- 1 | namespace Blog.Models 2 | { 3 | using System.Collections.Generic; 4 | using Common; 5 | using Common.Models; 6 | 7 | public class Course : Entity, IAggregateRoot 8 | { 9 | private readonly HashSet students; 10 | 11 | internal Course(string name) 12 | { 13 | this.Name = name; 14 | 15 | this.students = new HashSet(); 16 | } 17 | 18 | public string Name { get; private set; } 19 | 20 | public void AddStudent(Student student) 21 | => this.students.Add(student); 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /Domain Models/Blog/Models/Student.cs: -------------------------------------------------------------------------------- 1 | namespace Blog.Models 2 | { 3 | using Common.Models; 4 | 5 | public class Student : Entity 6 | { 7 | internal Student(string firstName, string lastName) 8 | { 9 | this.FirstName = firstName; 10 | this.LastName = lastName; 11 | } 12 | 13 | public string FirstName { get; private set; } 14 | 15 | public string LastName { get; private set; } 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /Domain Models/Common/BaseDomainException.cs: -------------------------------------------------------------------------------- 1 | namespace Common 2 | { 3 | using System; 4 | 5 | public abstract class BaseDomainException : Exception 6 | { 7 | private string? error; 8 | 9 | public string Error 10 | { 11 | get => this.error ?? base.Message; 12 | set => this.error = value; 13 | } 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /Domain Models/Common/Common.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | netstandard2.0 5 | 8.0 6 | enable 7 | true 8 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /Domain Models/Common/IAggregateRoot.cs: -------------------------------------------------------------------------------- 1 | namespace Common 2 | { 3 | public interface IAggregateRoot 4 | { 5 | } 6 | } 7 | -------------------------------------------------------------------------------- /Domain Models/Common/IDomainEvent.cs: -------------------------------------------------------------------------------- 1 | namespace Common 2 | { 3 | public interface IDomainEvent 4 | { 5 | } 6 | } 7 | -------------------------------------------------------------------------------- /Domain Models/Common/IFactory.cs: -------------------------------------------------------------------------------- 1 | namespace Common 2 | { 3 | public interface IFactory 4 | where TEntity : IAggregateRoot 5 | { 6 | TEntity Build(); 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /Domain Models/Common/IInitialData.cs: -------------------------------------------------------------------------------- 1 | namespace Common 2 | { 3 | using System; 4 | using System.Collections.Generic; 5 | 6 | public interface IInitialData 7 | { 8 | Type EntityType { get; } 9 | 10 | IEnumerable GetData(); 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /Domain Models/Common/Models/IEntity.cs: -------------------------------------------------------------------------------- 1 | namespace Common.Models 2 | { 3 | using System.Collections.Generic; 4 | 5 | public interface IEntity 6 | { 7 | IReadOnlyCollection Events { get; } 8 | 9 | void ClearEvents(); 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /Domain Models/PetClinic/Application/AppointmentScheduling/IDoctorsRepository.cs: -------------------------------------------------------------------------------- 1 | namespace PetClinic.Application.AppointmentScheduling 2 | { 3 | using Models.AppointmentScheduling.Models.Schedule; 4 | 5 | public interface IDoctorsRepository 6 | { 7 | Doctor GetById(int id); 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /Domain Models/PetClinic/Application/ClientPatientManagement/IDoctorsRepository.cs: -------------------------------------------------------------------------------- 1 | namespace PetClinic.Application.ClientPatientManagement 2 | { 3 | using System.Collections.Generic; 4 | using Models.ClientPatientManagement.Models; 5 | 6 | public interface IDoctorsRepository 7 | { 8 | IEnumerable GetByName(string name); 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /Domain Models/PetClinic/Infrastructure/AppointmentScheduling/DoctorRepository.cs: -------------------------------------------------------------------------------- 1 | namespace PetClinic.Infrastructure.AppointmentScheduling 2 | { 3 | using System.Linq; 4 | using Application.AppointmentScheduling; 5 | using Models.AppointmentScheduling.Models.Schedule; 6 | using Persistence; 7 | 8 | public class DoctorRepository : IDoctorsRepository 9 | { 10 | private readonly DbContext data = new DbContext(); 11 | 12 | public Doctor GetById(int id) 13 | { 14 | var doctor = this.data.Doctors.FirstOrDefault(); 15 | 16 | return new Doctor(doctor.Name); 17 | } 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /Domain Models/PetClinic/Infrastructure/ClientPatientManagement/DoctorsRepository.cs: -------------------------------------------------------------------------------- 1 | namespace PetClinic.Infrastructure.ClientPatientManagement 2 | { 3 | using System.Collections.Generic; 4 | using System.Linq; 5 | using Application.ClientPatientManagement; 6 | using Models.ClientPatientManagement.Models; 7 | using Persistence; 8 | 9 | public class DoctorsRepository : IDoctorsRepository 10 | { 11 | private readonly DbContext data = new DbContext(); 12 | 13 | public IEnumerable GetByName(string name) 14 | { 15 | return this.data 16 | .Doctors 17 | .Where(d => d.Name == name) 18 | .ToList() 19 | .Select(d => new Doctor(d.Name, d.Specialty)); 20 | } 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /Domain Models/PetClinic/Infrastructure/Persistence/DbContext.cs: -------------------------------------------------------------------------------- 1 | namespace PetClinic.Infrastructure.Persistence 2 | { 3 | using System.Collections.Generic; 4 | using Models; 5 | 6 | public class DbContext 7 | { 8 | public HashSet Doctors { get; set; } = default!; 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /Domain Models/PetClinic/Infrastructure/Persistence/Models/DoctorData.cs: -------------------------------------------------------------------------------- 1 |  2 | namespace PetClinic.Infrastructure.Persistence.Models 3 | { 4 | using System.Collections.Generic; 5 | using PetClinic.Models.ClientPatientManagement.Models; 6 | 7 | public class DoctorData 8 | { 9 | internal DoctorData(string name) 10 | { 11 | this.Name = name; 12 | 13 | this.Clients = new List(); 14 | } 15 | 16 | public string Name { get; private set; } 17 | 18 | public Specialty Specialty { get; private set; } 19 | 20 | public List Clients { get; private set; } 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /Domain Models/PetClinic/Models/AppointmentScheduling/Events/AppointmentConfirmedEvent.cs: -------------------------------------------------------------------------------- 1 | namespace PetClinic.Models.AppointmentScheduling.Events 2 | { 3 | using System; 4 | using Common; 5 | using Models.Schedule; 6 | 7 | public class AppointmentConfirmedEvent : IDomainEvent 8 | { 9 | internal AppointmentConfirmedEvent(Appointment appointment) 10 | : this() 11 | => this.AppointmentUpdated = appointment; 12 | 13 | internal AppointmentConfirmedEvent() 14 | { 15 | this.Id = Guid.NewGuid(); 16 | 17 | this.DateTimeEventOccurred = DateTime.UtcNow; 18 | } 19 | 20 | public Guid Id { get; } 21 | 22 | public DateTime DateTimeEventOccurred { get; } 23 | 24 | public Appointment? AppointmentUpdated { get; } 25 | } 26 | } -------------------------------------------------------------------------------- /Domain Models/PetClinic/Models/AppointmentScheduling/Events/AppointmentConflictingEvent.cs: -------------------------------------------------------------------------------- 1 | namespace PetClinic.Models.AppointmentScheduling.Events 2 | { 3 | using System; 4 | using System.Collections.Generic; 5 | using Common; 6 | 7 | public class AppointmentConflictingEvent : IDomainEvent 8 | { 9 | internal AppointmentConflictingEvent(List conflictingAppointments) 10 | { 11 | this.Id = Guid.NewGuid(); 12 | 13 | this.DateTimeEventOccurred = DateTime.UtcNow; 14 | 15 | this.ConflictingAppointments = conflictingAppointments.AsReadOnly(); 16 | } 17 | 18 | public Guid Id { get; } 19 | 20 | public DateTime DateTimeEventOccurred { get; } 21 | 22 | public IReadOnlyList ConflictingAppointments { get; } 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /Domain Models/PetClinic/Models/AppointmentScheduling/Events/AppointmentScheduledEvent.cs: -------------------------------------------------------------------------------- 1 | namespace PetClinic.Models.AppointmentScheduling.Events 2 | { 3 | using System; 4 | using Common; 5 | using Models.Schedule; 6 | 7 | public class AppointmentScheduledEvent : IDomainEvent 8 | { 9 | internal AppointmentScheduledEvent(Appointment appointment) 10 | : this() 11 | => this.AppointmentScheduled = appointment; 12 | 13 | internal AppointmentScheduledEvent() 14 | { 15 | this.Id = Guid.NewGuid(); 16 | 17 | this.DateTimeEventOccurred = DateTime.UtcNow; 18 | } 19 | 20 | public Guid Id { get; } 21 | 22 | public DateTime DateTimeEventOccurred { get; } 23 | 24 | public Appointment? AppointmentScheduled { get; } 25 | } 26 | } -------------------------------------------------------------------------------- /Domain Models/PetClinic/Models/AppointmentScheduling/Events/AppointmentUpdatedEvent.cs: -------------------------------------------------------------------------------- 1 | namespace PetClinic.Models.AppointmentScheduling.Events 2 | { 3 | using System; 4 | using Common; 5 | using Models.Schedule; 6 | 7 | public class AppointmentUpdatedEvent : IDomainEvent 8 | { 9 | internal AppointmentUpdatedEvent(Appointment appointment) 10 | : this() 11 | => this.AppointmentUpdated = appointment; 12 | 13 | internal AppointmentUpdatedEvent() 14 | { 15 | this.Id = Guid.NewGuid(); 16 | 17 | this.DateTimeEventOccurred = DateTime.UtcNow; 18 | } 19 | 20 | public Guid Id { get; } 21 | 22 | public DateTime DateTimeEventOccurred { get; } 23 | 24 | public Appointment? AppointmentUpdated { get; } 25 | } 26 | } -------------------------------------------------------------------------------- /Domain Models/PetClinic/Models/AppointmentScheduling/Models/Schedule/AppointmentDetails.cs: -------------------------------------------------------------------------------- 1 | namespace PetClinic.Models.AppointmentScheduling.Models.Schedule 2 | { 3 | using Common.Models; 4 | 5 | public class AppointmentDetails : ValueObject 6 | { 7 | internal AppointmentDetails(string name, string code, int duration) 8 | { 9 | // Validate data. 10 | 11 | this.Name = name; 12 | this.Code = code; 13 | this.Duration = duration; 14 | } 15 | 16 | public string Name { get; private set; } 17 | 18 | public string Code { get; private set; } 19 | 20 | public int Duration { get; private set; } 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /Domain Models/PetClinic/Models/AppointmentScheduling/Models/Schedule/Client.cs: -------------------------------------------------------------------------------- 1 | namespace PetClinic.Models.AppointmentScheduling.Models.Schedule 2 | { 3 | using System.Collections.Generic; 4 | using Common.Models; 5 | 6 | public class Client : Entity 7 | { 8 | internal Client(string fullName) 9 | { 10 | this.FullName = fullName; 11 | 12 | this.Patients = new List(); 13 | } 14 | 15 | public string FullName { get; private set; } 16 | 17 | public IList Patients { get; private set; } 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /Domain Models/PetClinic/Models/AppointmentScheduling/Models/Schedule/Doctor.cs: -------------------------------------------------------------------------------- 1 | namespace PetClinic.Models.AppointmentScheduling.Models.Schedule 2 | { 3 | using Common.Models; 4 | 5 | public class Doctor : Entity 6 | { 7 | internal Doctor(string name) 8 | { 9 | // Validate name. 10 | 11 | this.Name = name; 12 | } 13 | 14 | public string Name { get; private set; } 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /Domain Models/PetClinic/Models/AppointmentScheduling/Models/Schedule/Room.cs: -------------------------------------------------------------------------------- 1 | namespace PetClinic.Models.AppointmentScheduling.Models.Schedule 2 | { 3 | using Common.Models; 4 | 5 | public class Room : Entity 6 | { 7 | internal Room(string name) 8 | { 9 | // Validate name. 10 | 11 | this.Name = name; 12 | } 13 | 14 | public string Name { get; private set; } 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /Domain Models/PetClinic/Models/ClientPatientManagement/Models/Doctor.cs: -------------------------------------------------------------------------------- 1 | namespace PetClinic.Models.ClientPatientManagement.Models 2 | { 3 | using Common.Models; 4 | 5 | public class Doctor : Entity 6 | { 7 | internal Doctor(string name, Specialty specialty) 8 | { 9 | // Validate data. 10 | 11 | this.Name = name; 12 | this.Specialty = specialty; 13 | } 14 | 15 | public string Name { get; private set; } 16 | 17 | public Specialty Specialty { get; private set; } 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /Domain Models/PetClinic/Models/ClientPatientManagement/Models/Patient.cs: -------------------------------------------------------------------------------- 1 | namespace PetClinic.Models.ClientPatientManagement.Models 2 | { 3 | using Common.Models; 4 | using Shared; 5 | 6 | public class Patient : Entity 7 | { 8 | internal Patient(Client owner, string name, Gender gender) 9 | { 10 | // Validate data. 11 | 12 | this.Owner = owner; 13 | this.Name = name; 14 | this.Gender = gender; 15 | } 16 | 17 | public Client Owner { get; private set; } 18 | 19 | public string Name { get; private set; } 20 | 21 | public Gender Gender { get; private set; } 22 | } 23 | } -------------------------------------------------------------------------------- /Domain Models/PetClinic/Models/ClientPatientManagement/Models/Room.cs: -------------------------------------------------------------------------------- 1 | namespace PetClinic.Models.ClientPatientManagement.Models 2 | { 3 | using Common.Models; 4 | 5 | public class Room : Entity 6 | { 7 | internal Room(string name, bool isLab) 8 | { 9 | // Validate name. 10 | 11 | this.Name = name; 12 | this.IsLab = isLab; 13 | } 14 | 15 | public string Name { get; private set; } 16 | 17 | public bool IsLab { get; private set; } 18 | } 19 | } -------------------------------------------------------------------------------- /Domain Models/PetClinic/Models/ClientPatientManagement/Models/Specialty.cs: -------------------------------------------------------------------------------- 1 | namespace PetClinic.Models.ClientPatientManagement.Models 2 | { 3 | public enum Specialty 4 | { 5 | Cats = 1, 6 | Dogs = 2 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /Domain Models/PetClinic/Models/MedicalRecords/Models/Patient.cs: -------------------------------------------------------------------------------- 1 | namespace PetClinic.Models.MedicalRecords.Models 2 | { 3 | public class Patient 4 | { 5 | } 6 | } 7 | -------------------------------------------------------------------------------- /Domain Models/PetClinic/Models/Shared/AnimalType.cs: -------------------------------------------------------------------------------- 1 | namespace PetClinic.Models.Shared 2 | { 3 | using Common.Models; 4 | 5 | public class AnimalType : ValueObject 6 | { 7 | internal AnimalType(string species, string breed) 8 | { 9 | // Validate data. 10 | 11 | this.Species = species; 12 | this.Breed = breed; 13 | } 14 | 15 | public string Species { get; private set; } 16 | 17 | public string Breed { get; private set; } 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /Domain Models/PetClinic/Models/Shared/Gender.cs: -------------------------------------------------------------------------------- 1 | namespace PetClinic.Models.Shared 2 | { 3 | public enum Gender 4 | { 5 | Female = 1, 6 | Male = 2 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /Domain Models/PetClinic/PetClinic.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | netstandard2.0 5 | 8.0 6 | enable 7 | true 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | -------------------------------------------------------------------------------- /Domain Models/SnackMachine/SnackMachine.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | netstandard2.0 5 | 8.0 6 | enable 7 | true 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | -------------------------------------------------------------------------------- /Domain Models/SnackMachine/SnackMachines/Slot.cs: -------------------------------------------------------------------------------- 1 | namespace SnackMachine.SnackMachines 2 | { 3 | using Common.Models; 4 | 5 | public class Slot : Entity 6 | { 7 | internal Slot(int position) 8 | { 9 | // Validate position logic. 10 | 11 | this.Position = position; 12 | 13 | this.SnackPile = SnackPile.Empty; 14 | } 15 | 16 | public SnackPile SnackPile { get; private set; } 17 | 18 | public int Position { get; private set; } 19 | 20 | public void LoadSnackPile(SnackPile snackPile) 21 | => this.SnackPile = snackPile; 22 | 23 | public void RemoveOne() 24 | => this.SnackPile = this.SnackPile.RemoveOne(); 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /Domain Models/SnackMachine/Snacks/Snack.cs: -------------------------------------------------------------------------------- 1 | namespace SnackMachine.Snacks 2 | { 3 | using Common; 4 | using Common.Models; 5 | 6 | public class Snack : Entity, IAggregateRoot 7 | { 8 | public static readonly Snack None = new Snack("None"); 9 | 10 | internal Snack(string name) 11 | { 12 | this.Name = name; 13 | } 14 | 15 | public string Name { get; private set; } 16 | } 17 | } 18 | --------------------------------------------------------------------------------