├── .env ├── deployments └── docker-compose │ ├── .env.dev.sample │ ├── .env.prod.sample │ └── .env.prod ├── src ├── BuildingBlocks │ ├── BuildingBlocks.Core │ │ ├── readme.md │ │ ├── Messaging │ │ │ ├── IntegrationEvent.cs │ │ │ ├── MessagePersistence │ │ │ │ └── MessagePersistenceOptions.cs │ │ │ ├── Message.cs │ │ │ └── MessageHeaders.cs │ │ ├── CQRS │ │ │ ├── Event │ │ │ │ ├── Internal │ │ │ │ │ ├── NotificationEvent.cs │ │ │ │ │ ├── DomainNotificationEvent.cs │ │ │ │ │ ├── DomainNotificationEventWrapper.cs │ │ │ │ │ ├── IntegrationEventWrapper.cs │ │ │ │ │ └── DomainEvent.cs │ │ │ │ ├── NullDomainEventsAccessor.cs │ │ │ │ └── Event.cs │ │ │ ├── Command │ │ │ │ ├── TxInternalCommand.cs │ │ │ │ ├── InternalCommand.cs │ │ │ │ └── NullCommandScheduler.cs │ │ │ └── Query │ │ │ │ ├── PageRequest.cs │ │ │ │ ├── ListQuery.cs │ │ │ │ └── QueryHandler.cs │ │ ├── IdsGenerator │ │ │ └── GuidIdGenerator.cs │ │ ├── Persistence │ │ │ └── EventStore │ │ │ │ ├── StreamEventMetadata.cs │ │ │ │ ├── StreamEvent.cs │ │ │ │ └── StreamEventExtensions.cs │ │ ├── Domain │ │ │ ├── Exceptions │ │ │ │ ├── ConcurrencyException.cs │ │ │ │ ├── DomainException.cs │ │ │ │ └── BusinessRuleValidationException.cs │ │ │ ├── AuditableEntity.cs │ │ │ ├── AuditAggregate.cs │ │ │ └── Entity.cs │ │ ├── Exception │ │ │ └── Types │ │ │ │ ├── ConflictException.cs │ │ │ │ ├── NotFoundException.cs │ │ │ │ ├── BadRequestException.cs │ │ │ │ ├── AppException.cs │ │ │ │ ├── InvalidDateException.cs │ │ │ │ ├── InvalidEmailException.cs │ │ │ │ ├── ApiException.cs │ │ │ │ ├── InvalidAmountException.cs │ │ │ │ ├── InvalidAddressException.cs │ │ │ │ ├── InvalidCurrencyException.cs │ │ │ │ ├── IdentityException.cs │ │ │ │ ├── InvalidPhoneNumberException.cs │ │ │ │ └── CustomException.cs │ │ ├── Caching │ │ │ ├── CacheOptions.cs │ │ │ └── CacheKey.cs │ │ ├── Extensions │ │ │ ├── ClaimsPrincipalExtensions.cs │ │ │ └── ObjectExtensions.cs │ │ ├── Web │ │ │ └── AppOptions.cs │ │ ├── Utils │ │ │ └── IpUtilities.cs │ │ └── Types │ │ │ └── MachineInstanceInfo.cs │ ├── BuildingBlocks.Web │ │ ├── readme.md │ │ ├── Constants.cs │ │ ├── Module │ │ │ ├── ModuleHook.cs │ │ │ └── CompositionRoot.cs │ │ ├── Plugin │ │ │ ├── WebApplicationPlugin.cs │ │ │ └── WebApplicationPluginAttribute.cs │ │ ├── Extensions │ │ │ └── ServiceCollectionExtensions │ │ │ │ └── ServiceCollectionExtensions.Storage.cs │ │ └── SlugifyParameterTransformer.cs │ ├── BuildingBlocks.Email │ │ ├── readme.md │ │ ├── EmailProvider.cs │ │ ├── BuildingBlocks.Email.csproj │ │ └── Options │ │ │ └── EmailOptions.cs │ ├── BuildingBlocks.Logging │ │ ├── readme.md │ │ ├── LoggerOptions.cs │ │ └── BaggageEnricher.cs │ ├── BuildingBlocks.Security │ │ ├── readme.md │ │ ├── ApiKey │ │ │ ├── ApiKeyConstants.cs │ │ │ ├── IGetApiKeyQuery.cs │ │ │ ├── Authorization │ │ │ │ ├── OnlyAdminsRequirement.cs │ │ │ │ ├── OnlyCustomersRequirement.cs │ │ │ │ ├── OnlyThirdPartiesRequirement.cs │ │ │ │ ├── Roles.cs │ │ │ │ ├── Policies.cs │ │ │ │ ├── OnlyAdminsAuthorizationHandler.cs │ │ │ │ ├── OnlyCustomersAuthorizationHandler.cs │ │ │ │ └── OnlyThirdPartiesAuthorizationHandler.cs │ │ │ ├── ApiKeyAuthenticationOptions.cs │ │ │ └── AuthenticationBuilderExtensions.cs │ │ ├── Jwt │ │ │ ├── ISecurityContextAccessor.cs │ │ │ ├── CustomClaims.cs │ │ │ ├── RolePolicy.cs │ │ │ ├── ClaimPolicy.cs │ │ │ ├── JwtOptions.cs │ │ │ ├── readme.md │ │ │ └── IJwtService.cs │ │ ├── ForbiddenProblemDetails.cs │ │ ├── UnauthorizedProblemDetails.cs │ │ └── BuildingBlocks.Security.csproj │ ├── BuildingBlocks.Swagger │ │ ├── readme.md │ │ ├── SwaggerOptions.cs │ │ └── Constants.cs │ ├── BuildingBlocks.Abstractions │ │ ├── readme.md │ │ ├── Domain │ │ │ ├── IHaveSoftDelete.cs │ │ │ ├── IBusinessRule.cs │ │ │ ├── IHaveCreator.cs │ │ │ ├── IHaveAudit.cs │ │ │ ├── IHaveIdentity.cs │ │ │ ├── IEntity.cs │ │ │ ├── IAggregate.cs │ │ │ ├── IAuditableEntity.cs │ │ │ ├── IIdentity.cs │ │ │ ├── IHaveAggregateVersion.cs │ │ │ └── EventSourcing │ │ │ │ ├── IHaveEventSourcedAggregateVersion.cs │ │ │ │ └── IEventSourcedAggregate.cs │ │ ├── Messaging │ │ │ ├── IHaveExternalEvent.cs │ │ │ ├── IConsumeConfiguratorBuilder.cs │ │ │ ├── PersistMessage │ │ │ │ ├── MessageStatus.cs │ │ │ │ └── MessageDeliveryType.cs │ │ │ ├── IIntegrationEvent.cs │ │ │ ├── IMessage.cs │ │ │ ├── IBus.cs │ │ │ ├── IIntegrationEventHandler.cs │ │ │ ├── IMessageHandler.cs │ │ │ ├── MessageHandlerDelegate.cs │ │ │ ├── ContextItems.cs │ │ │ ├── Acknowledgement.cs │ │ │ └── Context │ │ │ │ └── IConsumeContext.cs │ │ ├── Core │ │ │ ├── IIdGenerator.cs │ │ │ └── IExclusiveLock.cs │ │ ├── CQRS │ │ │ ├── FilterModel.cs │ │ │ ├── Event │ │ │ │ ├── Internal │ │ │ │ │ ├── IHaveNotificationEvent.cs │ │ │ │ │ ├── IDomainEventHandler.cs │ │ │ │ │ ├── IDomainNotificationEventHandler.cs │ │ │ │ │ ├── IDomainEventContext.cs │ │ │ │ │ ├── IDomainEventPublisher.cs │ │ │ │ │ ├── IDomainNotificationEvent.cs │ │ │ │ │ ├── IDomainNotificationEventPublisher.cs │ │ │ │ │ └── IDomainEvent.cs │ │ │ │ ├── IEventHandler.cs │ │ │ │ ├── IDomainEventsAccessor.cs │ │ │ │ └── IAggregatesDomainEventsRequestStore.cs │ │ │ ├── Query │ │ │ │ ├── IListQuery.cs │ │ │ │ ├── IItemQuery.cs │ │ │ │ ├── IPageRequest.cs │ │ │ │ ├── IQuery.cs │ │ │ │ ├── IQueryProcessor.cs │ │ │ │ └── IQueryHandler.cs │ │ │ └── Command │ │ │ │ ├── ITxInternalCommand.cs │ │ │ │ ├── IInternalCommand.cs │ │ │ │ ├── ICommand.cs │ │ │ │ ├── ICreateCommand.cs │ │ │ │ ├── IUpdateCommand.cs │ │ │ │ ├── ITxCommand.cs │ │ │ │ ├── ITxCreateCommand.cs │ │ │ │ ├── ITxUpdateCommand.cs │ │ │ │ ├── IDeleteCommand.cs │ │ │ │ ├── ICommandHandler.cs │ │ │ │ └── ICommandProcessor.cs │ │ ├── Persistence │ │ │ ├── IDataSeeder.cs │ │ │ ├── Mongo │ │ │ │ ├── IMongoUnitOfWork.cs │ │ │ │ ├── IMongoRepository.cs │ │ │ │ └── IMongoDbContext.cs │ │ │ ├── ITxRequest.cs │ │ │ ├── IDbFacadeResolver.cs │ │ │ ├── EfCore │ │ │ │ ├── IConnectionFactory.cs │ │ │ │ ├── IRetryDbContextExecution.cs │ │ │ │ └── IPageRepository.cs │ │ │ ├── EventStore │ │ │ │ ├── AppendResult.cs │ │ │ │ ├── IStreamEventMetadata.cs │ │ │ │ ├── Projections │ │ │ │ │ ├── IHaveReadProjection.cs │ │ │ │ │ └── IReadProjectionPublisher.cs │ │ │ │ ├── ExpectedStreamVersion.cs │ │ │ │ └── IStreamEvent.cs │ │ │ ├── ITxDbContextExecution.cs │ │ │ ├── ITransactionAble.cs │ │ │ └── IUnitOfWork.cs │ │ ├── Types │ │ │ └── IMachineInstanceInfo.cs │ │ ├── Web │ │ │ ├── Storage │ │ │ │ └── IRequestStorage.cs │ │ │ ├── Module │ │ │ │ └── ICompositionRoot.cs │ │ │ └── MinimalApi │ │ │ │ └── IMinimalEndpointDefinition.cs │ │ ├── Scheduling │ │ │ ├── IScheduleExecutor.cs │ │ │ ├── ICommandScheduler.cs │ │ │ └── IScheduler.cs │ │ └── Caching │ │ │ ├── ICacheProvider.cs │ │ │ └── IInvalidateCachePolicy.cs │ ├── BuildingBlocks.Monitoring │ │ └── readme.md │ ├── BuildingBlocks.Resiliency │ │ ├── readme.md │ │ ├── Retry │ │ │ ├── IRetryPolicyOptions.cs │ │ │ ├── IRetryableRequest.cs │ │ │ └── HttpPolicyBuilders.cs │ │ ├── Timeout │ │ │ └── ITimeoutPolicyOptions.cs │ │ ├── CircuitBreaker │ │ │ └── ICircuitBreakerPolicyOptions.cs │ │ ├── BuildingBlocks.Resiliency.csproj │ │ ├── Fallback │ │ │ └── IFallbackHandler.cs │ │ ├── PolicyOptions.cs │ │ └── Extensions │ │ │ └── HttpClientBuilderExtensions.cs │ ├── BuildingBlocks.Validation │ │ ├── readme.md │ │ ├── ValidationException.cs │ │ ├── ValidationError.cs │ │ └── BuildingBlocks.Validation.csproj │ ├── BuildingBlocks.Caching.InMemory │ │ ├── readme.md │ │ ├── InMemoryCacheOptions.cs │ │ └── BuildingBlocks.Caching.InMemory.csproj │ ├── BuildingBlocks.Persistence.Mongo │ │ ├── readme.md │ │ ├── MongoOptions.cs │ │ └── BuildingBlocks.Persistence.Mongo.csproj │ ├── BuildingBlocks.Persistence.EfCore.Postgres │ │ ├── readme.md │ │ ├── PostgresOptions.cs │ │ └── BuildingBlocks.Persistence.EfCore.Postgres.csproj │ └── BuildingBlocks.Integration.Hangfire │ │ ├── HangfireOptions.cs │ │ └── BuildingBlocks.Integration.Hangfire.csproj ├── Directory.Build.targets ├── Api │ └── FoodDelivery.Api │ │ ├── ApiRoot.cs │ │ ├── ApiConstants.cs │ │ ├── Extensions │ │ ├── ApplicationBuilderExtensions │ │ │ ├── ApplicationBuilderExtensions.Monitoring.cs │ │ │ └── ApplicationBuilderExtensions.Cors.cs │ │ └── ServiceCollectionExtensions │ │ │ └── ServiceCollectionExtensions.Options.cs │ │ ├── FoodDelivery.Api.csproj │ │ ├── appsettings.json │ │ ├── appsettings.test.json │ │ └── appsettings.docker.json ├── Modules │ ├── Orders │ │ └── FoodDelivery.Modules.Orders │ │ │ ├── OrdersRoot.cs │ │ │ ├── OrdersConstants.cs │ │ │ ├── readme.md │ │ │ ├── Orders │ │ │ ├── Models │ │ │ │ ├── Reads │ │ │ │ │ └── OrderReadModel.cs │ │ │ │ └── Order.cs │ │ │ └── ValueObjects │ │ │ │ ├── OrderId.cs │ │ │ │ └── CustomerInfo.cs │ │ │ └── Shared │ │ │ ├── Contracts │ │ │ └── IOrdersDbContext.cs │ │ │ └── Data │ │ │ ├── OrdersDbContextDesignFactory.cs │ │ │ └── OrderReadDbContext.cs │ ├── Catalogs │ │ ├── FoodDelivery.Modules.Catalogs │ │ │ ├── CatalogRoot.cs │ │ │ ├── CatalogConstants.cs │ │ │ ├── Products │ │ │ │ ├── Models │ │ │ │ │ ├── Read │ │ │ │ │ │ └── ProductReadModel.cs │ │ │ │ │ ├── ProductStatus.cs │ │ │ │ │ ├── ProductColor.cs │ │ │ │ │ └── ProductView.cs │ │ │ │ ├── Features │ │ │ │ │ ├── ChangingProductBrand │ │ │ │ │ │ ├── ChangeProductBrandResponse.cs │ │ │ │ │ │ ├── Events │ │ │ │ │ │ │ └── Domain │ │ │ │ │ │ │ │ └── ProductBrandChanged.cs │ │ │ │ │ │ └── ChangeProductBrand.cs │ │ │ │ │ ├── ChangingProductCategory │ │ │ │ │ │ ├── ChangeProductCategoryResponse.cs │ │ │ │ │ │ └── Events │ │ │ │ │ │ │ ├── ProductCategoryChanged.cs │ │ │ │ │ │ │ └── ProductCategoryChangedNotification.cs │ │ │ │ │ ├── ChangingProductSupplier │ │ │ │ │ │ ├── ChangeProductSupplierResponse.cs │ │ │ │ │ │ └── Events │ │ │ │ │ │ │ └── ProductSupplierChanged.cs │ │ │ │ │ ├── GettingProductsView │ │ │ │ │ │ ├── GetProductsViewResponse.cs │ │ │ │ │ │ ├── ProductViewDto.cs │ │ │ │ │ │ └── GetProductsViewRequest.cs │ │ │ │ │ ├── CreatingProduct │ │ │ │ │ │ ├── CreateProductResponse.cs │ │ │ │ │ │ ├── Requests │ │ │ │ │ │ │ └── CreateProductImageRequest.cs │ │ │ │ │ │ └── Events │ │ │ │ │ │ │ └── Integration │ │ │ │ │ │ │ └── ProductCreated.cs │ │ │ │ │ ├── ChangingMaxThreshold │ │ │ │ │ │ ├── ChangeMaxThreshold.cs │ │ │ │ │ │ └── MaxThresholdChanged.cs │ │ │ │ │ ├── GettingAvailableStockById │ │ │ │ │ │ └── GetAvailableStockById.cs │ │ │ │ │ ├── ChangingRestockThreshold │ │ │ │ │ │ ├── ChangeRestockThreshold.cs │ │ │ │ │ │ └── RestockThresholdChanged.cs │ │ │ │ │ ├── GettingProducts │ │ │ │ │ │ └── GetProductsResponse.cs │ │ │ │ │ ├── ChangingProductPrice │ │ │ │ │ │ └── ProductPriceChanged.cs │ │ │ │ │ ├── DebitingProductStock │ │ │ │ │ │ └── Events │ │ │ │ │ │ │ ├── Integration │ │ │ │ │ │ │ └── ProductStockDebited.cs │ │ │ │ │ │ │ └── Domain │ │ │ │ │ │ │ └── ProductStockDebited.cs │ │ │ │ │ └── ReplenishingProductStock │ │ │ │ │ │ └── Events │ │ │ │ │ │ ├── Integration │ │ │ │ │ │ └── ProductStockReplenished.cs │ │ │ │ │ │ └── Domain │ │ │ │ │ │ └── ProductStockReplenished.cs │ │ │ │ ├── Dtos │ │ │ │ │ └── ProductImageDto.cs │ │ │ │ ├── Exceptions │ │ │ │ │ ├── Domain │ │ │ │ │ │ ├── ProductDomainException.cs │ │ │ │ │ │ ├── InsufficientStockException.cs │ │ │ │ │ │ └── MaxStockThresholdReachedException.cs │ │ │ │ │ └── Application │ │ │ │ │ │ └── ProductNotFoundException.cs │ │ │ │ ├── GuardExtensions.cs │ │ │ │ └── ValueObjects │ │ │ │ │ └── ProductId.cs │ │ │ ├── readme.md │ │ │ ├── CatalogCacheKey.cs │ │ │ ├── Brands │ │ │ │ ├── Exceptions │ │ │ │ │ ├── Domain │ │ │ │ │ │ └── BrandDomainException.cs │ │ │ │ │ └── Application │ │ │ │ │ │ └── BrandNotFoundException.cs │ │ │ │ ├── BrandId.cs │ │ │ │ ├── GuardExtensions.cs │ │ │ │ └── Configs.cs │ │ │ ├── Suppliers │ │ │ │ ├── Exceptions │ │ │ │ │ ├── Domain │ │ │ │ │ │ └── SupplierDomainException.cs │ │ │ │ │ └── Application │ │ │ │ │ │ └── SupplierNotFoundException.cs │ │ │ │ ├── Supplier.cs │ │ │ │ ├── SupplierId.cs │ │ │ │ ├── GuardExtensions.cs │ │ │ │ ├── Features │ │ │ │ │ ├── SupplierDeleted │ │ │ │ │ │ └── Events │ │ │ │ │ │ │ └── External │ │ │ │ │ │ │ └── SupplierDeleted.cs │ │ │ │ │ ├── SupplierCreated │ │ │ │ │ │ └── Events │ │ │ │ │ │ │ └── External │ │ │ │ │ │ │ └── SupplierCreated.cs │ │ │ │ │ └── SupplierUpdated │ │ │ │ │ │ └── Events │ │ │ │ │ │ └── External │ │ │ │ │ │ └── SupplierUpdated.cs │ │ │ │ └── Configs.cs │ │ │ ├── Shared │ │ │ │ ├── Extensions │ │ │ │ │ └── ApplicationBuilderExtensions │ │ │ │ │ │ └── Infrastructure.cs │ │ │ │ └── Data │ │ │ │ │ ├── DbContextDesignFactory.cs │ │ │ │ │ └── CatalogReadDbContext.cs │ │ │ └── Categories │ │ │ │ ├── CategoryId.cs │ │ │ │ ├── Exceptions │ │ │ │ ├── Domain │ │ │ │ │ └── CategoryDomainException.cs │ │ │ │ └── Application │ │ │ │ │ └── CategoryNotFoundException.cs │ │ │ │ ├── GuardExtensions.cs │ │ │ │ └── Configs.cs │ │ └── migrations.bat │ ├── Identity │ │ ├── FoodDelivery.Modules.Identity │ │ │ ├── IdentityRoot.cs │ │ │ ├── Shared │ │ │ │ ├── Models │ │ │ │ │ ├── UserState.cs │ │ │ │ │ ├── ApplicationUserRole.cs │ │ │ │ │ ├── PasswordResetCode.cs │ │ │ │ │ └── EmailVerificationCode.cs │ │ │ │ ├── Data │ │ │ │ │ └── DbContextDesignFactory.cs │ │ │ │ └── Exceptions │ │ │ │ │ └── UserNotFoundException.cs │ │ │ ├── Identity │ │ │ │ ├── Features │ │ │ │ │ ├── GetClaims │ │ │ │ │ │ ├── GetClaimsResponse.cs │ │ │ │ │ │ └── ClaimDto.cs │ │ │ │ │ ├── Login │ │ │ │ │ │ ├── LoginUserRequest.cs │ │ │ │ │ │ └── LoginFailedException.cs │ │ │ │ │ ├── RevokeRefreshToken │ │ │ │ │ │ ├── RevokeRefreshTokenResponse.cs │ │ │ │ │ │ └── RevokeRefreshTokenRequest.cs │ │ │ │ │ ├── RefreshingToken │ │ │ │ │ │ ├── RefreshTokenRequest.cs │ │ │ │ │ │ └── InvalidRefreshTokenException.cs │ │ │ │ │ ├── ResetPassword │ │ │ │ │ │ └── ResetPassword.cs │ │ │ │ │ ├── VerifyEmail │ │ │ │ │ │ ├── VerifyEmailRequest.cs │ │ │ │ │ │ └── Exceptions │ │ │ │ │ │ │ ├── EmailAlreadyVerifiedException.cs │ │ │ │ │ │ │ └── VerificationTokenIsInvalidException.cs │ │ │ │ │ ├── SendEmailVerificationCode │ │ │ │ │ │ └── SendEmailVerificationCodeRequest.cs │ │ │ │ │ ├── SendResetPasswordCode │ │ │ │ │ │ └── SendResetPasswordCode.cs │ │ │ │ │ └── GenerateRefreshToken │ │ │ │ │ │ └── GenerateRefreshTokenResponse.cs │ │ │ │ ├── Exceptions │ │ │ │ │ ├── PasswordIsInvalidException.cs │ │ │ │ │ ├── RequiresTwoFactorException.cs │ │ │ │ │ ├── UserLockedException.cs │ │ │ │ │ ├── InvalidTokenException.cs │ │ │ │ │ ├── PhoneNumberNotConfirmedException.cs │ │ │ │ │ ├── RefreshTokenNotFoundException.cs │ │ │ │ │ └── EmailNotConfirmedException.cs │ │ │ │ └── Dtos │ │ │ │ │ └── RefreshTokenDto.cs │ │ │ ├── readme.md │ │ │ ├── Users │ │ │ │ └── Features │ │ │ │ │ ├── GettingUserById │ │ │ │ │ └── UserByIdResponse.cs │ │ │ │ │ ├── RegisteringUser │ │ │ │ │ ├── RegisterUserResponse.cs │ │ │ │ │ ├── RegisterIdentityUserException.cs │ │ │ │ │ ├── Events │ │ │ │ │ │ └── Integration │ │ │ │ │ │ │ └── UserRegistered.cs │ │ │ │ │ └── RegisterUserRequest.cs │ │ │ │ │ ├── GettingUerByEmail │ │ │ │ │ └── GetUserByEmailResponse.cs │ │ │ │ │ ├── UpdatingUserState │ │ │ │ │ ├── UpdateUserStateRequest.cs │ │ │ │ │ └── Events │ │ │ │ │ │ └── Integration │ │ │ │ │ │ └── UserStateUpdated.cs │ │ │ │ │ └── GettingUsers │ │ │ │ │ └── GetUsersResponse.cs │ │ │ └── Constants.cs │ │ └── migrations.bat │ └── Customers │ │ ├── FoodDelivery.Modules.Customers │ │ ├── CustomersRoot.cs │ │ ├── Customers │ │ │ ├── Dtos │ │ │ │ └── CustomerDto.cs │ │ │ ├── Extensions │ │ │ │ └── GuardExtensions.cs │ │ │ ├── Models │ │ │ │ └── UserState.cs │ │ │ ├── Features │ │ │ │ ├── CreatingCustomer │ │ │ │ │ ├── CreateCustomerRequest.cs │ │ │ │ │ ├── Events │ │ │ │ │ │ └── Integration │ │ │ │ │ │ │ └── CustomerCreated.cs │ │ │ │ │ └── CreateCustomerResponse.cs │ │ │ │ ├── GettingCustomerById │ │ │ │ │ └── GetCustomerByIdResponse.cs │ │ │ │ └── GettingCustomers │ │ │ │ │ └── GetCustomersResponse.cs │ │ │ ├── Data │ │ │ │ └── CustomersDataSeeder.cs │ │ │ ├── Exceptions │ │ │ │ ├── Domain │ │ │ │ │ ├── InvalidNameException.cs │ │ │ │ │ ├── CustomerDomainException.cs │ │ │ │ │ ├── CustomerNotActiveException.cs │ │ │ │ │ ├── InvalidNationalityException.cs │ │ │ │ │ ├── UnsupportedNationalityException.cs │ │ │ │ │ ├── CustomerAlreadyVerifiedException.cs │ │ │ │ │ └── CustomerAlreadyCompletedException.cs │ │ │ │ └── Application │ │ │ │ │ └── CustomerNotFoundException.cs │ │ │ └── ValueObjects │ │ │ │ └── CustomerId.cs │ │ ├── Shared │ │ │ ├── Clients │ │ │ │ ├── Catalogs │ │ │ │ │ ├── Dtos │ │ │ │ │ │ ├── GetProductByIdResponse.cs │ │ │ │ │ │ └── ProductStatus.cs │ │ │ │ │ ├── CatalogsApiClientOptions.cs │ │ │ │ │ └── ICatalogApiClient.cs │ │ │ │ └── Identity │ │ │ │ │ ├── Dtos │ │ │ │ │ ├── CreateUserResponse.cs │ │ │ │ │ ├── GetUserByEmailResponse.cs │ │ │ │ │ ├── CreateUserRequest.cs │ │ │ │ │ └── UserIdentityDto.cs │ │ │ │ │ └── IdentityApiClientOptions.cs │ │ │ ├── Extensions │ │ │ │ └── ApplicationBuilderExtensions │ │ │ │ │ └── Infrastructure.cs │ │ │ ├── Data │ │ │ │ └── DbContextDesignFactory.cs │ │ │ └── Contracts │ │ │ │ └── ICustomersDbContext.cs │ │ ├── readme.md │ │ ├── CustomersConstants.cs │ │ ├── RestockSubscriptions │ │ │ ├── Features │ │ │ │ ├── CreatingRestockSubscription │ │ │ │ │ ├── CreateRestockSubscriptionRequest.cs │ │ │ │ │ ├── CreateRestockSubscriptionResponse.cs │ │ │ │ │ ├── Events │ │ │ │ │ │ └── Integration │ │ │ │ │ │ │ └── RestockSubscriptionCreated.cs │ │ │ │ │ └── Exceptions │ │ │ │ │ │ ├── ProductHaveStockException.cs │ │ │ │ │ │ └── ProductAlreadySubscribedException.cs │ │ │ │ ├── DeletingRestockSubscriptionsByTime │ │ │ │ │ └── DeleteRestockSubscriptionByTimeRequest.cs │ │ │ │ ├── GettingRestockSubscriptionById │ │ │ │ │ └── GetRestockSubscriptionByIdResponse.cs │ │ │ │ └── GettingRestockSubscriptions │ │ │ │ │ └── GetRestockSubscriptionsResponse.cs │ │ │ ├── Exceptions │ │ │ │ ├── Domain │ │ │ │ │ └── RestockSubscriptionDomainException.cs │ │ │ │ └── Application │ │ │ │ │ └── RestockSubscriptionNotFoundException.cs │ │ │ ├── Dtos │ │ │ │ ├── RestockSubscriptionReadDto.cs │ │ │ │ └── RestockSubscriptionDto.cs │ │ │ └── ValueObjects │ │ │ │ └── RestockSubscriptionId.cs │ │ └── Products │ │ │ ├── Exceptions │ │ │ └── ProductNotFoundException.cs │ │ │ └── ProductId.cs │ │ └── migrations.bat └── Packages.props ├── .devcontainer ├── scripts │ ├── update.sh │ ├── post-create.sh │ └── setup-fonts.sh └── Dockerfile ├── assets ├── vsa2.png ├── modules.png ├── catalog-module.png ├── level-structure.png ├── composition-roots.png └── vertical-slice-architecture.jpg ├── global.json ├── tests ├── building-blocks │ └── Core │ │ └── BuildingBlocks.Core.IntegrationTests │ │ └── xunit.runner.json ├── modules │ ├── Customers │ │ ├── FoodDelivery.Modules.Customers.UnitTests │ │ │ ├── xunit.runner.json │ │ │ └── FoodDelivery.Modules.Customers.UnitTests.csproj │ │ ├── FoodDelivery.Modules.Customers.EndToEndTests │ │ │ ├── xunit.runner.json │ │ │ └── FoodDelivery.Modules.Customers.EndToEndTests.csproj │ │ └── FoodDelivery.Modules.Customers.IntegrationTests │ │ │ ├── xunit.runner.json │ │ │ ├── FoodDelivery.Modules.Customers.IntegrationTests.csproj │ │ │ └── CustomerIntegrationTestBase.cs │ └── Identity │ │ ├── FoodDelivery.Modules.Identity.UnitTests │ │ ├── xunit.runner.json │ │ ├── UnitTest1.cs │ │ └── FoodDelivery.Modules.Identity.UnitTests.csproj │ │ ├── FoodDelivery.Modules.Identity.EndToEndTests │ │ ├── xunit.runner.json │ │ ├── UnitTest1.cs │ │ └── FoodDelivery.Modules.Identity.EndToEndTests.csproj │ │ └── FoodDelivery.Modules.Identity.IntegrationTests │ │ ├── xunit.runner.json │ │ └── FoodDelivery.Modules.Identity.IntegrationTests.csproj ├── shared │ └── Tests.Shared │ │ ├── Mocks │ │ ├── UserType.cs │ │ ├── LoginUserRequestMock.cs │ │ ├── LoginResponseMock.cs │ │ └── MockAuthUser.cs │ │ ├── Probing │ │ ├── AssertErrorException.cs │ │ ├── Timeout.cs │ │ └── IProbe.cs │ │ ├── OptionsHelper.cs │ │ ├── Fixtures │ │ └── DelegateHttpClientFactory.cs │ │ └── Extensions │ │ └── WebApplicationFactoryExtensions.cs └── Directory.Build.props ├── .csharpierrc.yaml ├── nuget.config ├── .dockerignore └── .github ├── multi-labeler.yml ├── workflows ├── lint-pr.yml ├── release-please.yml ├── release-drafter.yml └── multi-labeler.yml └── release.yml /.env: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /deployments/docker-compose/.env.dev.sample: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /deployments/docker-compose/.env.prod.sample: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/BuildingBlocks/BuildingBlocks.Core/readme.md: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/BuildingBlocks/BuildingBlocks.Web/readme.md: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/BuildingBlocks/BuildingBlocks.Email/readme.md: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/BuildingBlocks/BuildingBlocks.Logging/readme.md: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/BuildingBlocks/BuildingBlocks.Security/readme.md: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/BuildingBlocks/BuildingBlocks.Swagger/readme.md: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/BuildingBlocks/BuildingBlocks.Abstractions/readme.md: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/BuildingBlocks/BuildingBlocks.Monitoring/readme.md: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/BuildingBlocks/BuildingBlocks.Resiliency/readme.md: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/BuildingBlocks/BuildingBlocks.Validation/readme.md: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/BuildingBlocks/BuildingBlocks.Caching.InMemory/readme.md: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/BuildingBlocks/BuildingBlocks.Persistence.Mongo/readme.md: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /.devcontainer/scripts/update.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | set -eax -------------------------------------------------------------------------------- /src/BuildingBlocks/BuildingBlocks.Persistence.EfCore.Postgres/readme.md: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/Directory.Build.targets: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /assets/vsa2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mehdihadeli/food-delivery-modular-monolith/HEAD/assets/vsa2.png -------------------------------------------------------------------------------- /global.json: -------------------------------------------------------------------------------- 1 | { 2 | "sdk": { 3 | "version": "8.0.303", 4 | "rollForward": "latestFeature" 5 | } 6 | } -------------------------------------------------------------------------------- /src/Api/FoodDelivery.Api/ApiRoot.cs: -------------------------------------------------------------------------------- 1 | namespace FoodDelivery.Api; 2 | 3 | public class ApiRoot 4 | { 5 | } 6 | -------------------------------------------------------------------------------- /assets/modules.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mehdihadeli/food-delivery-modular-monolith/HEAD/assets/modules.png -------------------------------------------------------------------------------- /assets/catalog-module.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mehdihadeli/food-delivery-modular-monolith/HEAD/assets/catalog-module.png -------------------------------------------------------------------------------- /assets/level-structure.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mehdihadeli/food-delivery-modular-monolith/HEAD/assets/level-structure.png -------------------------------------------------------------------------------- /.devcontainer/scripts/post-create.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | set -eax 4 | 5 | # Run the setup_fonts.sh script 6 | ./setup-fonts.sh -------------------------------------------------------------------------------- /assets/composition-roots.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mehdihadeli/food-delivery-modular-monolith/HEAD/assets/composition-roots.png -------------------------------------------------------------------------------- /src/Modules/Orders/FoodDelivery.Modules.Orders/OrdersRoot.cs: -------------------------------------------------------------------------------- 1 | namespace FoodDelivery.Modules.Orders; 2 | 3 | public class OrdersRoot 4 | { 5 | } 6 | -------------------------------------------------------------------------------- /assets/vertical-slice-architecture.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mehdihadeli/food-delivery-modular-monolith/HEAD/assets/vertical-slice-architecture.jpg -------------------------------------------------------------------------------- /src/Modules/Catalogs/FoodDelivery.Modules.Catalogs/CatalogRoot.cs: -------------------------------------------------------------------------------- 1 | namespace FoodDelivery.Modules.Catalogs; 2 | 3 | public class CatalogRoot 4 | { 5 | } 6 | -------------------------------------------------------------------------------- /src/Modules/Identity/FoodDelivery.Modules.Identity/IdentityRoot.cs: -------------------------------------------------------------------------------- 1 | namespace FoodDelivery.Modules.Identity; 2 | 3 | public class IdentityRoot 4 | { 5 | } 6 | -------------------------------------------------------------------------------- /src/Modules/Orders/FoodDelivery.Modules.Orders/OrdersConstants.cs: -------------------------------------------------------------------------------- 1 | namespace FoodDelivery.Modules.Orders; 2 | 3 | public class OrdersConstants 4 | { 5 | } 6 | -------------------------------------------------------------------------------- /src/Modules/Customers/FoodDelivery.Modules.Customers/CustomersRoot.cs: -------------------------------------------------------------------------------- 1 | namespace FoodDelivery.Modules.Customers; 2 | 3 | public class CustomersRoot 4 | { 5 | } 6 | -------------------------------------------------------------------------------- /tests/building-blocks/Core/BuildingBlocks.Core.IntegrationTests/xunit.runner.json: -------------------------------------------------------------------------------- 1 | { 2 | "parallelizeAssembly": true, 3 | "parallelizeTestCollections": false 4 | } -------------------------------------------------------------------------------- /tests/modules/Customers/FoodDelivery.Modules.Customers.UnitTests/xunit.runner.json: -------------------------------------------------------------------------------- 1 | { 2 | "parallelizeAssembly": false, 3 | "parallelizeTestCollections": false 4 | } -------------------------------------------------------------------------------- /tests/modules/Identity/FoodDelivery.Modules.Identity.UnitTests/xunit.runner.json: -------------------------------------------------------------------------------- 1 | { 2 | "parallelizeAssembly": false, 3 | "parallelizeTestCollections": false 4 | } -------------------------------------------------------------------------------- /tests/shared/Tests.Shared/Mocks/UserType.cs: -------------------------------------------------------------------------------- 1 | namespace Tests.Shared.Mocks; 2 | 3 | public enum UserType 4 | { 5 | None = 0, 6 | Admin, 7 | User 8 | } 9 | -------------------------------------------------------------------------------- /.devcontainer/Dockerfile: -------------------------------------------------------------------------------- 1 | # https://containers.dev/guide/dockerfile#dockerfile 2 | 3 | FROM mcr.microsoft.com/devcontainers/dotnet:latest 4 | 5 | # Add additional commands -------------------------------------------------------------------------------- /tests/modules/Customers/FoodDelivery.Modules.Customers.EndToEndTests/xunit.runner.json: -------------------------------------------------------------------------------- 1 | { 2 | "parallelizeAssembly": false, 3 | "parallelizeTestCollections": false 4 | } -------------------------------------------------------------------------------- /tests/modules/Identity/FoodDelivery.Modules.Identity.EndToEndTests/xunit.runner.json: -------------------------------------------------------------------------------- 1 | { 2 | "parallelizeAssembly": false, 3 | "parallelizeTestCollections": false 4 | } -------------------------------------------------------------------------------- /tests/modules/Identity/FoodDelivery.Modules.Identity.IntegrationTests/xunit.runner.json: -------------------------------------------------------------------------------- 1 | { 2 | "parallelizeAssembly": false, 3 | "parallelizeTestCollections": false 4 | } -------------------------------------------------------------------------------- /src/BuildingBlocks/BuildingBlocks.Abstractions/Domain/IHaveSoftDelete.cs: -------------------------------------------------------------------------------- 1 | namespace BuildingBlocks.Abstractions.Domain; 2 | 3 | public interface IHaveSoftDelete 4 | { 5 | } 6 | -------------------------------------------------------------------------------- /src/Modules/Catalogs/FoodDelivery.Modules.Catalogs/CatalogConstants.cs: -------------------------------------------------------------------------------- 1 | namespace FoodDelivery.Modules.Catalogs; 2 | 3 | public static class CatalogConstants 4 | { 5 | } 6 | 7 | -------------------------------------------------------------------------------- /tests/shared/Tests.Shared/Mocks/LoginUserRequestMock.cs: -------------------------------------------------------------------------------- 1 | namespace Tests.Shared.Mocks; 2 | 3 | public record LoginUserRequestMock(string UserNameOrEmail, string Password); 4 | -------------------------------------------------------------------------------- /src/BuildingBlocks/BuildingBlocks.Email/EmailProvider.cs: -------------------------------------------------------------------------------- 1 | namespace BuildingBlocks.Email; 2 | 3 | public enum EmailProvider 4 | { 5 | SendGrid = 1, 6 | MimKit = 2 7 | } 8 | -------------------------------------------------------------------------------- /tests/modules/Customers/FoodDelivery.Modules.Customers.IntegrationTests/xunit.runner.json: -------------------------------------------------------------------------------- 1 | { 2 | "parallelizeAssembly": false, 3 | "parallelizeTestCollections": false 4 | } 5 | -------------------------------------------------------------------------------- /src/BuildingBlocks/BuildingBlocks.Abstractions/Messaging/IHaveExternalEvent.cs: -------------------------------------------------------------------------------- 1 | namespace BuildingBlocks.Abstractions.Messaging; 2 | 3 | public interface IHaveExternalEvent 4 | { 5 | } 6 | -------------------------------------------------------------------------------- /src/BuildingBlocks/BuildingBlocks.Abstractions/Core/IIdGenerator.cs: -------------------------------------------------------------------------------- 1 | namespace BuildingBlocks.Abstractions.Core; 2 | 3 | public interface IIdGenerator 4 | { 5 | TId New(); 6 | } 7 | -------------------------------------------------------------------------------- /src/Modules/Customers/FoodDelivery.Modules.Customers/Customers/Dtos/CustomerDto.cs: -------------------------------------------------------------------------------- 1 | namespace FoodDelivery.Modules.Customers.Customers.Dtos; 2 | 3 | public class CustomerDto 4 | { 5 | 6 | } 7 | -------------------------------------------------------------------------------- /.csharpierrc.yaml: -------------------------------------------------------------------------------- 1 | # https://csharpier.com/docs/Configuration 2 | printWidth: 120 3 | useTabs: false 4 | tabWidth: 4 5 | preprocessorSymbolSets: 6 | - "" 7 | - "DEBUG" 8 | - "DEBUG,CODE_STYLE" 9 | -------------------------------------------------------------------------------- /src/BuildingBlocks/BuildingBlocks.Abstractions/CQRS/FilterModel.cs: -------------------------------------------------------------------------------- 1 | namespace BuildingBlocks.Abstractions.CQRS; 2 | 3 | public record FilterModel(string FieldName, string Comparision, string FieldValue); 4 | -------------------------------------------------------------------------------- /src/BuildingBlocks/BuildingBlocks.Abstractions/Messaging/IConsumeConfiguratorBuilder.cs: -------------------------------------------------------------------------------- 1 | namespace BuildingBlocks.Abstractions.Messaging; 2 | 3 | public interface IConsumeConfigurationBuilder 4 | { 5 | } 6 | -------------------------------------------------------------------------------- /src/BuildingBlocks/BuildingBlocks.Abstractions/Persistence/IDataSeeder.cs: -------------------------------------------------------------------------------- 1 | namespace BuildingBlocks.Abstractions.Persistence; 2 | 3 | public interface IDataSeeder 4 | { 5 | Task SeedAllAsync(); 6 | } 7 | -------------------------------------------------------------------------------- /src/BuildingBlocks/BuildingBlocks.Web/Constants.cs: -------------------------------------------------------------------------------- 1 | namespace BuildingBlocks.Web; 2 | 3 | public static class Constants 4 | { 5 | public const string BaseApiPath = "api/v{version:apiVersion}"; 6 | } 7 | -------------------------------------------------------------------------------- /deployments/docker-compose/.env.prod: -------------------------------------------------------------------------------- 1 | #https://docs.docker.com/compose/environment-variables/set-environment-variables/#substitute-with---env-file 2 | #https://docs.docker.com/compose/environment-variables/env-file/ -------------------------------------------------------------------------------- /src/Modules/Catalogs/FoodDelivery.Modules.Catalogs/Products/Models/Read/ProductReadModel.cs: -------------------------------------------------------------------------------- 1 | namespace FoodDelivery.Modules.Catalogs.Products.Models.Read; 2 | 3 | public class ProductReadModel 4 | { 5 | 6 | } 7 | -------------------------------------------------------------------------------- /src/BuildingBlocks/BuildingBlocks.Abstractions/CQRS/Event/Internal/IHaveNotificationEvent.cs: -------------------------------------------------------------------------------- 1 | namespace BuildingBlocks.Abstractions.CQRS.Event.Internal; 2 | 3 | public interface IHaveNotificationEvent 4 | { 5 | } 6 | -------------------------------------------------------------------------------- /src/BuildingBlocks/BuildingBlocks.Resiliency/Retry/IRetryPolicyOptions.cs: -------------------------------------------------------------------------------- 1 | namespace BuildingBlocks.Resiliency.Retry; 2 | 3 | public interface IRetryPolicyOptions 4 | { 5 | int RetryCount { get; set; } 6 | } 7 | -------------------------------------------------------------------------------- /src/BuildingBlocks/BuildingBlocks.Security/ApiKey/ApiKeyConstants.cs: -------------------------------------------------------------------------------- 1 | namespace BuildingBlocks.Security.ApiKey; 2 | 3 | public class ApiKeyConstants 4 | { 5 | public const string HeaderName = "X-Api-Key"; 6 | } 7 | -------------------------------------------------------------------------------- /src/Modules/Customers/FoodDelivery.Modules.Customers/Customers/Extensions/GuardExtensions.cs: -------------------------------------------------------------------------------- 1 | namespace FoodDelivery.Modules.Customers.Customers.Extensions; 2 | 3 | public static class GuardExtensions 4 | { 5 | } 6 | -------------------------------------------------------------------------------- /src/Modules/Identity/FoodDelivery.Modules.Identity/Shared/Models/UserState.cs: -------------------------------------------------------------------------------- 1 | namespace FoodDelivery.Modules.Identity.Shared.Models; 2 | 3 | public enum UserState 4 | { 5 | Active = 1, 6 | Locked = 2 7 | } 8 | -------------------------------------------------------------------------------- /src/BuildingBlocks/BuildingBlocks.Security/ApiKey/IGetApiKeyQuery.cs: -------------------------------------------------------------------------------- 1 | namespace BuildingBlocks.Security.ApiKey; 2 | 3 | public interface IGetApiKeyQuery 4 | { 5 | Task ExecuteAsync(string providedApiKey); 6 | } 7 | -------------------------------------------------------------------------------- /src/Modules/Customers/FoodDelivery.Modules.Customers/Customers/Models/UserState.cs: -------------------------------------------------------------------------------- 1 | namespace FoodDelivery.Modules.Customers.Customers.Models; 2 | 3 | public enum UserState 4 | { 5 | Active = 1, 6 | Locked = 2 7 | } 8 | -------------------------------------------------------------------------------- /src/BuildingBlocks/BuildingBlocks.Abstractions/Domain/IBusinessRule.cs: -------------------------------------------------------------------------------- 1 | namespace BuildingBlocks.Abstractions.Domain; 2 | 3 | public interface IBusinessRule 4 | { 5 | bool IsBroken(); 6 | string Message { get; } 7 | } 8 | -------------------------------------------------------------------------------- /src/BuildingBlocks/BuildingBlocks.Abstractions/Domain/IHaveCreator.cs: -------------------------------------------------------------------------------- 1 | namespace BuildingBlocks.Abstractions.Domain; 2 | 3 | public interface IHaveCreator 4 | { 5 | DateTime Created { get; } 6 | int? CreatedBy { get; } 7 | } 8 | -------------------------------------------------------------------------------- /src/BuildingBlocks/BuildingBlocks.Caching.InMemory/InMemoryCacheOptions.cs: -------------------------------------------------------------------------------- 1 | using BuildingBlocks.Core.Caching; 2 | 3 | namespace BuildingBlocks.Caching.InMemory; 4 | 5 | public class InMemoryCacheOptions : CacheOptions 6 | { 7 | } 8 | -------------------------------------------------------------------------------- /src/BuildingBlocks/BuildingBlocks.Core/Messaging/IntegrationEvent.cs: -------------------------------------------------------------------------------- 1 | using BuildingBlocks.Abstractions.Messaging; 2 | 3 | namespace BuildingBlocks.Core.Messaging; 4 | 5 | public record IntegrationEvent : Message, IIntegrationEvent; 6 | -------------------------------------------------------------------------------- /src/BuildingBlocks/BuildingBlocks.Resiliency/Timeout/ITimeoutPolicyOptions.cs: -------------------------------------------------------------------------------- 1 | namespace BuildingBlocks.Resiliency.Timeout; 2 | 3 | public interface ITimeoutPolicyOptions 4 | { 5 | public int TimeOutDuration { get; set; } 6 | } 7 | -------------------------------------------------------------------------------- /src/Modules/Catalogs/FoodDelivery.Modules.Catalogs/Products/Models/ProductStatus.cs: -------------------------------------------------------------------------------- 1 | namespace FoodDelivery.Modules.Catalogs.Products.Models; 2 | 3 | public enum ProductStatus 4 | { 5 | Available = 1, 6 | Unavailable, 7 | } 8 | -------------------------------------------------------------------------------- /tests/shared/Tests.Shared/Mocks/LoginResponseMock.cs: -------------------------------------------------------------------------------- 1 | namespace Tests.Shared.Mocks; 2 | 3 | public class LoginResponseMock 4 | { 5 | public string RefreshToken { get; set; } 6 | public string AccessToken { get; set; } 7 | } 8 | -------------------------------------------------------------------------------- /src/BuildingBlocks/BuildingBlocks.Abstractions/Messaging/PersistMessage/MessageStatus.cs: -------------------------------------------------------------------------------- 1 | namespace BuildingBlocks.Abstractions.Messaging.PersistMessage; 2 | 3 | public enum MessageStatus 4 | { 5 | Stored = 1, 6 | Processed = 2 7 | } 8 | -------------------------------------------------------------------------------- /src/Modules/Identity/FoodDelivery.Modules.Identity/Identity/Features/GetClaims/GetClaimsResponse.cs: -------------------------------------------------------------------------------- 1 | namespace FoodDelivery.Modules.Identity.Identity.Features.GetClaims; 2 | 3 | public record GetClaimsResponse(IEnumerable Claims); 4 | -------------------------------------------------------------------------------- /tests/modules/Identity/FoodDelivery.Modules.Identity.UnitTests/UnitTest1.cs: -------------------------------------------------------------------------------- 1 | namespace FoodDelivery.Modules.Identity.UnitTests; 2 | 3 | public class UnitTest1 4 | { 5 | [Fact] 6 | public void Test1() 7 | { 8 | 9 | } 10 | } -------------------------------------------------------------------------------- /src/BuildingBlocks/BuildingBlocks.Abstractions/Types/IMachineInstanceInfo.cs: -------------------------------------------------------------------------------- 1 | namespace BuildingBlocks.Abstractions.Types; 2 | 3 | public interface IMachineInstanceInfo 4 | { 5 | string ClientGroup { get; } 6 | Guid ClientId { get; } 7 | } 8 | -------------------------------------------------------------------------------- /src/Modules/Customers/FoodDelivery.Modules.Customers/Shared/Clients/Catalogs/Dtos/GetProductByIdResponse.cs: -------------------------------------------------------------------------------- 1 | namespace FoodDelivery.Modules.Customers.Shared.Clients.Catalogs.Dtos; 2 | 3 | public record GetProductByIdResponse(ProductDto Product); 4 | -------------------------------------------------------------------------------- /src/BuildingBlocks/BuildingBlocks.Abstractions/CQRS/Query/IListQuery.cs: -------------------------------------------------------------------------------- 1 | namespace BuildingBlocks.Abstractions.CQRS.Query; 2 | 3 | public interface IListQuery : IPageRequest, IQuery 4 | where TResponse : notnull 5 | { 6 | } 7 | -------------------------------------------------------------------------------- /src/BuildingBlocks/BuildingBlocks.Abstractions/Domain/IHaveAudit.cs: -------------------------------------------------------------------------------- 1 | namespace BuildingBlocks.Abstractions.Domain; 2 | 3 | public interface IHaveAudit : IHaveCreator 4 | { 5 | DateTime? LastModified { get; } 6 | int? LastModifiedBy { get; } 7 | } 8 | -------------------------------------------------------------------------------- /src/Modules/Catalogs/FoodDelivery.Modules.Catalogs/Products/Features/ChangingProductBrand/ChangeProductBrandResponse.cs: -------------------------------------------------------------------------------- 1 | namespace FoodDelivery.Modules.Catalogs.Products.Features.ChangingProductBrand; 2 | 3 | internal record ChangeProductBrandResponse; 4 | -------------------------------------------------------------------------------- /src/Modules/Customers/FoodDelivery.Modules.Customers/Customers/Features/CreatingCustomer/CreateCustomerRequest.cs: -------------------------------------------------------------------------------- 1 | namespace FoodDelivery.Modules.Customers.Customers.Features.CreatingCustomer; 2 | 3 | public record CreateCustomerRequest(string Email); 4 | -------------------------------------------------------------------------------- /src/Modules/Customers/FoodDelivery.Modules.Customers/Shared/Clients/Identity/Dtos/CreateUserResponse.cs: -------------------------------------------------------------------------------- 1 | namespace FoodDelivery.Modules.Customers.Shared.Clients.Identity.Dtos; 2 | 3 | public record CreateUserResponse(UserIdentityDto? UserIdentity); 4 | 5 | -------------------------------------------------------------------------------- /tests/modules/Identity/FoodDelivery.Modules.Identity.EndToEndTests/UnitTest1.cs: -------------------------------------------------------------------------------- 1 | namespace FoodDelivery.Modules.Identity.EndToEndTests; 2 | 3 | public class UnitTest1 4 | { 5 | [Fact] 6 | public void Test1() 7 | { 8 | 9 | } 10 | } -------------------------------------------------------------------------------- /src/BuildingBlocks/BuildingBlocks.Core/CQRS/Event/Internal/NotificationEvent.cs: -------------------------------------------------------------------------------- 1 | namespace BuildingBlocks.Core.CQRS.Event.Internal; 2 | 3 | // Just for executing after transaction 4 | public record NotificationEvent(dynamic Data) : DomainNotificationEvent; 5 | -------------------------------------------------------------------------------- /src/BuildingBlocks/BuildingBlocks.Core/Messaging/MessagePersistence/MessagePersistenceOptions.cs: -------------------------------------------------------------------------------- 1 | namespace BuildingBlocks.Core.Messaging.MessagePersistence; 2 | 3 | public class MessagePersistenceOptions 4 | { 5 | public int? Interval { get; set; } 6 | } 7 | -------------------------------------------------------------------------------- /src/Modules/Customers/FoodDelivery.Modules.Customers/Shared/Clients/Identity/Dtos/GetUserByEmailResponse.cs: -------------------------------------------------------------------------------- 1 | namespace FoodDelivery.Modules.Customers.Shared.Clients.Identity.Dtos; 2 | 3 | public record GetUserByEmailResponse(UserIdentityDto? UserIdentity); 4 | -------------------------------------------------------------------------------- /src/Modules/Identity/FoodDelivery.Modules.Identity/Identity/Features/Login/LoginUserRequest.cs: -------------------------------------------------------------------------------- 1 | namespace FoodDelivery.Modules.Identity.Identity.Features.Login; 2 | 3 | public record LoginUserRequest(string UserNameOrEmail, string Password, bool Remember); 4 | -------------------------------------------------------------------------------- /src/Modules/Identity/FoodDelivery.Modules.Identity/Identity/Features/RevokeRefreshToken/RevokeRefreshTokenResponse.cs: -------------------------------------------------------------------------------- 1 | namespace FoodDelivery.Modules.Identity.Identity.Features.RevokeRefreshToken; 2 | 3 | public class RevokeRefreshTokenResponse 4 | { 5 | } 6 | -------------------------------------------------------------------------------- /src/Modules/Orders/FoodDelivery.Modules.Orders/readme.md: -------------------------------------------------------------------------------- 1 | #### Migration Scripts 2 | 3 | ```bash 4 | dotnet ef migrations add InitialOrdersMigration -o Shared/Data/Migrations/Order -c OrdersDbContext 5 | dotnet ef database update -c OrdersDbContext 6 | ``` 7 | -------------------------------------------------------------------------------- /src/BuildingBlocks/BuildingBlocks.Core/CQRS/Command/TxInternalCommand.cs: -------------------------------------------------------------------------------- 1 | using BuildingBlocks.Abstractions.CQRS.Command; 2 | 3 | namespace BuildingBlocks.Core.CQRS.Command; 4 | 5 | public abstract record TxInternalCommand : InternalCommand, ITxInternalCommand; 6 | -------------------------------------------------------------------------------- /src/Modules/Catalogs/FoodDelivery.Modules.Catalogs/Products/Features/ChangingProductCategory/ChangeProductCategoryResponse.cs: -------------------------------------------------------------------------------- 1 | namespace FoodDelivery.Modules.Catalogs.Products.Features.ChangingProductCategory; 2 | 3 | internal record ChangeProductCategoryResponse; 4 | -------------------------------------------------------------------------------- /src/Modules/Catalogs/FoodDelivery.Modules.Catalogs/Products/Features/ChangingProductSupplier/ChangeProductSupplierResponse.cs: -------------------------------------------------------------------------------- 1 | namespace FoodDelivery.Modules.Catalogs.Products.Features.ChangingProductSupplier; 2 | 3 | internal record ChangeProductSupplierResponse; 4 | -------------------------------------------------------------------------------- /src/Modules/Customers/FoodDelivery.Modules.Customers/Shared/Clients/Catalogs/Dtos/ProductStatus.cs: -------------------------------------------------------------------------------- 1 | namespace FoodDelivery.Modules.Customers.Shared.Clients.Catalogs.Dtos; 2 | 3 | public enum ProductStatus 4 | { 5 | Available = 1, 6 | Unavailable, 7 | } 8 | -------------------------------------------------------------------------------- /src/BuildingBlocks/BuildingBlocks.Swagger/SwaggerOptions.cs: -------------------------------------------------------------------------------- 1 | namespace BuildingBlocks.Swagger; 2 | 3 | public class SwaggerOptions 4 | { 5 | public string Title { get; set; } 6 | public string Name { get; set; } 7 | public string Version { get; set; } 8 | } 9 | -------------------------------------------------------------------------------- /src/Modules/Catalogs/FoodDelivery.Modules.Catalogs/readme.md: -------------------------------------------------------------------------------- 1 | #### Migration Scripts 2 | 3 | ```bash 4 | dotnet ef migrations add InitialCatalogMigration -o Shared/Data/Migrations/Catalogs -c CatalogDbContext 5 | dotnet ef database update -c CatalogDbContext 6 | ``` 7 | -------------------------------------------------------------------------------- /src/Modules/Identity/FoodDelivery.Modules.Identity/Identity/Features/RefreshingToken/RefreshTokenRequest.cs: -------------------------------------------------------------------------------- 1 | namespace FoodDelivery.Modules.Identity.Identity.Features.RefreshingToken; 2 | 3 | public record RefreshTokenRequest(string AccessToken, string RefreshToken); 4 | -------------------------------------------------------------------------------- /tests/shared/Tests.Shared/Probing/AssertErrorException.cs: -------------------------------------------------------------------------------- 1 | namespace Tests.Shared.Probing; 2 | 3 | public class AssertErrorException : Exception 4 | { 5 | public AssertErrorException(string message) 6 | : base(message) 7 | { 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /src/BuildingBlocks/BuildingBlocks.Abstractions/Messaging/IIntegrationEvent.cs: -------------------------------------------------------------------------------- 1 | namespace BuildingBlocks.Abstractions.Messaging; 2 | 3 | /// 4 | /// The integration event interface. 5 | /// 6 | public interface IIntegrationEvent : IMessage 7 | { 8 | } 9 | -------------------------------------------------------------------------------- /src/BuildingBlocks/BuildingBlocks.Abstractions/Persistence/Mongo/IMongoUnitOfWork.cs: -------------------------------------------------------------------------------- 1 | namespace BuildingBlocks.Abstractions.Persistence.Mongo; 2 | 3 | public interface IMongoUnitOfWork : IUnitOfWork where TContext : class, IMongoDbContext 4 | { 5 | } 6 | -------------------------------------------------------------------------------- /src/Modules/Customers/FoodDelivery.Modules.Customers/readme.md: -------------------------------------------------------------------------------- 1 | #### Migration Scripts 2 | 3 | ```bash 4 | dotnet ef migrations add InitialCustomersMigration -o Shared/Data/Migrations/Customer -c CustomersDbContext 5 | dotnet ef database update -c CustomersDbContext 6 | ``` 7 | -------------------------------------------------------------------------------- /src/Modules/Identity/FoodDelivery.Modules.Identity/readme.md: -------------------------------------------------------------------------------- 1 | #### Migration Scripts 2 | 3 | ```bash 4 | dotnet ef migrations add InitialIdentityServerMigration -o Shared/Data/Migrations/Identity -c IdentityContext 5 | dotnet ef database update -c IdentityContext 6 | ``` 7 | -------------------------------------------------------------------------------- /src/Modules/Orders/FoodDelivery.Modules.Orders/Orders/Models/Reads/OrderReadModel.cs: -------------------------------------------------------------------------------- 1 | namespace FoodDelivery.Modules.Orders.Orders.Models.Reads; 2 | 3 | public class OrderReadModel 4 | { 5 | public Guid Id { get; init; } 6 | public long OrderId { get; init; } 7 | } 8 | -------------------------------------------------------------------------------- /src/BuildingBlocks/BuildingBlocks.Abstractions/CQRS/Command/ITxInternalCommand.cs: -------------------------------------------------------------------------------- 1 | using BuildingBlocks.Abstractions.Persistence; 2 | 3 | namespace BuildingBlocks.Abstractions.CQRS.Command; 4 | 5 | public interface ITxInternalCommand : IInternalCommand, ITxRequest 6 | { 7 | } 8 | -------------------------------------------------------------------------------- /src/BuildingBlocks/BuildingBlocks.Abstractions/CQRS/Event/IEventHandler.cs: -------------------------------------------------------------------------------- 1 | using MediatR; 2 | 3 | namespace BuildingBlocks.Abstractions.CQRS.Event; 4 | 5 | public interface IEventHandler : INotificationHandler 6 | where TEvent : INotification 7 | { 8 | } 9 | -------------------------------------------------------------------------------- /src/BuildingBlocks/BuildingBlocks.Abstractions/CQRS/Event/Internal/IDomainEventHandler.cs: -------------------------------------------------------------------------------- 1 | namespace BuildingBlocks.Abstractions.CQRS.Event.Internal; 2 | 3 | public interface IDomainEventHandler : IEventHandler 4 | where TEvent : IDomainEvent 5 | { 6 | } 7 | -------------------------------------------------------------------------------- /src/BuildingBlocks/BuildingBlocks.Abstractions/Persistence/ITxRequest.cs: -------------------------------------------------------------------------------- 1 | namespace BuildingBlocks.Abstractions.Persistence; 2 | 3 | // Ref: https://github.com/thangchung/clean-architecture-dotnet/blob/main/src/N8T.Core/Domain/Cqrs.cs 4 | public interface ITxRequest 5 | { 6 | } 7 | -------------------------------------------------------------------------------- /src/BuildingBlocks/BuildingBlocks.Integration.Hangfire/HangfireOptions.cs: -------------------------------------------------------------------------------- 1 | namespace BuildingBlocks.Integration.Hangfire; 2 | 3 | public class HangfireOptions 4 | { 5 | public string ConnectionString { get; set; } = null!; 6 | public bool UseInMemory { get; set; } 7 | } 8 | -------------------------------------------------------------------------------- /src/BuildingBlocks/BuildingBlocks.Security/ApiKey/Authorization/OnlyAdminsRequirement.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.AspNetCore.Authorization; 2 | 3 | namespace BuildingBlocks.Security.ApiKey.Authorization; 4 | 5 | public class OnlyAdminsRequirement : IAuthorizationRequirement 6 | { 7 | } 8 | -------------------------------------------------------------------------------- /src/Api/FoodDelivery.Api/ApiConstants.cs: -------------------------------------------------------------------------------- 1 | namespace FoodDelivery.Api; 2 | 3 | public static class ApiConstants 4 | { 5 | public static class Role 6 | { 7 | public const string Admin = "admin"; 8 | public const string User = "user"; 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /src/BuildingBlocks/BuildingBlocks.Abstractions/Messaging/IMessage.cs: -------------------------------------------------------------------------------- 1 | using MediatR; 2 | 3 | namespace BuildingBlocks.Abstractions.Messaging; 4 | 5 | public interface IMessage : INotification 6 | { 7 | Guid MessageId { get; } 8 | DateTime Created { get; } 9 | } 10 | -------------------------------------------------------------------------------- /src/Modules/Catalogs/FoodDelivery.Modules.Catalogs/Products/Features/GettingProductsView/GetProductsViewResponse.cs: -------------------------------------------------------------------------------- 1 | namespace FoodDelivery.Modules.Catalogs.Products.Features.GettingProductsView; 2 | 3 | public record GetProductsViewResponse(IEnumerable Products); 4 | -------------------------------------------------------------------------------- /src/BuildingBlocks/BuildingBlocks.Abstractions/CQRS/Command/IInternalCommand.cs: -------------------------------------------------------------------------------- 1 | namespace BuildingBlocks.Abstractions.CQRS.Command; 2 | 3 | public interface IInternalCommand : ICommand 4 | { 5 | Guid Id { get; } 6 | DateTime OccurredOn { get; } 7 | string Type { get; } 8 | } 9 | -------------------------------------------------------------------------------- /src/BuildingBlocks/BuildingBlocks.Abstractions/Persistence/IDbFacadeResolver.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.EntityFrameworkCore.Infrastructure; 2 | 3 | namespace BuildingBlocks.Abstractions.Persistence; 4 | 5 | public interface IDbFacadeResolver 6 | { 7 | DatabaseFacade Database { get; } 8 | } 9 | -------------------------------------------------------------------------------- /src/BuildingBlocks/BuildingBlocks.Abstractions/Web/Storage/IRequestStorage.cs: -------------------------------------------------------------------------------- 1 | namespace BuildingBlocks.Abstractions.Web.Storage; 2 | 3 | public interface IRequestStorage 4 | { 5 | void Set(string key, T value) 6 | where T : notnull; 7 | T? Get(string key); 8 | } 9 | -------------------------------------------------------------------------------- /src/BuildingBlocks/BuildingBlocks.Resiliency/CircuitBreaker/ICircuitBreakerPolicyOptions.cs: -------------------------------------------------------------------------------- 1 | namespace BuildingBlocks.Resiliency.CircuitBreaker; 2 | 3 | public interface ICircuitBreakerPolicyOptions 4 | { 5 | int RetryCount { get; set; } 6 | int BreakDuration { get; set; } 7 | } 8 | -------------------------------------------------------------------------------- /src/BuildingBlocks/BuildingBlocks.Security/ApiKey/Authorization/OnlyCustomersRequirement.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.AspNetCore.Authorization; 2 | 3 | namespace BuildingBlocks.Security.ApiKey.Authorization; 4 | 5 | public class OnlyCustomersRequirement : IAuthorizationRequirement 6 | { 7 | } 8 | -------------------------------------------------------------------------------- /src/Modules/Catalogs/FoodDelivery.Modules.Catalogs/Products/Models/ProductColor.cs: -------------------------------------------------------------------------------- 1 | namespace FoodDelivery.Modules.Catalogs.Products.Models; 2 | 3 | public enum ProductColor 4 | { 5 | Black, 6 | Blue, 7 | Red, 8 | White, 9 | Yellow, 10 | Purple, 11 | } 12 | -------------------------------------------------------------------------------- /src/Modules/Identity/FoodDelivery.Modules.Identity/Identity/Features/GetClaims/ClaimDto.cs: -------------------------------------------------------------------------------- 1 | namespace FoodDelivery.Modules.Identity.Identity.Features.GetClaims; 2 | 3 | public class ClaimDto 4 | { 5 | public string Type { get; init; } 6 | public string Value { get; init; } 7 | } 8 | -------------------------------------------------------------------------------- /src/Modules/Identity/FoodDelivery.Modules.Identity/Identity/Features/ResetPassword/ResetPassword.cs: -------------------------------------------------------------------------------- 1 | using BuildingBlocks.Abstractions.CQRS.Command; 2 | 3 | namespace FoodDelivery.Modules.Identity.Identity.Features.ResetPassword; 4 | 5 | public class ResetPassword : ICommand 6 | { 7 | } 8 | -------------------------------------------------------------------------------- /src/BuildingBlocks/BuildingBlocks.Abstractions/Messaging/PersistMessage/MessageDeliveryType.cs: -------------------------------------------------------------------------------- 1 | namespace BuildingBlocks.Abstractions.Messaging.PersistMessage; 2 | 3 | [Flags] 4 | public enum MessageDeliveryType 5 | { 6 | Outbox = 1, 7 | Inbox = 2, 8 | Internal = 4 9 | } 10 | -------------------------------------------------------------------------------- /src/BuildingBlocks/BuildingBlocks.Abstractions/Persistence/EfCore/IConnectionFactory.cs: -------------------------------------------------------------------------------- 1 | using System.Data; 2 | 3 | namespace BuildingBlocks.Abstractions.Persistence.EfCore; 4 | 5 | public interface IConnectionFactory : IDisposable 6 | { 7 | IDbConnection GetOrCreateConnection(); 8 | } 9 | -------------------------------------------------------------------------------- /src/BuildingBlocks/BuildingBlocks.Core/CQRS/Event/Internal/DomainNotificationEvent.cs: -------------------------------------------------------------------------------- 1 | using BuildingBlocks.Abstractions.CQRS.Event.Internal; 2 | 3 | namespace BuildingBlocks.Core.CQRS.Event.Internal; 4 | 5 | public abstract record DomainNotificationEvent : Event, IDomainNotificationEvent; 6 | -------------------------------------------------------------------------------- /src/BuildingBlocks/BuildingBlocks.Persistence.EfCore.Postgres/PostgresOptions.cs: -------------------------------------------------------------------------------- 1 | namespace BuildingBlocks.Persistence.EfCore.Postgres; 2 | 3 | public class PostgresOptions 4 | { 5 | public string ConnectionString { get; set; } = null!; 6 | public bool UseInMemory { get; set; } 7 | } 8 | -------------------------------------------------------------------------------- /src/BuildingBlocks/BuildingBlocks.Security/ApiKey/Authorization/OnlyThirdPartiesRequirement.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.AspNetCore.Authorization; 2 | 3 | namespace BuildingBlocks.Security.ApiKey.Authorization; 4 | 5 | public class OnlyThirdPartiesRequirement : IAuthorizationRequirement 6 | { 7 | } 8 | -------------------------------------------------------------------------------- /src/BuildingBlocks/BuildingBlocks.Web/Module/ModuleHook.cs: -------------------------------------------------------------------------------- 1 | using BuildingBlocks.Abstractions.Web.Module; 2 | 3 | namespace BuildingBlocks.Web.Module; 4 | 5 | public static class ModuleHook 6 | { 7 | public static Action? ModuleServicesConfigured; 8 | } 9 | -------------------------------------------------------------------------------- /src/Modules/Identity/FoodDelivery.Modules.Identity/Identity/Features/RevokeRefreshToken/RevokeRefreshTokenRequest.cs: -------------------------------------------------------------------------------- 1 | namespace FoodDelivery.Modules.Identity.Identity.Features.RevokeRefreshToken; 2 | 3 | public class RevokeRefreshTokenRequest 4 | { 5 | public string RefreshToken { get; set; } 6 | } 7 | -------------------------------------------------------------------------------- /src/Modules/Identity/FoodDelivery.Modules.Identity/Users/Features/GettingUserById/UserByIdResponse.cs: -------------------------------------------------------------------------------- 1 | using FoodDelivery.Modules.Identity.Users.Dtos; 2 | 3 | namespace FoodDelivery.Modules.Identity.Users.Features.GettingUserById; 4 | 5 | internal record UserByIdResponse(IdentityUserDto IdentityUser); 6 | -------------------------------------------------------------------------------- /tests/shared/Tests.Shared/Mocks/MockAuthUser.cs: -------------------------------------------------------------------------------- 1 | using System.Security.Claims; 2 | 3 | namespace Tests.Shared.Mocks; 4 | 5 | public class MockAuthUser 6 | { 7 | public List Claims { get; } 8 | 9 | public MockAuthUser(params Claim[] claims) => Claims = claims.ToList(); 10 | } 11 | -------------------------------------------------------------------------------- /src/BuildingBlocks/BuildingBlocks.Abstractions/Persistence/EventStore/AppendResult.cs: -------------------------------------------------------------------------------- 1 | namespace BuildingBlocks.Abstractions.Persistence.EventStore; 2 | 3 | public record AppendResult(long GlobalPosition, long NextExpectedVersion) 4 | { 5 | public static readonly AppendResult None = new(0, -1); 6 | }; 7 | -------------------------------------------------------------------------------- /src/Modules/Identity/FoodDelivery.Modules.Identity/Identity/Features/VerifyEmail/VerifyEmailRequest.cs: -------------------------------------------------------------------------------- 1 | namespace FoodDelivery.Modules.Identity.Identity.Features.VerifyEmail; 2 | 3 | public class VerifyEmailRequest 4 | { 5 | public string Email { get; set; } 6 | public string Code { get; set; } 7 | } 8 | -------------------------------------------------------------------------------- /src/Modules/Catalogs/FoodDelivery.Modules.Catalogs/Products/Features/CreatingProduct/CreateProductResponse.cs: -------------------------------------------------------------------------------- 1 | using FoodDelivery.Modules.Catalogs.Products.Dtos; 2 | 3 | namespace FoodDelivery.Modules.Catalogs.Products.Features.CreatingProduct; 4 | 5 | public record CreateProductResponse(ProductDto Product); 6 | -------------------------------------------------------------------------------- /src/Modules/Catalogs/FoodDelivery.Modules.Catalogs/Products/Features/GettingProductsView/ProductViewDto.cs: -------------------------------------------------------------------------------- 1 | namespace FoodDelivery.Modules.Catalogs.Products.Features.GettingProductsView; 2 | 3 | public record struct ProductViewDto(long Id, string Name, string CategoryName, string SupplierName, long ItemCount); 4 | -------------------------------------------------------------------------------- /src/Modules/Identity/FoodDelivery.Modules.Identity/Users/Features/RegisteringUser/RegisterUserResponse.cs: -------------------------------------------------------------------------------- 1 | using FoodDelivery.Modules.Identity.Users.Dtos; 2 | 3 | namespace FoodDelivery.Modules.Identity.Users.Features.RegisteringUser; 4 | 5 | internal record RegisterUserResponse(IdentityUserDto? UserIdentity); 6 | -------------------------------------------------------------------------------- /src/BuildingBlocks/BuildingBlocks.Abstractions/CQRS/Command/ICommand.cs: -------------------------------------------------------------------------------- 1 | using MediatR; 2 | 3 | namespace BuildingBlocks.Abstractions.CQRS.Command; 4 | 5 | public interface ICommand : ICommand 6 | { 7 | } 8 | 9 | public interface ICommand : IRequest 10 | where T : notnull 11 | { 12 | } 13 | -------------------------------------------------------------------------------- /src/BuildingBlocks/BuildingBlocks.Abstractions/CQRS/Event/Internal/IDomainNotificationEventHandler.cs: -------------------------------------------------------------------------------- 1 | namespace BuildingBlocks.Abstractions.CQRS.Event.Internal; 2 | 3 | public interface IDomainNotificationEventHandler : IEventHandler 4 | where TEvent : IDomainNotificationEvent 5 | 6 | { 7 | } 8 | -------------------------------------------------------------------------------- /src/BuildingBlocks/BuildingBlocks.Abstractions/Persistence/EventStore/IStreamEventMetadata.cs: -------------------------------------------------------------------------------- 1 | namespace BuildingBlocks.Abstractions.Persistence.EventStore; 2 | 3 | public interface IStreamEventMetadata 4 | { 5 | string EventId { get; } 6 | long? LogPosition { get; } 7 | long StreamPosition { get; } 8 | } 9 | -------------------------------------------------------------------------------- /src/BuildingBlocks/BuildingBlocks.Security/Jwt/ISecurityContextAccessor.cs: -------------------------------------------------------------------------------- 1 | namespace BuildingBlocks.Security.Jwt; 2 | 3 | public interface ISecurityContextAccessor 4 | { 5 | string UserId { get; } 6 | string Role { get; } 7 | string JwtToken { get; } 8 | bool IsAuthenticated { get; } 9 | } 10 | -------------------------------------------------------------------------------- /src/Modules/Identity/FoodDelivery.Modules.Identity/Users/Features/GettingUerByEmail/GetUserByEmailResponse.cs: -------------------------------------------------------------------------------- 1 | using FoodDelivery.Modules.Identity.Users.Dtos; 2 | 3 | namespace FoodDelivery.Modules.Identity.Users.Features.GettingUerByEmail; 4 | 5 | public record GetUserByEmailResponse(IdentityUserDto? UserIdentity); 6 | -------------------------------------------------------------------------------- /nuget.config: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /src/BuildingBlocks/BuildingBlocks.Abstractions/CQRS/Event/Internal/IDomainEventContext.cs: -------------------------------------------------------------------------------- 1 | namespace BuildingBlocks.Abstractions.CQRS.Event.Internal; 2 | 3 | public interface IDomainEventContext 4 | { 5 | IReadOnlyList GetAllUncommittedEvents(); 6 | void MarkUncommittedDomainEventAsCommitted(); 7 | } 8 | -------------------------------------------------------------------------------- /src/BuildingBlocks/BuildingBlocks.Abstractions/Web/Module/ICompositionRoot.cs: -------------------------------------------------------------------------------- 1 | namespace BuildingBlocks.Abstractions.Web.Module; 2 | 3 | public interface ICompositionRoot 4 | { 5 | IServiceProvider ServiceProvider { get; } 6 | IModuleDefinition ModuleDefinition { get; } 7 | IServiceScope CreateScope(); 8 | } 9 | -------------------------------------------------------------------------------- /src/BuildingBlocks/BuildingBlocks.Core/Messaging/Message.cs: -------------------------------------------------------------------------------- 1 | using BuildingBlocks.Abstractions.Messaging; 2 | 3 | namespace BuildingBlocks.Core.Messaging; 4 | 5 | public record Message : IMessage 6 | { 7 | public Guid MessageId => Guid.NewGuid(); 8 | public DateTime Created { get; } = DateTime.Now; 9 | } 10 | -------------------------------------------------------------------------------- /src/Modules/Identity/FoodDelivery.Modules.Identity/Identity/Features/SendEmailVerificationCode/SendEmailVerificationCodeRequest.cs: -------------------------------------------------------------------------------- 1 | namespace FoodDelivery.Modules.Identity.Identity.Features.SendEmailVerificationCode; 2 | 3 | public class SendEmailVerificationCodeRequest 4 | { 5 | public string Email { get; set; } 6 | } 7 | -------------------------------------------------------------------------------- /src/Modules/Identity/FoodDelivery.Modules.Identity/Identity/Features/SendResetPasswordCode/SendResetPasswordCode.cs: -------------------------------------------------------------------------------- 1 | using BuildingBlocks.Abstractions.CQRS.Command; 2 | 3 | namespace FoodDelivery.Modules.Identity.Identity.Features.SendResetPasswordCode; 4 | 5 | public class SendResetPasswordCode : ICommand 6 | { 7 | } 8 | -------------------------------------------------------------------------------- /src/BuildingBlocks/BuildingBlocks.Abstractions/CQRS/Command/ICreateCommand.cs: -------------------------------------------------------------------------------- 1 | namespace BuildingBlocks.Abstractions.CQRS.Command; 2 | 3 | public interface ICreateCommand : ICommand 4 | where TResponse : notnull 5 | { 6 | } 7 | 8 | public interface ICreateCommand : ICommand 9 | { 10 | } 11 | -------------------------------------------------------------------------------- /src/BuildingBlocks/BuildingBlocks.Abstractions/CQRS/Event/IDomainEventsAccessor.cs: -------------------------------------------------------------------------------- 1 | using BuildingBlocks.Abstractions.CQRS.Event.Internal; 2 | 3 | namespace BuildingBlocks.Abstractions.CQRS.Event; 4 | 5 | public interface IDomainEventsAccessor 6 | { 7 | IReadOnlyList UnCommittedDomainEvents { get; } 8 | } 9 | -------------------------------------------------------------------------------- /src/BuildingBlocks/BuildingBlocks.Abstractions/Messaging/IBus.cs: -------------------------------------------------------------------------------- 1 | namespace BuildingBlocks.Abstractions.Messaging; 2 | 3 | public interface IBus : IBusProducer, IBusConsumer 4 | { 5 | Task StartAsync(CancellationToken cancellationToken = default); 6 | Task StopAsync(CancellationToken cancellationToken = default); 7 | } 8 | -------------------------------------------------------------------------------- /src/BuildingBlocks/BuildingBlocks.Abstractions/Messaging/IIntegrationEventHandler.cs: -------------------------------------------------------------------------------- 1 | using BuildingBlocks.Abstractions.CQRS.Event; 2 | 3 | namespace BuildingBlocks.Abstractions.Messaging; 4 | 5 | public interface IIntegrationEventHandler : IEventHandler 6 | where TEvent : IIntegrationEvent 7 | { 8 | } 9 | -------------------------------------------------------------------------------- /src/BuildingBlocks/BuildingBlocks.Abstractions/Web/MinimalApi/IMinimalEndpointDefinition.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.AspNetCore.Routing; 2 | 3 | namespace BuildingBlocks.Abstractions.Web.MinimalApi; 4 | 5 | public interface IMinimalEndpointDefinition 6 | { 7 | IEndpointRouteBuilder MapEndpoint(IEndpointRouteBuilder builder); 8 | } 9 | -------------------------------------------------------------------------------- /src/BuildingBlocks/BuildingBlocks.Core/IdsGenerator/GuidIdGenerator.cs: -------------------------------------------------------------------------------- 1 | using BuildingBlocks.Abstractions.Core; 2 | 3 | namespace BuildingBlocks.Core.IdsGenerator; 4 | 5 | public class GuidIdGenerator : IIdGenerator 6 | { 7 | public Guid New() 8 | { 9 | return Guid.NewGuid(); 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /src/BuildingBlocks/BuildingBlocks.Security/ApiKey/Authorization/Roles.cs: -------------------------------------------------------------------------------- 1 | namespace BuildingBlocks.Security.ApiKey.Authorization; 2 | 3 | public static class Roles 4 | { 5 | public const string Customer = "Customer"; 6 | public const string Admin = "Admin"; 7 | public const string ThirdParty = "Third Party"; 8 | } 9 | -------------------------------------------------------------------------------- /src/BuildingBlocks/BuildingBlocks.Security/Jwt/CustomClaims.cs: -------------------------------------------------------------------------------- 1 | namespace BuildingBlocks.Security.Jwt; 2 | 3 | public static class CustomClaimTypes 4 | { 5 | public const string Permission = "permission"; 6 | public const string IpAddress = "ipAddress"; 7 | public const string RefreshToken = "refreshToken"; 8 | } 9 | -------------------------------------------------------------------------------- /src/BuildingBlocks/BuildingBlocks.Abstractions/Scheduling/IScheduleExecutor.cs: -------------------------------------------------------------------------------- 1 | using MediatR; 2 | 3 | namespace BuildingBlocks.Abstractions.Scheduling; 4 | 5 | public interface IScheduleExecutor 6 | { 7 | Task Execute(T request) 8 | where T : class, IRequest; 9 | 10 | Task Execute(object request); 11 | } 12 | -------------------------------------------------------------------------------- /src/Modules/Customers/FoodDelivery.Modules.Customers/Customers/Features/GettingCustomerById/GetCustomerByIdResponse.cs: -------------------------------------------------------------------------------- 1 | using FoodDelivery.Modules.Customers.Customers.Dtos; 2 | 3 | namespace FoodDelivery.Modules.Customers.Customers.Features.GettingCustomerById; 4 | 5 | public record GetCustomerByIdResponse(CustomerReadDto Customer); 6 | -------------------------------------------------------------------------------- /src/Modules/Customers/FoodDelivery.Modules.Customers/CustomersConstants.cs: -------------------------------------------------------------------------------- 1 | namespace FoodDelivery.Modules.Customers; 2 | 3 | public static class CustomersConstants 4 | { 5 | public static class Role 6 | { 7 | public const string Admin = "admin"; 8 | public const string User = "user"; 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /src/BuildingBlocks/BuildingBlocks.Abstractions/Persistence/Mongo/IMongoRepository.cs: -------------------------------------------------------------------------------- 1 | using BuildingBlocks.Abstractions.Domain; 2 | 3 | namespace BuildingBlocks.Abstractions.Persistence.Mongo; 4 | 5 | public interface IMongoRepository : IRepository 6 | where TEntity : class, IHaveIdentity 7 | { 8 | } 9 | -------------------------------------------------------------------------------- /src/Modules/Catalogs/FoodDelivery.Modules.Catalogs/Products/Features/ChangingMaxThreshold/ChangeMaxThreshold.cs: -------------------------------------------------------------------------------- 1 | using BuildingBlocks.Abstractions.CQRS.Command; 2 | 3 | namespace FoodDelivery.Modules.Catalogs.Products.Features.ChangingMaxThreshold; 4 | 5 | public record ChangeMaxThreshold(long ProductId, int NewMaxThreshold) : ITxCommand; 6 | -------------------------------------------------------------------------------- /src/Modules/Catalogs/FoodDelivery.Modules.Catalogs/Products/Features/ChangingMaxThreshold/MaxThresholdChanged.cs: -------------------------------------------------------------------------------- 1 | using BuildingBlocks.Core.CQRS.Event.Internal; 2 | 3 | namespace FoodDelivery.Modules.Catalogs.Products.Features.ChangingMaxThreshold; 4 | 5 | public record MaxThresholdChanged(long ProductId, int MaxThreshold) : DomainEvent; 6 | -------------------------------------------------------------------------------- /src/Modules/Catalogs/FoodDelivery.Modules.Catalogs/Products/Features/GettingAvailableStockById/GetAvailableStockById.cs: -------------------------------------------------------------------------------- 1 | using BuildingBlocks.Abstractions.CQRS.Query; 2 | 3 | namespace FoodDelivery.Modules.Catalogs.Products.Features.GettingAvailableStockById; 4 | 5 | public record GetAvailableStockById(long ProductId) : IQuery; 6 | 7 | -------------------------------------------------------------------------------- /src/Modules/Catalogs/FoodDelivery.Modules.Catalogs/Products/Features/GettingProductsView/GetProductsViewRequest.cs: -------------------------------------------------------------------------------- 1 | namespace FoodDelivery.Modules.Catalogs.Products.Features.GettingProductsView; 2 | 3 | public class GetProductsViewRequest 4 | { 5 | public int Page { get; set; } = 1; 6 | public int PageSize { get; set; } = 20; 7 | } 8 | -------------------------------------------------------------------------------- /src/Modules/Identity/FoodDelivery.Modules.Identity/Identity/Features/GenerateRefreshToken/GenerateRefreshTokenResponse.cs: -------------------------------------------------------------------------------- 1 | using FoodDelivery.Modules.Identity.Identity.Dtos; 2 | 3 | namespace FoodDelivery.Modules.Identity.Identity.Features.GenerateRefreshToken; 4 | 5 | public record GenerateRefreshTokenResponse(RefreshTokenDto RefreshToken); 6 | -------------------------------------------------------------------------------- /src/BuildingBlocks/BuildingBlocks.Persistence.Mongo/MongoOptions.cs: -------------------------------------------------------------------------------- 1 | namespace BuildingBlocks.Persistence.Mongo; 2 | 3 | public class MongoOptions 4 | { 5 | public string ConnectionString { get; set; } = null!; 6 | public string DatabaseName { get; set; } = null!; 7 | public static Guid UniqueId { get; set; } = Guid.NewGuid(); 8 | } 9 | -------------------------------------------------------------------------------- /src/Modules/Customers/FoodDelivery.Modules.Customers/Customers/Features/CreatingCustomer/Events/Integration/CustomerCreated.cs: -------------------------------------------------------------------------------- 1 | using BuildingBlocks.Core.Messaging; 2 | 3 | namespace FoodDelivery.Modules.Customers.Customers.Features.CreatingCustomer.Events.Integration; 4 | 5 | public record CustomerCreated(long CustomerId) : IntegrationEvent; 6 | -------------------------------------------------------------------------------- /src/Modules/Customers/FoodDelivery.Modules.Customers/Shared/Clients/Identity/IdentityApiClientOptions.cs: -------------------------------------------------------------------------------- 1 | namespace FoodDelivery.Modules.Customers.Shared.Clients.Identity; 2 | 3 | public class IdentityApiClientOptions 4 | { 5 | public string BaseApiAddress { get; set; } = null!; 6 | public string UsersEndpoint { get; set; } = null!; 7 | } 8 | -------------------------------------------------------------------------------- /src/BuildingBlocks/BuildingBlocks.Abstractions/Domain/IHaveIdentity.cs: -------------------------------------------------------------------------------- 1 | namespace BuildingBlocks.Abstractions.Domain; 2 | 3 | public interface IHaveIdentity : IHaveIdentity 4 | { 5 | new TId Id { get; } 6 | object IHaveIdentity.Id => Id; 7 | } 8 | 9 | public interface IHaveIdentity 10 | { 11 | object Id { get; } 12 | } 13 | -------------------------------------------------------------------------------- /src/Modules/Customers/FoodDelivery.Modules.Customers/RestockSubscriptions/Features/CreatingRestockSubscription/CreateRestockSubscriptionRequest.cs: -------------------------------------------------------------------------------- 1 | namespace FoodDelivery.Modules.Customers.RestockSubscriptions.Features.CreatingRestockSubscription; 2 | 3 | public record CreateRestockSubscriptionRequest(long CustomerId, long ProductId, string Email); 4 | -------------------------------------------------------------------------------- /src/Modules/Customers/FoodDelivery.Modules.Customers/Shared/Clients/Catalogs/CatalogsApiClientOptions.cs: -------------------------------------------------------------------------------- 1 | namespace FoodDelivery.Modules.Customers.Shared.Clients.Catalogs; 2 | 3 | public class CatalogsApiClientOptions 4 | { 5 | public string BaseApiAddress { get; set; } = null!; 6 | public string ProductsEndpoint { get; set; } = null!; 7 | } 8 | -------------------------------------------------------------------------------- /src/Modules/Identity/FoodDelivery.Modules.Identity/Users/Features/UpdatingUserState/UpdateUserStateRequest.cs: -------------------------------------------------------------------------------- 1 | using FoodDelivery.Modules.Identity.Shared.Models; 2 | 3 | namespace FoodDelivery.Modules.Identity.Users.Features.UpdatingUserState; 4 | 5 | public record UpdateUserStateRequest 6 | { 7 | public UserState UserState { get; init; } 8 | } 9 | -------------------------------------------------------------------------------- /src/BuildingBlocks/BuildingBlocks.Abstractions/CQRS/Query/IItemQuery.cs: -------------------------------------------------------------------------------- 1 | namespace BuildingBlocks.Abstractions.CQRS.Query; 2 | 3 | public interface IItemQuery : IQuery 4 | where TId : struct 5 | where TResponse : notnull 6 | { 7 | public IList Includes { get; } 8 | public TId Id { get; } 9 | } 10 | -------------------------------------------------------------------------------- /src/BuildingBlocks/BuildingBlocks.Abstractions/Persistence/EfCore/IRetryDbContextExecution.cs: -------------------------------------------------------------------------------- 1 | namespace BuildingBlocks.Abstractions.Persistence.EfCore; 2 | 3 | public interface IRetryDbContextExecution 4 | { 5 | Task RetryOnExceptionAsync(Func operation); 6 | Task RetryOnExceptionAsync(Func> operation); 7 | } 8 | -------------------------------------------------------------------------------- /src/Modules/Identity/FoodDelivery.Modules.Identity/Users/Features/GettingUsers/GetUsersResponse.cs: -------------------------------------------------------------------------------- 1 | using BuildingBlocks.Core.CQRS.Query; 2 | using FoodDelivery.Modules.Identity.Users.Dtos; 3 | 4 | namespace FoodDelivery.Modules.Identity.Users.Features.GettingUsers; 5 | 6 | public record GetUsersResponse(ListResultModel IdentityUsers); 7 | -------------------------------------------------------------------------------- /src/BuildingBlocks/BuildingBlocks.Core/Persistence/EventStore/StreamEventMetadata.cs: -------------------------------------------------------------------------------- 1 | using BuildingBlocks.Abstractions.Persistence.EventStore; 2 | 3 | namespace BuildingBlocks.Core.Persistence.EventStore; 4 | 5 | public record StreamEventMetadata(string EventId, long StreamPosition) : IStreamEventMetadata 6 | { 7 | public long? LogPosition { get; } 8 | } 9 | -------------------------------------------------------------------------------- /src/Modules/Catalogs/FoodDelivery.Modules.Catalogs/Products/Features/ChangingRestockThreshold/ChangeRestockThreshold.cs: -------------------------------------------------------------------------------- 1 | using BuildingBlocks.Abstractions.CQRS.Command; 2 | 3 | namespace FoodDelivery.Modules.Catalogs.Products.Features.ChangingRestockThreshold; 4 | 5 | public record ChangeRestockThreshold(long ProductId, int NewRestockThreshold) : ITxCommand; 6 | -------------------------------------------------------------------------------- /src/Modules/Catalogs/FoodDelivery.Modules.Catalogs/Products/Features/ChangingRestockThreshold/RestockThresholdChanged.cs: -------------------------------------------------------------------------------- 1 | using BuildingBlocks.Core.CQRS.Event.Internal; 2 | 3 | namespace FoodDelivery.Modules.Catalogs.Products.Features.ChangingRestockThreshold; 4 | 5 | public record RestockThresholdChanged(long ProductId, int RestockThreshold) : DomainEvent; 6 | -------------------------------------------------------------------------------- /src/BuildingBlocks/BuildingBlocks.Abstractions/CQRS/Command/IUpdateCommand.cs: -------------------------------------------------------------------------------- 1 | using MediatR; 2 | 3 | namespace BuildingBlocks.Abstractions.CQRS.Command; 4 | 5 | public interface IUpdateCommand : IUpdateCommand 6 | { 7 | } 8 | 9 | public interface IUpdateCommand : ICommand 10 | where TResponse : notnull 11 | { 12 | } 13 | -------------------------------------------------------------------------------- /src/BuildingBlocks/BuildingBlocks.Core/Domain/Exceptions/ConcurrencyException.cs: -------------------------------------------------------------------------------- 1 | namespace BuildingBlocks.Core.Domain.Exceptions; 2 | 3 | public class ConcurrencyException : DomainException 4 | { 5 | public ConcurrencyException(TId id) 6 | : base($"A different version than expected was found in aggregate {id}") 7 | { 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /src/BuildingBlocks/BuildingBlocks.Core/Exception/Types/ConflictException.cs: -------------------------------------------------------------------------------- 1 | using System.Net; 2 | 3 | namespace BuildingBlocks.Core.Exception.Types; 4 | 5 | public class ConflictException : CustomException 6 | { 7 | public ConflictException(string message) : base(message) 8 | { 9 | StatusCode = HttpStatusCode.Conflict; 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /src/BuildingBlocks/BuildingBlocks.Core/Exception/Types/NotFoundException.cs: -------------------------------------------------------------------------------- 1 | using System.Net; 2 | 3 | namespace BuildingBlocks.Core.Exception.Types; 4 | 5 | public class NotFoundException : CustomException 6 | { 7 | public NotFoundException(string message) : base(message) 8 | { 9 | StatusCode = HttpStatusCode.NotFound; 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /src/BuildingBlocks/BuildingBlocks.Resiliency/BuildingBlocks.Resiliency.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /src/Modules/Catalogs/FoodDelivery.Modules.Catalogs/Products/Features/CreatingProduct/Requests/CreateProductImageRequest.cs: -------------------------------------------------------------------------------- 1 | namespace FoodDelivery.Modules.Catalogs.Products.Features.CreatingProduct.Requests; 2 | 3 | public record CreateProductImageRequest 4 | { 5 | public string ImageUrl { get; init; } = default!; 6 | public bool IsMain { get; init; } 7 | } 8 | -------------------------------------------------------------------------------- /src/Modules/Catalogs/FoodDelivery.Modules.Catalogs/Products/Features/GettingProducts/GetProductsResponse.cs: -------------------------------------------------------------------------------- 1 | using BuildingBlocks.Core.CQRS.Query; 2 | using FoodDelivery.Modules.Catalogs.Products.Dtos; 3 | 4 | namespace FoodDelivery.Modules.Catalogs.Products.Features.GettingProducts; 5 | 6 | public record GetProductsResponse(ListResultModel Products); 7 | -------------------------------------------------------------------------------- /src/Modules/Customers/FoodDelivery.Modules.Customers/Customers/Features/CreatingCustomer/CreateCustomerResponse.cs: -------------------------------------------------------------------------------- 1 | namespace FoodDelivery.Modules.Customers.Customers.Features.CreatingCustomer; 2 | 3 | public record CreateCustomerResponse( 4 | long CustomerId, 5 | string Email, 6 | string FirstName, 7 | string LastName, 8 | Guid IdentityUserId); 9 | -------------------------------------------------------------------------------- /src/BuildingBlocks/BuildingBlocks.Core/Exception/Types/BadRequestException.cs: -------------------------------------------------------------------------------- 1 | using System.Net; 2 | 3 | namespace BuildingBlocks.Core.Exception.Types; 4 | 5 | public class BadRequestException : CustomException 6 | { 7 | public BadRequestException(string message) : base(message) 8 | { 9 | StatusCode = HttpStatusCode.NotFound; 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /src/Modules/Customers/FoodDelivery.Modules.Customers/RestockSubscriptions/Features/DeletingRestockSubscriptionsByTime/DeleteRestockSubscriptionByTimeRequest.cs: -------------------------------------------------------------------------------- 1 | namespace FoodDelivery.Modules.Customers.RestockSubscriptions.Features.DeletingRestockSubscriptionsByTime; 2 | 3 | public record DeleteRestockSubscriptionByTimeRequest(DateTime? From = null, DateTime? To = null); 4 | -------------------------------------------------------------------------------- /src/BuildingBlocks/BuildingBlocks.Caching.InMemory/BuildingBlocks.Caching.InMemory.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /src/BuildingBlocks/BuildingBlocks.Core/Caching/CacheOptions.cs: -------------------------------------------------------------------------------- 1 | namespace BuildingBlocks.Core.Caching; 2 | 3 | public class CacheOptions 4 | { 5 | /// 6 | /// Gets or sets the value indicating the default cache time (in seconds). 7 | /// Default value is 60. 8 | /// 9 | public int DefaultCacheTime { get; set; } = 60; 10 | } 11 | -------------------------------------------------------------------------------- /src/BuildingBlocks/BuildingBlocks.Resiliency/Fallback/IFallbackHandler.cs: -------------------------------------------------------------------------------- 1 | using MediatR; 2 | 3 | namespace BuildingBlocks.Resiliency.Fallback; 4 | 5 | public interface IFallbackHandler 6 | where TRequest : IRequest 7 | { 8 | Task HandleFallbackAsync(TRequest request, CancellationToken cancellationToken); 9 | } 10 | -------------------------------------------------------------------------------- /src/Modules/Customers/FoodDelivery.Modules.Customers/Customers/Data/CustomersDataSeeder.cs: -------------------------------------------------------------------------------- 1 | using BuildingBlocks.Abstractions.Persistence; 2 | 3 | namespace FoodDelivery.Modules.Customers.Customers.Data; 4 | 5 | public class CustomersDataSeeder : IDataSeeder 6 | { 7 | public Task SeedAllAsync() 8 | { 9 | return Task.CompletedTask; 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /src/BuildingBlocks/BuildingBlocks.Core/CQRS/Event/Internal/DomainNotificationEventWrapper.cs: -------------------------------------------------------------------------------- 1 | using BuildingBlocks.Abstractions.CQRS.Event.Internal; 2 | 3 | namespace BuildingBlocks.Core.CQRS.Event.Internal; 4 | 5 | public record DomainNotificationEventWrapper(TDomainEventType DomainEvent) : DomainNotificationEvent 6 | where TDomainEventType : IDomainEvent; 7 | -------------------------------------------------------------------------------- /src/BuildingBlocks/BuildingBlocks.Core/CQRS/Event/Internal/IntegrationEventWrapper.cs: -------------------------------------------------------------------------------- 1 | using BuildingBlocks.Abstractions.CQRS.Event.Internal; 2 | using BuildingBlocks.Core.Messaging; 3 | 4 | namespace BuildingBlocks.Core.CQRS.Event.Internal; 5 | 6 | public record IntegrationEventWrapper 7 | : IntegrationEvent 8 | where TDomainEventType : IDomainEvent; 9 | -------------------------------------------------------------------------------- /src/BuildingBlocks/BuildingBlocks.Persistence.Mongo/BuildingBlocks.Persistence.Mongo.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /src/BuildingBlocks/BuildingBlocks.Security/ApiKey/Authorization/Policies.cs: -------------------------------------------------------------------------------- 1 | namespace BuildingBlocks.Security.ApiKey.Authorization; 2 | 3 | public static class Policies 4 | { 5 | public const string OnlyCustomers = nameof(OnlyCustomers); 6 | public const string OnlyAdmins = nameof(OnlyAdmins); 7 | public const string OnlyThirdParties = nameof(OnlyThirdParties); 8 | } 9 | -------------------------------------------------------------------------------- /src/Modules/Catalogs/FoodDelivery.Modules.Catalogs/CatalogCacheKey.cs: -------------------------------------------------------------------------------- 1 | namespace FoodDelivery.Modules.Catalogs; 2 | 3 | public static class CatalogCacheKey 4 | { 5 | public static string ProductsByCategory(long categoryId) => $"{nameof(ProductsByCategory)}{categoryId}"; 6 | public static string ProductsWithDiscounts(long id) => $"{nameof(ProductsWithDiscounts)}{id}"; 7 | } 8 | -------------------------------------------------------------------------------- /src/Modules/Catalogs/FoodDelivery.Modules.Catalogs/Products/Dtos/ProductImageDto.cs: -------------------------------------------------------------------------------- 1 | namespace FoodDelivery.Modules.Catalogs.Products.Dtos; 2 | 3 | public record ProductImageDto 4 | { 5 | public long Id { get; init; } 6 | public string ImageUrl { get; init; } = default!; 7 | public bool IsMain { get; init; } 8 | public long ProductId { get; init; } 9 | } 10 | -------------------------------------------------------------------------------- /src/Modules/Catalogs/FoodDelivery.Modules.Catalogs/Products/Features/ChangingProductPrice/ProductPriceChanged.cs: -------------------------------------------------------------------------------- 1 | using BuildingBlocks.Core.CQRS.Event.Internal; 2 | using FoodDelivery.Modules.Catalogs.Products.ValueObjects; 3 | 4 | namespace FoodDelivery.Modules.Catalogs.Products.Features.ChangingProductPrice; 5 | 6 | public record ProductPriceChanged(Price Price) : DomainEvent; 7 | -------------------------------------------------------------------------------- /src/Modules/Customers/FoodDelivery.Modules.Customers/Customers/Features/GettingCustomers/GetCustomersResponse.cs: -------------------------------------------------------------------------------- 1 | using BuildingBlocks.Core.CQRS.Query; 2 | using FoodDelivery.Modules.Customers.Customers.Dtos; 3 | 4 | namespace FoodDelivery.Modules.Customers.Customers.Features.GettingCustomers; 5 | 6 | public record GetCustomersResponse(ListResultModel Customers); 7 | -------------------------------------------------------------------------------- /src/Modules/Customers/FoodDelivery.Modules.Customers/Shared/Clients/Identity/Dtos/CreateUserRequest.cs: -------------------------------------------------------------------------------- 1 | namespace FoodDelivery.Modules.Customers.Shared.Clients.Identity.Dtos; 2 | 3 | public record CreateUserRequest( 4 | string UserName, 5 | string Email, 6 | string FirstName, 7 | string LastName, 8 | string Password, 9 | string ConfirmPassword); 10 | -------------------------------------------------------------------------------- /src/BuildingBlocks/BuildingBlocks.Abstractions/CQRS/Command/ITxCommand.cs: -------------------------------------------------------------------------------- 1 | using BuildingBlocks.Abstractions.Persistence; 2 | using MediatR; 3 | 4 | namespace BuildingBlocks.Abstractions.CQRS.Command; 5 | 6 | public interface ITxCommand : ITxCommand 7 | { 8 | } 9 | 10 | public interface ITxCommand : ICommand, ITxRequest 11 | where T : notnull 12 | { 13 | } 14 | -------------------------------------------------------------------------------- /src/BuildingBlocks/BuildingBlocks.Core/Exception/Types/AppException.cs: -------------------------------------------------------------------------------- 1 | using System.Net; 2 | 3 | namespace BuildingBlocks.Core.Exception.Types; 4 | 5 | public class AppException : CustomException 6 | { 7 | public AppException(string message, HttpStatusCode statusCode = HttpStatusCode.BadRequest) : base(message) 8 | { 9 | StatusCode = statusCode; 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /src/BuildingBlocks/BuildingBlocks.Core/Exception/Types/InvalidDateException.cs: -------------------------------------------------------------------------------- 1 | namespace BuildingBlocks.Core.Exception.Types; 2 | 3 | public class InvalidDateException : BadRequestException 4 | { 5 | public DateTime Date { get; } 6 | 7 | public InvalidDateException(DateTime date) : base($"Date: '{date}' is invalid.") 8 | { 9 | Date = date; 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /src/BuildingBlocks/BuildingBlocks.Swagger/Constants.cs: -------------------------------------------------------------------------------- 1 | namespace BuildingBlocks.Swagger; 2 | 3 | public static class Constants 4 | { 5 | public static class ApiKeyConstants 6 | { 7 | public const string HeaderName = "X-Api-Key"; 8 | public const string DefaultScheme = "ApiKey"; 9 | public const string HeaderVersion = "api-version"; 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /src/BuildingBlocks/BuildingBlocks.Web/Plugin/WebApplicationPlugin.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.AspNetCore.Builder; 2 | 3 | namespace BuildingBlocks.Web.Plugin; 4 | 5 | public abstract class WebApplicationPlugin 6 | { 7 | public virtual void ConfigureWebApplicationBuilder(WebApplicationBuilder builder) { } 8 | public virtual void ConfigureWebApplication(WebApplication app) { } 9 | } 10 | -------------------------------------------------------------------------------- /src/Modules/Catalogs/FoodDelivery.Modules.Catalogs/Brands/Exceptions/Domain/BrandDomainException.cs: -------------------------------------------------------------------------------- 1 | using BuildingBlocks.Core.Domain.Exceptions; 2 | 3 | namespace FoodDelivery.Modules.Catalogs.Brands.Exceptions.Domain; 4 | 5 | public class BrandDomainException : DomainException 6 | { 7 | public BrandDomainException(string message) : base(message) 8 | { 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /src/Modules/Catalogs/FoodDelivery.Modules.Catalogs/Products/Features/DebitingProductStock/Events/Integration/ProductStockDebited.cs: -------------------------------------------------------------------------------- 1 | using BuildingBlocks.Core.Messaging; 2 | 3 | namespace FoodDelivery.Modules.Catalogs.Products.Features.DebitingProductStock.Events.Integration; 4 | 5 | public record ProductStockDebited(long ProductId, int NewStock, int DebitedQuantity) : IntegrationEvent; 6 | -------------------------------------------------------------------------------- /src/Modules/Identity/FoodDelivery.Modules.Identity/Identity/Exceptions/PasswordIsInvalidException.cs: -------------------------------------------------------------------------------- 1 | using BuildingBlocks.Core.Exception.Types; 2 | 3 | namespace FoodDelivery.Modules.Identity.Identity.Exceptions; 4 | 5 | public class PasswordIsInvalidException : AppException 6 | { 7 | public PasswordIsInvalidException(string message) : base(message) 8 | { 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /src/BuildingBlocks/BuildingBlocks.Abstractions/CQRS/Query/IPageRequest.cs: -------------------------------------------------------------------------------- 1 | namespace BuildingBlocks.Abstractions.CQRS.Query; 2 | 3 | public interface IPageRequest 4 | { 5 | IList? Includes { get; init; } 6 | IList? Filters { get; init; } 7 | IList? Sorts { get; init; } 8 | int Page { get; init; } 9 | int PageSize { get; init; } 10 | } 11 | -------------------------------------------------------------------------------- /src/BuildingBlocks/BuildingBlocks.Core/Exception/Types/InvalidEmailException.cs: -------------------------------------------------------------------------------- 1 | namespace BuildingBlocks.Core.Exception.Types; 2 | 3 | public class InvalidEmailException : BadRequestException 4 | { 5 | public string Email { get; } 6 | 7 | public InvalidEmailException(string email) : base($"Email: '{email}' is invalid.") 8 | { 9 | Email = email; 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /src/BuildingBlocks/BuildingBlocks.Core/Extensions/ClaimsPrincipalExtensions.cs: -------------------------------------------------------------------------------- 1 | using System.Security.Claims; 2 | 3 | namespace BuildingBlocks.Core.Extensions; 4 | 5 | public static class ClaimsPrincipalExtensions 6 | { 7 | public static string GetClaimValue(this ClaimsPrincipal principal, string type) 8 | { 9 | return principal.FindFirst(type)!.Value; 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /src/Modules/Catalogs/FoodDelivery.Modules.Catalogs/Products/Features/CreatingProduct/Events/Integration/ProductCreated.cs: -------------------------------------------------------------------------------- 1 | using BuildingBlocks.Core.Messaging; 2 | 3 | namespace FoodDelivery.Modules.Catalogs.Products.Features.CreatingProduct.Events.Integration; 4 | 5 | public record ProductCreated(long Id, string Name, long CategoryId, string CategoryName, int Stock) : 6 | IntegrationEvent; 7 | -------------------------------------------------------------------------------- /src/Modules/Identity/FoodDelivery.Modules.Identity/Shared/Models/ApplicationUserRole.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.AspNetCore.Identity; 2 | 3 | namespace FoodDelivery.Modules.Identity.Shared.Models; 4 | 5 | public class ApplicationUserRole : IdentityUserRole 6 | { 7 | public virtual ApplicationUser? User { get; set; } 8 | public virtual ApplicationRole? Role { get; set; } 9 | } 10 | -------------------------------------------------------------------------------- /src/BuildingBlocks/BuildingBlocks.Core/CQRS/Event/NullDomainEventsAccessor.cs: -------------------------------------------------------------------------------- 1 | using BuildingBlocks.Abstractions.CQRS.Event; 2 | using BuildingBlocks.Abstractions.CQRS.Event.Internal; 3 | 4 | namespace BuildingBlocks.Core.CQRS.Event; 5 | 6 | public class NullDomainEventsAccessor : IDomainEventsAccessor 7 | { 8 | public IReadOnlyList UnCommittedDomainEvents { get; } 9 | } 10 | -------------------------------------------------------------------------------- /src/BuildingBlocks/BuildingBlocks.Core/Exception/Types/ApiException.cs: -------------------------------------------------------------------------------- 1 | using System.Net; 2 | 3 | namespace BuildingBlocks.Core.Exception.Types; 4 | 5 | public class ApiException : CustomException 6 | { 7 | public ApiException(string message, HttpStatusCode statusCode = HttpStatusCode.InternalServerError) : base(message) 8 | { 9 | StatusCode = statusCode; 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /src/Modules/Catalogs/FoodDelivery.Modules.Catalogs/Products/Exceptions/Domain/ProductDomainException.cs: -------------------------------------------------------------------------------- 1 | using BuildingBlocks.Core.Domain.Exceptions; 2 | 3 | namespace FoodDelivery.Modules.Catalogs.Products.Exceptions.Domain; 4 | 5 | public class ProductDomainException : DomainException 6 | { 7 | public ProductDomainException(string message) : base(message) 8 | { 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /src/Modules/Identity/FoodDelivery.Modules.Identity/Identity/Exceptions/RequiresTwoFactorException.cs: -------------------------------------------------------------------------------- 1 | using BuildingBlocks.Core.Exception.Types; 2 | 3 | namespace FoodDelivery.Modules.Identity.Identity.Exceptions; 4 | 5 | public class RequiresTwoFactorException : BadRequestException 6 | { 7 | public RequiresTwoFactorException(string message) : base(message) 8 | { 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /src/BuildingBlocks/BuildingBlocks.Abstractions/CQRS/Query/IQuery.cs: -------------------------------------------------------------------------------- 1 | using MediatR; 2 | 3 | namespace BuildingBlocks.Abstractions.CQRS.Query; 4 | 5 | public interface IQuery : IRequest 6 | where T : notnull 7 | { 8 | } 9 | 10 | // https://jimmybogard.com/mediatr-10-0-released/ 11 | public interface IStreamQuery : IStreamRequest 12 | where T : notnull 13 | { 14 | } 15 | -------------------------------------------------------------------------------- /src/BuildingBlocks/BuildingBlocks.Core/Exception/Types/InvalidAmountException.cs: -------------------------------------------------------------------------------- 1 | namespace BuildingBlocks.Core.Exception.Types; 2 | 3 | public class InvalidAmountException : BadRequestException 4 | { 5 | public decimal Amount { get; } 6 | 7 | public InvalidAmountException(decimal amount) : base($"Amount: '{amount}' is invalid.") 8 | { 9 | Amount = amount; 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /src/Modules/Catalogs/FoodDelivery.Modules.Catalogs/Suppliers/Exceptions/Domain/SupplierDomainException.cs: -------------------------------------------------------------------------------- 1 | using BuildingBlocks.Core.Domain.Exceptions; 2 | 3 | namespace FoodDelivery.Modules.Catalogs.Suppliers.Exceptions.Domain; 4 | 5 | public class SupplierDomainException : DomainException 6 | { 7 | public SupplierDomainException(string message) : base(message) 8 | { 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /src/Modules/Catalogs/FoodDelivery.Modules.Catalogs/Suppliers/Supplier.cs: -------------------------------------------------------------------------------- 1 | using BuildingBlocks.Core.Domain; 2 | 3 | namespace FoodDelivery.Modules.Catalogs.Suppliers; 4 | 5 | public class Supplier : Entity 6 | { 7 | public string Name { get; private set; } 8 | 9 | public Supplier(SupplierId id, string name) : base(id) 10 | { 11 | Name = name; 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /src/Modules/Identity/FoodDelivery.Modules.Identity/Identity/Exceptions/UserLockedException.cs: -------------------------------------------------------------------------------- 1 | using BuildingBlocks.Core.Exception.Types; 2 | 3 | namespace FoodDelivery.Modules.Identity.Identity.Exceptions; 4 | 5 | public class UserLockedException : BadRequestException 6 | { 7 | public UserLockedException(string userId) : base($"userId '{userId}' has been locked.") 8 | { 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /tests/shared/Tests.Shared/Probing/Timeout.cs: -------------------------------------------------------------------------------- 1 | namespace Tests.Shared.Probing; 2 | 3 | public class Timeout 4 | { 5 | private readonly DateTime _endTime; 6 | 7 | public Timeout(int duration) 8 | { 9 | _endTime = DateTime.Now.AddMilliseconds(duration); 10 | } 11 | 12 | public bool HasTimedOut() 13 | { 14 | return DateTime.Now > _endTime; 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /src/BuildingBlocks/BuildingBlocks.Abstractions/CQRS/Event/Internal/IDomainEventPublisher.cs: -------------------------------------------------------------------------------- 1 | namespace BuildingBlocks.Abstractions.CQRS.Event.Internal; 2 | 3 | public interface IDomainEventPublisher 4 | { 5 | Task PublishAsync(IDomainEvent domainEvent, CancellationToken cancellationToken = default); 6 | Task PublishAsync(IDomainEvent[] domainEvents, CancellationToken cancellationToken = default); 7 | } 8 | -------------------------------------------------------------------------------- /src/BuildingBlocks/BuildingBlocks.Abstractions/Domain/IEntity.cs: -------------------------------------------------------------------------------- 1 | namespace BuildingBlocks.Abstractions.Domain; 2 | 3 | public interface IEntity : IHaveIdentity, IHaveCreator 4 | { 5 | } 6 | 7 | public interface IEntity : IEntity 8 | where TIdentity : IIdentity 9 | { 10 | } 11 | 12 | public interface IEntity : IEntity 13 | { 14 | } 15 | -------------------------------------------------------------------------------- /src/BuildingBlocks/BuildingBlocks.Core/Exception/Types/InvalidAddressException.cs: -------------------------------------------------------------------------------- 1 | namespace BuildingBlocks.Core.Exception.Types; 2 | 3 | public class InvalidAddressException : BadRequestException 4 | { 5 | public string Address { get; } 6 | 7 | public InvalidAddressException(string address) : base($"Address: '{address}' is invalid.") 8 | { 9 | Address = address; 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /src/BuildingBlocks/BuildingBlocks.Security/ForbiddenProblemDetails.cs: -------------------------------------------------------------------------------- 1 | namespace BuildingBlocks.Security; 2 | 3 | public class ForbiddenProblemDetails : ProblemDetails 4 | { 5 | public ForbiddenProblemDetails(string details = null) 6 | { 7 | Title = "Forbidden"; 8 | Detail = details; 9 | Status = 403; 10 | Type = "https://httpstatuses.com/403"; 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /src/BuildingBlocks/BuildingBlocks.Validation/ValidationException.cs: -------------------------------------------------------------------------------- 1 | namespace BuildingBlocks.Validation; 2 | 3 | public class ValidationException : Exception 4 | { 5 | public ValidationException(ValidationResultModel validationResultModel) 6 | { 7 | ValidationResultModel = validationResultModel; 8 | } 9 | 10 | public ValidationResultModel ValidationResultModel { get; } 11 | } 12 | -------------------------------------------------------------------------------- /src/BuildingBlocks/BuildingBlocks.Web/Extensions/ServiceCollectionExtensions/ServiceCollectionExtensions.Storage.cs: -------------------------------------------------------------------------------- 1 | namespace BuildingBlocks.Web.Extensions.ServiceCollectionExtensions; 2 | 3 | public static partial class ServiceCollectionExtensions 4 | { 5 | public static IServiceCollection AddRequestStorage(this IServiceCollection services) 6 | { 7 | return services; 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /src/Modules/Catalogs/FoodDelivery.Modules.Catalogs/Products/Exceptions/Domain/InsufficientStockException.cs: -------------------------------------------------------------------------------- 1 | using BuildingBlocks.Core.Domain.Exceptions; 2 | 3 | namespace FoodDelivery.Modules.Catalogs.Products.Exceptions.Domain; 4 | 5 | public class InsufficientStockException : DomainException 6 | { 7 | public InsufficientStockException(string message) : base(message) 8 | { 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /src/Modules/Catalogs/FoodDelivery.Modules.Catalogs/Products/Features/ReplenishingProductStock/Events/Integration/ProductStockReplenished.cs: -------------------------------------------------------------------------------- 1 | using BuildingBlocks.Core.Messaging; 2 | 3 | namespace FoodDelivery.Modules.Catalogs.Products.Features.ReplenishingProductStock.Events.Integration; 4 | 5 | public record ProductStockReplenished(long ProductId, int NewStock, int ReplenishedQuantity) : IntegrationEvent; 6 | -------------------------------------------------------------------------------- /src/BuildingBlocks/BuildingBlocks.Abstractions/Messaging/IMessageHandler.cs: -------------------------------------------------------------------------------- 1 | using BuildingBlocks.Abstractions.Messaging.Context; 2 | 3 | namespace BuildingBlocks.Abstractions.Messaging; 4 | 5 | public interface IMessageHandler 6 | where TMessage : class, IMessage 7 | { 8 | Task HandleAsync(IConsumeContext messageContext, CancellationToken cancellationToken = default); 9 | } 10 | -------------------------------------------------------------------------------- /src/BuildingBlocks/BuildingBlocks.Security/Jwt/RolePolicy.cs: -------------------------------------------------------------------------------- 1 | namespace BuildingBlocks.Security.Jwt; 2 | 3 | public class RolePolicy 4 | { 5 | public RolePolicy(string name, IReadOnlyList? roles) 6 | { 7 | Name = name; 8 | Roles = roles ?? new List(); 9 | } 10 | 11 | public string Name { get; set; } 12 | public IReadOnlyList Roles { get; set; } 13 | } -------------------------------------------------------------------------------- /src/BuildingBlocks/BuildingBlocks.Validation/ValidationError.cs: -------------------------------------------------------------------------------- 1 | namespace BuildingBlocks.Validation; 2 | 3 | public class ValidationError 4 | { 5 | public ValidationError(string field, string message) 6 | { 7 | Field = field != string.Empty ? field : null; 8 | Message = message; 9 | } 10 | 11 | public string? Field { get; } 12 | 13 | public string Message { get; } 14 | } -------------------------------------------------------------------------------- /src/Modules/Customers/FoodDelivery.Modules.Customers/Products/Exceptions/ProductNotFoundException.cs: -------------------------------------------------------------------------------- 1 | using BuildingBlocks.Core.Exception.Types; 2 | 3 | namespace FoodDelivery.Modules.Customers.Products.Exceptions; 4 | 5 | public class ProductNotFoundException : NotFoundException 6 | { 7 | public ProductNotFoundException(long id) : base($"Product with id {id} not found") 8 | { 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /src/Modules/Orders/FoodDelivery.Modules.Orders/Shared/Contracts/IOrdersDbContext.cs: -------------------------------------------------------------------------------- 1 | using BuildingBlocks.Abstractions.Persistence.EfCore; 2 | using FoodDelivery.Modules.Orders.Orders.Models; 3 | using Microsoft.EntityFrameworkCore; 4 | 5 | namespace FoodDelivery.Modules.Orders.Shared.Contracts; 6 | 7 | public interface IOrdersDbContext : IDbContext 8 | { 9 | public DbSet Orders { get; } 10 | } 11 | -------------------------------------------------------------------------------- /src/BuildingBlocks/BuildingBlocks.Core/Exception/Types/InvalidCurrencyException.cs: -------------------------------------------------------------------------------- 1 | namespace BuildingBlocks.Core.Exception.Types; 2 | 3 | public class InvalidCurrencyException : BadRequestException 4 | { 5 | public string Currency { get; } 6 | 7 | public InvalidCurrencyException(string currency) : base($"Currency: '{currency}' is invalid.") 8 | { 9 | Currency = currency; 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /src/Modules/Customers/FoodDelivery.Modules.Customers/RestockSubscriptions/Features/CreatingRestockSubscription/CreateRestockSubscriptionResponse.cs: -------------------------------------------------------------------------------- 1 | using FoodDelivery.Modules.Customers.RestockSubscriptions.Dtos; 2 | 3 | namespace FoodDelivery.Modules.Customers.RestockSubscriptions.Features.CreatingRestockSubscription; 4 | 5 | public record CreateRestockSubscriptionResponse(RestockSubscriptionDto RestockSubscription); 6 | -------------------------------------------------------------------------------- /src/Modules/Identity/FoodDelivery.Modules.Identity/Shared/Data/DbContextDesignFactory.cs: -------------------------------------------------------------------------------- 1 | using BuildingBlocks.Persistence.EfCore.Postgres; 2 | 3 | namespace FoodDelivery.Modules.Identity.Shared.Data; 4 | 5 | public class DbContextDesignFactory : DbContextDesignFactoryBase 6 | { 7 | public DbContextDesignFactory() : base("Identity", "Identity:PostgresOptions") 8 | { 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /src/BuildingBlocks/BuildingBlocks.Abstractions/Persistence/ITxDbContextExecution.cs: -------------------------------------------------------------------------------- 1 | namespace BuildingBlocks.Abstractions.Persistence; 2 | 3 | public interface ITxDbContextExecution 4 | { 5 | public Task ExecuteTransactionalAsync(Func action, CancellationToken cancellationToken = default); 6 | public Task ExecuteTransactionalAsync(Func> action, CancellationToken cancellationToken = default); 7 | } 8 | -------------------------------------------------------------------------------- /src/BuildingBlocks/BuildingBlocks.Security/UnauthorizedProblemDetails.cs: -------------------------------------------------------------------------------- 1 | namespace BuildingBlocks.Security; 2 | 3 | public class UnauthorizedProblemDetails : ProblemDetails 4 | { 5 | public UnauthorizedProblemDetails(string details = null) 6 | { 7 | Title = "Unauthorized"; 8 | Detail = details; 9 | Status = 401; 10 | Type = "https://httpstatuses.com/401"; 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /src/Modules/Catalogs/FoodDelivery.Modules.Catalogs/Shared/Extensions/ApplicationBuilderExtensions/Infrastructure.cs: -------------------------------------------------------------------------------- 1 | namespace FoodDelivery.Modules.Catalogs.Shared.Extensions.ApplicationBuilderExtensions; 2 | 3 | public static partial class ApplicationBuilderExtensions 4 | { 5 | public static IApplicationBuilder UseInfrastructure(this IApplicationBuilder app) 6 | { 7 | return app; 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /src/Modules/Customers/FoodDelivery.Modules.Customers/Shared/Extensions/ApplicationBuilderExtensions/Infrastructure.cs: -------------------------------------------------------------------------------- 1 | namespace FoodDelivery.Modules.Customers.Shared.Extensions.ApplicationBuilderExtensions; 2 | 3 | public static partial class ApplicationBuilderExtensions 4 | { 5 | public static IApplicationBuilder UseInfrastructure(this IApplicationBuilder app) 6 | { 7 | return app; 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /src/Modules/Identity/FoodDelivery.Modules.Identity/Users/Features/RegisteringUser/RegisterIdentityUserException.cs: -------------------------------------------------------------------------------- 1 | using BuildingBlocks.Core.Exception.Types; 2 | 3 | namespace FoodDelivery.Modules.Identity.Users.Features.RegisteringUser; 4 | 5 | public class RegisterIdentityUserException : BadRequestException 6 | { 7 | public RegisterIdentityUserException(string error) : base(error) 8 | { 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /src/Modules/Orders/FoodDelivery.Modules.Orders/Shared/Data/OrdersDbContextDesignFactory.cs: -------------------------------------------------------------------------------- 1 | using BuildingBlocks.Persistence.EfCore.Postgres; 2 | 3 | namespace FoodDelivery.Modules.Orders.Shared.Data; 4 | 5 | public class OrdersDbContextDesignFactory : DbContextDesignFactoryBase 6 | { 7 | public OrdersDbContextDesignFactory() : base("Orders", "Orders:PostgresOptions") 8 | { 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /src/BuildingBlocks/BuildingBlocks.Abstractions/Domain/IAggregate.cs: -------------------------------------------------------------------------------- 1 | namespace BuildingBlocks.Abstractions.Domain; 2 | 3 | public interface IAggregate : IEntity, IHaveAggregate 4 | { 5 | } 6 | 7 | public interface IAggregate : IAggregate 8 | where TIdentity : Identity 9 | { 10 | } 11 | 12 | public interface IAggregate : IAggregate 13 | { 14 | } 15 | -------------------------------------------------------------------------------- /src/BuildingBlocks/BuildingBlocks.Core/Exception/Types/IdentityException.cs: -------------------------------------------------------------------------------- 1 | using System.Net; 2 | 3 | namespace BuildingBlocks.Core.Exception.Types; 4 | 5 | public class IdentityException : CustomException 6 | { 7 | public IdentityException(string message, List errors = default, HttpStatusCode statusCode = HttpStatusCode.BadRequest) 8 | : base(message, statusCode, errors) 9 | { 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /src/Modules/Catalogs/FoodDelivery.Modules.Catalogs/Products/Exceptions/Domain/MaxStockThresholdReachedException.cs: -------------------------------------------------------------------------------- 1 | using BuildingBlocks.Core.Domain.Exceptions; 2 | 3 | namespace FoodDelivery.Modules.Catalogs.Products.Exceptions.Domain; 4 | 5 | public class MaxStockThresholdReachedException : DomainException 6 | { 7 | public MaxStockThresholdReachedException(string message) : base(message) 8 | { 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /src/Modules/Customers/FoodDelivery.Modules.Customers/RestockSubscriptions/Features/CreatingRestockSubscription/Events/Integration/RestockSubscriptionCreated.cs: -------------------------------------------------------------------------------- 1 | using BuildingBlocks.Core.Messaging; 2 | 3 | namespace FoodDelivery.Modules.Customers.RestockSubscriptions.Features.CreatingRestockSubscription.Events.Integration; 4 | 5 | public record RestockSubscriptionCreated(long CustomerId, string? Email) : IntegrationEvent; 6 | 7 | -------------------------------------------------------------------------------- /src/Modules/Customers/FoodDelivery.Modules.Customers/RestockSubscriptions/Features/GettingRestockSubscriptionById/GetRestockSubscriptionByIdResponse.cs: -------------------------------------------------------------------------------- 1 | using FoodDelivery.Modules.Customers.RestockSubscriptions.Dtos; 2 | 3 | namespace FoodDelivery.Modules.Customers.RestockSubscriptions.Features.GettingRestockSubscriptionById; 4 | 5 | public record GetRestockSubscriptionByIdResponse(RestockSubscriptionDto RestockSubscription); 6 | -------------------------------------------------------------------------------- /src/BuildingBlocks/BuildingBlocks.Abstractions/CQRS/Command/ITxCreateCommand.cs: -------------------------------------------------------------------------------- 1 | using BuildingBlocks.Abstractions.Persistence; 2 | using MediatR; 3 | 4 | namespace BuildingBlocks.Abstractions.CQRS.Command; 5 | 6 | public interface ITxCreateCommand : ICommand, ITxRequest 7 | where TResponse : notnull 8 | { 9 | } 10 | 11 | public interface ITxCreateCommand : ITxCreateCommand 12 | { 13 | } 14 | -------------------------------------------------------------------------------- /src/BuildingBlocks/BuildingBlocks.Abstractions/Persistence/ITransactionAble.cs: -------------------------------------------------------------------------------- 1 | namespace BuildingBlocks.Abstractions.Persistence; 2 | 3 | public interface ITransactionAble 4 | { 5 | Task BeginTransactionAsync(CancellationToken cancellationToken = default); 6 | Task RollbackTransactionAsync(CancellationToken cancellationToken = default); 7 | Task CommitTransactionAsync(CancellationToken cancellationToken = default); 8 | } 9 | -------------------------------------------------------------------------------- /src/BuildingBlocks/BuildingBlocks.Security/ApiKey/ApiKeyAuthenticationOptions.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.AspNetCore.Authentication; 2 | 3 | namespace BuildingBlocks.Security.ApiKey; 4 | 5 | public class ApiKeyAuthenticationOptions : AuthenticationSchemeOptions 6 | { 7 | public const string DefaultScheme = "ApiKey"; 8 | public string AuthenticationType = DefaultScheme; 9 | public string Scheme => DefaultScheme; 10 | } 11 | -------------------------------------------------------------------------------- /src/Modules/Catalogs/FoodDelivery.Modules.Catalogs/Shared/Data/DbContextDesignFactory.cs: -------------------------------------------------------------------------------- 1 | using BuildingBlocks.Persistence.EfCore.Postgres; 2 | 3 | namespace FoodDelivery.Modules.Catalogs.Shared.Data; 4 | 5 | public class CatalogDbContextDesignFactory : DbContextDesignFactoryBase 6 | { 7 | public CatalogDbContextDesignFactory() : base("Catalogs", "Catalogs:PostgresOptions") 8 | { 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /src/Modules/Identity/FoodDelivery.Modules.Identity/Constants.cs: -------------------------------------------------------------------------------- 1 | namespace FoodDelivery.Modules.Identity; 2 | 3 | public static class Constants 4 | { 5 | public static class Role 6 | { 7 | public const string Admin = "admin"; 8 | public const string User = "user"; 9 | } 10 | 11 | public static string IdentityRoleName => "http://schemas.microsoft.com/ws/2008/06/identity/claims/role"; 12 | } 13 | -------------------------------------------------------------------------------- /src/Modules/Identity/FoodDelivery.Modules.Identity/Shared/Models/PasswordResetCode.cs: -------------------------------------------------------------------------------- 1 | namespace FoodDelivery.Modules.Identity.Shared.Models; 2 | 3 | public class PasswordResetCode 4 | { 5 | public Guid Id { get; set; } 6 | 7 | public string Email { get; set; } 8 | 9 | public string Code { get; set; } 10 | 11 | public DateTime SentAt { get; set; } 12 | 13 | public DateTime? UsedAt { get; set; } 14 | } 15 | -------------------------------------------------------------------------------- /src/Modules/Identity/FoodDelivery.Modules.Identity/Users/Features/UpdatingUserState/Events/Integration/UserStateUpdated.cs: -------------------------------------------------------------------------------- 1 | using BuildingBlocks.Core.Messaging; 2 | using FoodDelivery.Modules.Identity.Shared.Models; 3 | 4 | namespace FoodDelivery.Modules.Identity.Users.Features.UpdatingUserState.Events.Integration; 5 | 6 | public record UserStateUpdated(Guid UserId, UserState OldUserState, UserState NewUserState) : IntegrationEvent; 7 | -------------------------------------------------------------------------------- /src/BuildingBlocks/BuildingBlocks.Abstractions/CQRS/Command/ITxUpdateCommand.cs: -------------------------------------------------------------------------------- 1 | using BuildingBlocks.Abstractions.Persistence; 2 | using MediatR; 3 | 4 | namespace BuildingBlocks.Abstractions.CQRS.Command; 5 | 6 | public interface ITxUpdateCommand : IUpdateCommand, ITxRequest 7 | where TResponse : notnull 8 | { 9 | } 10 | 11 | public interface ITxUpdateCommand : ITxUpdateCommand 12 | { 13 | } 14 | -------------------------------------------------------------------------------- /src/BuildingBlocks/BuildingBlocks.Abstractions/CQRS/Event/Internal/IDomainNotificationEvent.cs: -------------------------------------------------------------------------------- 1 | namespace BuildingBlocks.Abstractions.CQRS.Event.Internal; 2 | 3 | public interface IDomainNotificationEvent : IDomainNotificationEvent 4 | where TDomainEventType : IDomainEvent 5 | { 6 | TDomainEventType DomainEvent { get; set; } 7 | } 8 | 9 | public interface IDomainNotificationEvent : IEvent 10 | { 11 | } 12 | -------------------------------------------------------------------------------- /src/BuildingBlocks/BuildingBlocks.Resiliency/Retry/IRetryableRequest.cs: -------------------------------------------------------------------------------- 1 | using MediatR; 2 | 3 | namespace BuildingBlocks.Resiliency.Retry; 4 | 5 | public interface IRetryableRequest 6 | where TRequest : IRequest 7 | { 8 | int RetryAttempts => 1; 9 | int RetryDelay => 250; 10 | bool RetryWithExponentialBackoff => false; 11 | int ExceptionsAllowedBeforeCircuitTrip => 1; 12 | } 13 | -------------------------------------------------------------------------------- /src/BuildingBlocks/BuildingBlocks.Logging/LoggerOptions.cs: -------------------------------------------------------------------------------- 1 | namespace BuildingBlocks.Logging; 2 | 3 | public class LoggerOptions 4 | { 5 | public string? Level { get; set; } 6 | public string? SeqUrl { get; set; } 7 | public string? ElasticSearchUrl { get; set; } 8 | public string? LogTemplate { get; set; } 9 | public string? DevelopmentLogPath { get; set; } 10 | public string? ProductionLogPath { get; set; } 11 | } -------------------------------------------------------------------------------- /src/Modules/Customers/FoodDelivery.Modules.Customers/Shared/Clients/Identity/Dtos/UserIdentityDto.cs: -------------------------------------------------------------------------------- 1 | namespace FoodDelivery.Modules.Customers.Shared.Clients.Identity.Dtos; 2 | 3 | public class UserIdentityDto 4 | { 5 | public Guid Id { get; set; } 6 | public string UserName { get; set; } 7 | public string Email { get; set; } 8 | public string FirstName { get; set; } 9 | public string LastName { get; set; } 10 | } 11 | -------------------------------------------------------------------------------- /src/Modules/Customers/FoodDelivery.Modules.Customers/Shared/Data/DbContextDesignFactory.cs: -------------------------------------------------------------------------------- 1 | using BuildingBlocks.Persistence.EfCore.Postgres; 2 | 3 | namespace FoodDelivery.Modules.Customers.Shared.Data; 4 | 5 | public class CustomerDbContextDesignFactory : DbContextDesignFactoryBase 6 | { 7 | public CustomerDbContextDesignFactory() : base("Customers", "Customers:PostgresOptions") 8 | { 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /src/BuildingBlocks/BuildingBlocks.Abstractions/Persistence/EventStore/Projections/IHaveReadProjection.cs: -------------------------------------------------------------------------------- 1 | using BuildingBlocks.Abstractions.CQRS.Event.Internal; 2 | 3 | namespace BuildingBlocks.Abstractions.Persistence.EventStore.Projections; 4 | 5 | public interface IHaveReadProjection 6 | { 7 | Task ProjectAsync(IStreamEvent streamEvent, CancellationToken cancellationToken = default) 8 | where T : IDomainEvent; 9 | } 10 | -------------------------------------------------------------------------------- /src/BuildingBlocks/BuildingBlocks.Core/Exception/Types/InvalidPhoneNumberException.cs: -------------------------------------------------------------------------------- 1 | namespace BuildingBlocks.Core.Exception.Types; 2 | 3 | public class InvalidPhoneNumberException : BadRequestException 4 | { 5 | public string PhoneNumber { get; } 6 | 7 | public InvalidPhoneNumberException(string phoneNumber) : base($"PhoneNumber: '{phoneNumber}' is invalid.") 8 | { 9 | PhoneNumber = phoneNumber; 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /.dockerignore: -------------------------------------------------------------------------------- 1 | **/.dockerignore 2 | **/.env 3 | **/.git 4 | **/.gitignore 5 | **/.project 6 | **/.settings 7 | **/.toolstarget 8 | **/.vs 9 | **/.vscode 10 | **/.idea 11 | **/*.*proj.user 12 | **/*.dbmdl 13 | **/*.jfm 14 | **/azds.yaml 15 | **/charts 16 | **/docker-compose* 17 | **/Dockerfile* 18 | **/node_modules 19 | **/npm-debug.log 20 | **/secrets.dev.yaml 21 | **/values.dev.yaml 22 | **/bin/ 23 | **/obj/ 24 | **/out/ 25 | LICENSE 26 | README.md -------------------------------------------------------------------------------- /src/Modules/Catalogs/FoodDelivery.Modules.Catalogs/Brands/BrandId.cs: -------------------------------------------------------------------------------- 1 | using BuildingBlocks.Abstractions.Domain; 2 | 3 | namespace FoodDelivery.Modules.Catalogs.Brands; 4 | 5 | public record BrandId : AggregateId 6 | { 7 | public BrandId(long value) : base(value) 8 | { 9 | } 10 | 11 | public static implicit operator long(BrandId id) => id.Value; 12 | 13 | public static implicit operator BrandId(long id) => new(id); 14 | } 15 | -------------------------------------------------------------------------------- /src/Modules/Catalogs/FoodDelivery.Modules.Catalogs/Products/Features/DebitingProductStock/Events/Domain/ProductStockDebited.cs: -------------------------------------------------------------------------------- 1 | using BuildingBlocks.Core.CQRS.Event.Internal; 2 | using FoodDelivery.Modules.Catalogs.Products.ValueObjects; 3 | 4 | namespace FoodDelivery.Modules.Catalogs.Products.Features.DebitingProductStock.Events.Domain; 5 | 6 | public record ProductStockDebited(ProductId ProductId, Stock NewStock, int DebitedQuantity) : DomainEvent; 7 | -------------------------------------------------------------------------------- /src/Modules/Identity/FoodDelivery.Modules.Identity/Identity/Exceptions/InvalidTokenException.cs: -------------------------------------------------------------------------------- 1 | using System.Security.Claims; 2 | using BuildingBlocks.Core.Exception.Types; 3 | 4 | namespace FoodDelivery.Modules.Identity.Identity.Exceptions; 5 | 6 | public class InvalidTokenException : BadRequestException 7 | { 8 | public InvalidTokenException(ClaimsPrincipal? claimsPrincipal) : base("access_token is invalid!") 9 | { 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /src/BuildingBlocks/BuildingBlocks.Abstractions/Persistence/EfCore/IPageRepository.cs: -------------------------------------------------------------------------------- 1 | using BuildingBlocks.Abstractions.Domain; 2 | 3 | namespace BuildingBlocks.Abstractions.Persistence.EfCore; 4 | 5 | public interface IPageRepository 6 | where TEntity : IHaveIdentity 7 | { 8 | } 9 | 10 | public interface IPageRepository : IPageRepository 11 | where TEntity : IHaveIdentity 12 | { 13 | } 14 | -------------------------------------------------------------------------------- /src/BuildingBlocks/BuildingBlocks.Security/Jwt/ClaimPolicy.cs: -------------------------------------------------------------------------------- 1 | using System.Security.Claims; 2 | 3 | namespace BuildingBlocks.Security.Jwt; 4 | 5 | public class ClaimPolicy 6 | { 7 | public ClaimPolicy(string name, IReadOnlyList? claims) 8 | { 9 | Name = name; 10 | Claims = claims ?? new List(); 11 | } 12 | 13 | public string Name { get; set; } 14 | public IReadOnlyList Claims { get; set; } 15 | } -------------------------------------------------------------------------------- /src/Modules/Customers/FoodDelivery.Modules.Customers/RestockSubscriptions/Exceptions/Domain/RestockSubscriptionDomainException.cs: -------------------------------------------------------------------------------- 1 | using BuildingBlocks.Core.Domain.Exceptions; 2 | 3 | namespace FoodDelivery.Modules.Customers.RestockSubscriptions.Exceptions.Domain; 4 | 5 | public class RestockSubscriptionDomainException : DomainException 6 | { 7 | public RestockSubscriptionDomainException(string message) : base(message) 8 | { 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /src/Modules/Identity/FoodDelivery.Modules.Identity/Identity/Exceptions/PhoneNumberNotConfirmedException.cs: -------------------------------------------------------------------------------- 1 | using BuildingBlocks.Core.Exception.Types; 2 | 3 | namespace FoodDelivery.Modules.Identity.Identity.Exceptions; 4 | 5 | public class PhoneNumberNotConfirmedException : BadRequestException 6 | { 7 | public PhoneNumberNotConfirmedException(string phone) : base($"The phone number '{phone}' is not confirmed yet.") 8 | { 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /src/BuildingBlocks/BuildingBlocks.Abstractions/Domain/IAuditableEntity.cs: -------------------------------------------------------------------------------- 1 | namespace BuildingBlocks.Abstractions.Domain; 2 | 3 | public interface IAuditableEntity : IEntity, IHaveAudit 4 | { 5 | } 6 | 7 | public interface IAuditableEntity : IAuditableEntity 8 | where TIdentity : Identity 9 | { 10 | } 11 | 12 | public interface IAuditableEntity : IAuditableEntity, long> 13 | { 14 | } 15 | -------------------------------------------------------------------------------- /src/BuildingBlocks/BuildingBlocks.Abstractions/CQRS/Command/IDeleteCommand.cs: -------------------------------------------------------------------------------- 1 | namespace BuildingBlocks.Abstractions.CQRS.Command; 2 | 3 | public interface IDeleteCommand : ICommand 4 | where TId : struct 5 | where TResponse : notnull 6 | { 7 | public TId Id { get; init; } 8 | } 9 | 10 | public interface IDeleteCommand : ICommand 11 | where TId : struct 12 | { 13 | public TId Id { get; init; } 14 | } 15 | -------------------------------------------------------------------------------- /tests/shared/Tests.Shared/Probing/IProbe.cs: -------------------------------------------------------------------------------- 1 | namespace Tests.Shared.Probing; 2 | 3 | // Ref: https://github.com/kgrzybek/modular-monolith-with-ddd/ 4 | public interface IProbe 5 | { 6 | bool IsSatisfied(); 7 | 8 | Task SampleAsync(); 9 | 10 | string DescribeFailureTo(); 11 | } 12 | 13 | public interface IProbe 14 | { 15 | bool IsSatisfied(T sample); 16 | 17 | Task GetSampleAsync(); 18 | 19 | string DescribeFailureTo(); 20 | } 21 | -------------------------------------------------------------------------------- /src/Modules/Catalogs/FoodDelivery.Modules.Catalogs/Products/Features/ReplenishingProductStock/Events/Domain/ProductStockReplenished.cs: -------------------------------------------------------------------------------- 1 | using BuildingBlocks.Core.CQRS.Event.Internal; 2 | using FoodDelivery.Modules.Catalogs.Products.ValueObjects; 3 | 4 | namespace FoodDelivery.Modules.Catalogs.Products.Features.ReplenishingProductStock.Events.Domain; 5 | 6 | public record ProductStockReplenished(ProductId ProductId, Stock NewStock, int ReplenishedQuantity) : DomainEvent; 7 | -------------------------------------------------------------------------------- /src/Modules/Catalogs/FoodDelivery.Modules.Catalogs/Suppliers/SupplierId.cs: -------------------------------------------------------------------------------- 1 | using BuildingBlocks.Abstractions.Domain; 2 | 3 | namespace FoodDelivery.Modules.Catalogs.Suppliers; 4 | 5 | public record SupplierId : AggregateId 6 | { 7 | public SupplierId(long value) : base(value) 8 | { 9 | } 10 | 11 | public static implicit operator long(SupplierId id) => id.Value; 12 | 13 | public static implicit operator SupplierId(long id) => new(id); 14 | } 15 | -------------------------------------------------------------------------------- /src/Modules/Customers/FoodDelivery.Modules.Customers/RestockSubscriptions/Features/GettingRestockSubscriptions/GetRestockSubscriptionsResponse.cs: -------------------------------------------------------------------------------- 1 | using BuildingBlocks.Core.CQRS.Query; 2 | using FoodDelivery.Modules.Customers.RestockSubscriptions.Dtos; 3 | 4 | namespace FoodDelivery.Modules.Customers.RestockSubscriptions.Features.GettingRestockSubscriptions; 5 | 6 | public record GetRestockSubscriptionsResponse(ListResultModel RestockSubscriptions); 7 | -------------------------------------------------------------------------------- /src/Modules/Identity/FoodDelivery.Modules.Identity/Users/Features/RegisteringUser/Events/Integration/UserRegistered.cs: -------------------------------------------------------------------------------- 1 | using BuildingBlocks.Core.Messaging; 2 | 3 | namespace FoodDelivery.Modules.Identity.Users.Features.RegisteringUser.Events.Integration; 4 | 5 | public record UserRegistered( 6 | Guid IdentityId, 7 | string Email, 8 | string UserName, 9 | string FirstName, 10 | string LastName, 11 | List? Roles) : IntegrationEvent; 12 | -------------------------------------------------------------------------------- /src/BuildingBlocks/BuildingBlocks.Abstractions/Domain/IIdentity.cs: -------------------------------------------------------------------------------- 1 | namespace BuildingBlocks.Abstractions.Domain; 2 | 3 | /// 4 | /// Super type for all Store.Services.Identity types with generic Id. 5 | /// 6 | /// The generic identifier. 7 | public interface IIdentity 8 | { 9 | /// 10 | /// Gets the generic identifier. 11 | /// 12 | public TId Value { get; } 13 | } 14 | -------------------------------------------------------------------------------- /src/Modules/Catalogs/FoodDelivery.Modules.Catalogs/Categories/CategoryId.cs: -------------------------------------------------------------------------------- 1 | using BuildingBlocks.Abstractions.Domain; 2 | 3 | namespace FoodDelivery.Modules.Catalogs.Categories; 4 | 5 | public record CategoryId : AggregateId 6 | { 7 | public CategoryId(long value) : base(value) 8 | { 9 | } 10 | 11 | public static implicit operator long(CategoryId id) => id.Value; 12 | 13 | public static implicit operator CategoryId(long id) => new(id); 14 | } 15 | -------------------------------------------------------------------------------- /tests/modules/Customers/FoodDelivery.Modules.Customers.UnitTests/FoodDelivery.Modules.Customers.UnitTests.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | PreserveNewest 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | -------------------------------------------------------------------------------- /tests/modules/Identity/FoodDelivery.Modules.Identity.UnitTests/FoodDelivery.Modules.Identity.UnitTests.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | PreserveNewest 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | -------------------------------------------------------------------------------- /src/Api/FoodDelivery.Api/Extensions/ApplicationBuilderExtensions/ApplicationBuilderExtensions.Monitoring.cs: -------------------------------------------------------------------------------- 1 | using BuildingBlocks.Monitoring; 2 | 3 | namespace FoodDelivery.Api.Extensions.ApplicationBuilderExtensions; 4 | 5 | public static partial class ApplicationBuilderExtensions 6 | { 7 | public static IApplicationBuilder UseFoodDeliveryMonitoring(this IApplicationBuilder app) 8 | { 9 | app.UseMonitoring(); 10 | 11 | return app; 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /src/BuildingBlocks/BuildingBlocks.Abstractions/Core/IExclusiveLock.cs: -------------------------------------------------------------------------------- 1 | namespace BuildingBlocks.Abstractions.Core; 2 | 3 | public interface IExclusiveLock : IDisposable 4 | { 5 | Task AquireAsync(object obj, CancellationToken token = default); 6 | Task ReleaseAsync(object obj); 7 | void Execute(T obj, Action action, CancellationToken token = default); 8 | Task ExecuteAsync(T obj, Func func, CancellationToken token = default); 9 | } 10 | -------------------------------------------------------------------------------- /src/Modules/Catalogs/FoodDelivery.Modules.Catalogs/Products/Features/ChangingProductBrand/Events/Domain/ProductBrandChanged.cs: -------------------------------------------------------------------------------- 1 | using BuildingBlocks.Core.CQRS.Event.Internal; 2 | using FoodDelivery.Modules.Catalogs.Brands; 3 | using FoodDelivery.Modules.Catalogs.Products.ValueObjects; 4 | 5 | namespace FoodDelivery.Modules.Catalogs.Products.Features.ChangingProductBrand.Events.Domain; 6 | 7 | internal record ProductBrandChanged(BrandId BrandId, ProductId ProductId) : DomainEvent; 8 | -------------------------------------------------------------------------------- /src/Modules/Identity/FoodDelivery.Modules.Identity/Identity/Features/VerifyEmail/Exceptions/EmailAlreadyVerifiedException.cs: -------------------------------------------------------------------------------- 1 | using BuildingBlocks.Core.Exception.Types; 2 | 3 | namespace FoodDelivery.Modules.Identity.Identity.Features.VerifyEmail.Exceptions; 4 | 5 | public class EmailAlreadyVerifiedException : ConflictException 6 | { 7 | public EmailAlreadyVerifiedException(string email) : base($"User with email {email} already verified.") 8 | { 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /src/Modules/Identity/FoodDelivery.Modules.Identity/Shared/Models/EmailVerificationCode.cs: -------------------------------------------------------------------------------- 1 | namespace FoodDelivery.Modules.Identity.Shared.Models 2 | { 3 | public class EmailVerificationCode 4 | { 5 | public Guid Id { get; set; } 6 | 7 | public string Email { get; set; } 8 | 9 | public string Code { get; set; } 10 | 11 | public DateTime SentAt { get; set; } 12 | 13 | public DateTime? UsedAt { get; set; } 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /tests/modules/Identity/FoodDelivery.Modules.Identity.EndToEndTests/FoodDelivery.Modules.Identity.EndToEndTests.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | PreserveNewest 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | -------------------------------------------------------------------------------- /src/BuildingBlocks/BuildingBlocks.Core/Web/AppOptions.cs: -------------------------------------------------------------------------------- 1 | namespace BuildingBlocks.Core.Web; 2 | 3 | public class AppOptions 4 | { 5 | public string? Name { get; set; } 6 | public string? ApiAddress { get; set; } 7 | public string? Instance { get; set; } 8 | public string? Version { get; set; } 9 | public bool DisplayBanner { get; set; } = true; 10 | public bool DisplayVersion { get; set; } = true; 11 | public string? Description { get; set; } 12 | } 13 | -------------------------------------------------------------------------------- /src/Modules/Catalogs/FoodDelivery.Modules.Catalogs/Products/Features/ChangingProductSupplier/Events/ProductSupplierChanged.cs: -------------------------------------------------------------------------------- 1 | using BuildingBlocks.Core.CQRS.Event.Internal; 2 | using FoodDelivery.Modules.Catalogs.Products.ValueObjects; 3 | using FoodDelivery.Modules.Catalogs.Suppliers; 4 | 5 | namespace FoodDelivery.Modules.Catalogs.Products.Features.ChangingProductSupplier.Events; 6 | 7 | public record ProductSupplierChanged(SupplierId SupplierId, ProductId ProductId) : DomainEvent; 8 | -------------------------------------------------------------------------------- /src/Modules/Catalogs/migrations.bat: -------------------------------------------------------------------------------- 1 | 2 | IF "%1"=="init-context" dotnet ef migrations add InitialCatalogMigration -o \FoodDelivery.Modules.Catalogs\Shared\Data\Migrations\Catalogs --project .\FoodDelivery.Modules.Catalogs\FoodDelivery.Modules.Catalogs.csproj -c CatalogDbContext --verbose & goto exit 3 | IF "%1"=="update-context" dotnet ef database update -c CatalogDbContext --verbose --project .\FoodDelivery.Modules.Catalogs\FoodDelivery.Modules.Catalogs.csproj & goto exit 4 | 5 | :exit 6 | -------------------------------------------------------------------------------- /src/Modules/Customers/FoodDelivery.Modules.Customers/Customers/Exceptions/Domain/InvalidNameException.cs: -------------------------------------------------------------------------------- 1 | using BuildingBlocks.Core.Exception.Types; 2 | 3 | namespace FoodDelivery.Modules.Customers.Customers.Exceptions.Domain; 4 | 5 | public class InvalidNameException : BadRequestException 6 | { 7 | public string Name { get; } 8 | 9 | public InvalidNameException(string name) : base($"Name: '{name}' is invalid.") 10 | { 11 | Name = name; 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /src/Modules/Identity/migrations.bat: -------------------------------------------------------------------------------- 1 | 2 | IF "%1"=="init-context" dotnet ef migrations add InitialIdentityServerMigration -o \FoodDelivery.Modules.Identity\Shared\Data\Migrations\Identity --project .\FoodDelivery.Modules.Identity\FoodDelivery.Modules.Identity.csproj -c IdentityContext --verbose & goto exit 3 | IF "%1"=="update-context" dotnet ef database update -c IdentityContext --verbose --project .\FoodDelivery.Modules.Identity\FoodDelivery.Modules.Identity.csproj & goto exit 4 | :exit 5 | -------------------------------------------------------------------------------- /tests/modules/Customers/FoodDelivery.Modules.Customers.EndToEndTests/FoodDelivery.Modules.Customers.EndToEndTests.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | PreserveNewest 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | -------------------------------------------------------------------------------- /src/Modules/Catalogs/FoodDelivery.Modules.Catalogs/Products/Features/ChangingProductCategory/Events/ProductCategoryChanged.cs: -------------------------------------------------------------------------------- 1 | using BuildingBlocks.Core.CQRS.Event.Internal; 2 | using FoodDelivery.Modules.Catalogs.Categories; 3 | using FoodDelivery.Modules.Catalogs.Products.ValueObjects; 4 | 5 | namespace FoodDelivery.Modules.Catalogs.Products.Features.ChangingProductCategory.Events; 6 | 7 | public record ProductCategoryChanged(CategoryId CategoryId, ProductId ProductId) : 8 | DomainEvent; 9 | -------------------------------------------------------------------------------- /src/Modules/Identity/FoodDelivery.Modules.Identity/Identity/Exceptions/RefreshTokenNotFoundException.cs: -------------------------------------------------------------------------------- 1 | using BuildingBlocks.Core.Exception.Types; 2 | using FoodDelivery.Modules.Identity.Shared.Models; 3 | 4 | namespace FoodDelivery.Modules.Identity.Identity.Exceptions; 5 | 6 | public class RefreshTokenNotFoundException : NotFoundException 7 | { 8 | public RefreshTokenNotFoundException(RefreshToken? refreshToken) : base("Refresh token not found.") 9 | { 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /.github/multi-labeler.yml: -------------------------------------------------------------------------------- 1 | version: v1 2 | 3 | labels: 4 | - label: "feat" 5 | matcher: 6 | title: "^feat: .*" 7 | commits: "^feat: .*" 8 | 9 | - label: "fix" 10 | matcher: 11 | title: "^fix: .*" 12 | commits: "^fix: .*" 13 | 14 | - label: "chore" 15 | matcher: 16 | title: "^chore: .*" 17 | commits: "^chore: .*" 18 | 19 | - label: "docs" 20 | matcher: 21 | title: "^docs: .*" 22 | commits: "^docs: .*" -------------------------------------------------------------------------------- /src/Modules/Catalogs/FoodDelivery.Modules.Catalogs/Brands/GuardExtensions.cs: -------------------------------------------------------------------------------- 1 | using Ardalis.GuardClauses; 2 | using FoodDelivery.Modules.Catalogs.Brands.Exceptions.Application; 3 | 4 | namespace FoodDelivery.Modules.Catalogs.Brands; 5 | 6 | public static class GuardExtensions 7 | { 8 | public static void ExistsBrand(this IGuardClause guardClause, bool exists, long brandId) 9 | { 10 | if (!exists) 11 | throw new BrandNotFoundException(brandId); 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /src/Modules/Customers/migrations.bat: -------------------------------------------------------------------------------- 1 | 2 | IF "%1"=="init-context" dotnet ef migrations add InitialCustomersMigration -o \FoodDelivery.Modules.Customer\Shared\Data\Migrations\Customer --project .\FoodDelivery.Modules.Customers\FoodDelivery.Modules.Customers.csproj -c CustomersDbContext --verbose & goto exit 3 | IF "%1"=="update-context" dotnet ef database update -c CustomersDbContext --verbose --project .\FoodDelivery.Modules.Customers\FoodDelivery.Modules.Customers.csproj & goto exit 4 | 5 | :exit 6 | -------------------------------------------------------------------------------- /src/Modules/Identity/FoodDelivery.Modules.Identity/Users/Features/RegisteringUser/RegisterUserRequest.cs: -------------------------------------------------------------------------------- 1 | namespace FoodDelivery.Modules.Identity.Users.Features.RegisteringUser; 2 | 3 | public record RegisterUserRequest( 4 | string FirstName, 5 | string LastName, 6 | string UserName, 7 | string Email, 8 | string Password, 9 | string ConfirmPassword) 10 | { 11 | public IEnumerable Roles { get; init; } = new List { Constants.Role.User }; 12 | } 13 | -------------------------------------------------------------------------------- /.github/workflows/lint-pr.yml: -------------------------------------------------------------------------------- 1 | # https://github.com/amannn/action-semantic-pull-request 2 | name: "Lint Conventional Commit PR" 3 | 4 | on: 5 | pull_request_target: 6 | types: 7 | - opened 8 | - edited 9 | - synchronize 10 | 11 | jobs: 12 | main: 13 | name: Validate PR title 14 | runs-on: ubuntu-latest 15 | steps: 16 | - uses: amannn/action-semantic-pull-request@v5 17 | env: 18 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} -------------------------------------------------------------------------------- /src/BuildingBlocks/BuildingBlocks.Abstractions/CQRS/Command/ICommandHandler.cs: -------------------------------------------------------------------------------- 1 | using MediatR; 2 | 3 | namespace BuildingBlocks.Abstractions.CQRS.Command; 4 | 5 | public interface ICommandHandler : ICommandHandler 6 | where TCommand : ICommand 7 | { 8 | } 9 | 10 | public interface ICommandHandler : IRequestHandler 11 | where TCommand : ICommand 12 | where TResponse : notnull 13 | { 14 | } 15 | -------------------------------------------------------------------------------- /src/BuildingBlocks/BuildingBlocks.Core/Caching/CacheKey.cs: -------------------------------------------------------------------------------- 1 | using BuildingBlocks.Core.Extensions; 2 | 3 | namespace BuildingBlocks.Core.Caching; 4 | 5 | public static class CacheKey 6 | { 7 | public static string With(params string[] keys) 8 | { 9 | return string.Join("-", keys); 10 | } 11 | 12 | public static string With(Type ownerType, params string[] keys) 13 | { 14 | return With($"{ownerType.GetCacheKey()}:{string.Join("-", keys)}"); 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /src/BuildingBlocks/BuildingBlocks.Resiliency/Retry/HttpPolicyBuilders.cs: -------------------------------------------------------------------------------- 1 | using System.Net; 2 | using Polly; 3 | using Polly.Extensions.Http; 4 | 5 | namespace BuildingBlocks.Resiliency.Retry; 6 | 7 | public static class HttpPolicyBuilders 8 | { 9 | public static PolicyBuilder GetBaseBuilder() 10 | { 11 | return HttpPolicyExtensions.HandleTransientHttpError() 12 | .OrResult(msg => msg.StatusCode == HttpStatusCode.BadRequest); 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /src/Modules/Customers/FoodDelivery.Modules.Customers/Customers/Exceptions/Domain/CustomerDomainException.cs: -------------------------------------------------------------------------------- 1 | using System.Net; 2 | using BuildingBlocks.Core.Domain.Exceptions; 3 | 4 | namespace FoodDelivery.Modules.Customers.Customers.Exceptions.Domain; 5 | 6 | public class CustomerDomainException : DomainException 7 | { 8 | public CustomerDomainException(string message, HttpStatusCode statusCode = HttpStatusCode.BadRequest) : 9 | base(message, statusCode) 10 | { 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /src/Modules/Identity/FoodDelivery.Modules.Identity/Identity/Exceptions/EmailNotConfirmedException.cs: -------------------------------------------------------------------------------- 1 | using BuildingBlocks.Core.Exception.Types; 2 | 3 | namespace FoodDelivery.Modules.Identity.Identity.Exceptions; 4 | 5 | public class EmailNotConfirmedException : BadRequestException 6 | { 7 | public EmailNotConfirmedException(string email) : base($"Email not confirmed for email address `{email}`") 8 | { 9 | Email = email; 10 | } 11 | 12 | public string Email { get; } 13 | } 14 | -------------------------------------------------------------------------------- /src/Modules/Catalogs/FoodDelivery.Modules.Catalogs/Products/Features/ChangingProductCategory/Events/ProductCategoryChangedNotification.cs: -------------------------------------------------------------------------------- 1 | using BuildingBlocks.Core.CQRS.Event.Internal; 2 | using FoodDelivery.Modules.Catalogs.Categories; 3 | using FoodDelivery.Modules.Catalogs.Products.ValueObjects; 4 | 5 | namespace FoodDelivery.Modules.Catalogs.Products.Features.ChangingProductCategory.Events; 6 | 7 | public record ProductCategoryChangedNotification(CategoryId CategoryId, ProductId ProductId) : DomainEvent; 8 | -------------------------------------------------------------------------------- /src/Modules/Customers/FoodDelivery.Modules.Customers/RestockSubscriptions/Exceptions/Application/RestockSubscriptionNotFoundException.cs: -------------------------------------------------------------------------------- 1 | using BuildingBlocks.Core.Exception.Types; 2 | 3 | namespace FoodDelivery.Modules.Customers.RestockSubscriptions.Exceptions.Application; 4 | 5 | public class RestockSubscriptionNotFoundException : NotFoundException 6 | { 7 | public RestockSubscriptionNotFoundException(long id) : base("RestockSubscription with id: " + id + " not found") 8 | { 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /src/BuildingBlocks/BuildingBlocks.Abstractions/CQRS/Event/Internal/IDomainNotificationEventPublisher.cs: -------------------------------------------------------------------------------- 1 | namespace BuildingBlocks.Abstractions.CQRS.Event.Internal; 2 | 3 | public interface IDomainNotificationEventPublisher 4 | { 5 | Task PublishAsync(IDomainNotificationEvent domainNotificationEvent, CancellationToken cancellationToken = default); 6 | 7 | Task PublishAsync( 8 | IDomainNotificationEvent[] domainNotificationEvents, 9 | CancellationToken cancellationToken = default); 10 | } 11 | -------------------------------------------------------------------------------- /src/BuildingBlocks/BuildingBlocks.Abstractions/Domain/IHaveAggregateVersion.cs: -------------------------------------------------------------------------------- 1 | namespace BuildingBlocks.Abstractions.Domain; 2 | 3 | public interface IHaveAggregateVersion 4 | { 5 | /// 6 | /// The original version is the aggregate version we got from the store. This is used to ensure optimistic concurrency, 7 | /// to check if there were no changes made to the aggregate state between load and save for the current operation. 8 | /// 9 | long OriginalVersion { get; } 10 | } 11 | -------------------------------------------------------------------------------- /src/Modules/Catalogs/FoodDelivery.Modules.Catalogs/Brands/Exceptions/Application/BrandNotFoundException.cs: -------------------------------------------------------------------------------- 1 | using BuildingBlocks.Core.Exception.Types; 2 | 3 | namespace FoodDelivery.Modules.Catalogs.Brands.Exceptions.Application; 4 | 5 | public class BrandNotFoundException : NotFoundException 6 | { 7 | public BrandNotFoundException(long id) : base($"Brand with id '{id}' not found") 8 | { 9 | } 10 | 11 | public BrandNotFoundException(string message) : base(message) 12 | { 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /src/Modules/Catalogs/FoodDelivery.Modules.Catalogs/Products/GuardExtensions.cs: -------------------------------------------------------------------------------- 1 | using Ardalis.GuardClauses; 2 | using FoodDelivery.Modules.Catalogs.Products.Exceptions.Application; 3 | 4 | namespace FoodDelivery.Modules.Catalogs.Products; 5 | 6 | public static class GuardExtensions 7 | { 8 | public static void ExistsProduct(this IGuardClause guardClause, bool exists, long productId) 9 | { 10 | if (exists == false) 11 | throw new ProductNotFoundException(productId); 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /.github/workflows/release-please.yml: -------------------------------------------------------------------------------- 1 | # https://github.com/google-github-actions/release-please-action#how-release-please-works 2 | # https://www.conventionalcommits.org/en/v1.0.0/ 3 | on: 4 | push: 5 | branches: 6 | - main 7 | name: release-please 8 | jobs: 9 | release-please: 10 | runs-on: ubuntu-latest 11 | steps: 12 | - uses: google-github-actions/release-please-action@v3 13 | with: 14 | release-type: go 15 | package-name: release-please-action 16 | -------------------------------------------------------------------------------- /src/BuildingBlocks/BuildingBlocks.Validation/BuildingBlocks.Validation.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | -------------------------------------------------------------------------------- /src/Modules/Catalogs/FoodDelivery.Modules.Catalogs/Categories/Exceptions/Domain/CategoryDomainException.cs: -------------------------------------------------------------------------------- 1 | using BuildingBlocks.Core.Domain.Exceptions; 2 | 3 | namespace FoodDelivery.Modules.Catalogs.Categories.Exceptions.Domain; 4 | 5 | public class CategoryDomainException : DomainException 6 | { 7 | public CategoryDomainException(string message) : base(message) 8 | { 9 | } 10 | 11 | public CategoryDomainException(long id) : base($"Category with id: '{id}' not found.") 12 | { 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /src/Modules/Catalogs/FoodDelivery.Modules.Catalogs/Suppliers/GuardExtensions.cs: -------------------------------------------------------------------------------- 1 | using Ardalis.GuardClauses; 2 | using FoodDelivery.Modules.Catalogs.Suppliers.Exceptions.Application; 3 | 4 | namespace FoodDelivery.Modules.Catalogs.Suppliers; 5 | 6 | public static class GuardExtensions 7 | { 8 | public static void ExistsSupplier(this IGuardClause guardClause, bool exists, long supplierId) 9 | { 10 | if (exists == false) 11 | throw new SupplierNotFoundException(supplierId); 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /src/Modules/Identity/FoodDelivery.Modules.Identity/Identity/Features/Login/LoginFailedException.cs: -------------------------------------------------------------------------------- 1 | using BuildingBlocks.Core.Exception.Types; 2 | 3 | namespace FoodDelivery.Modules.Identity.Identity.Features.Login; 4 | 5 | public class LoginFailedException : AppException 6 | { 7 | public LoginFailedException(string userNameOrEmail) : base($"Login failed for username: {userNameOrEmail}") 8 | { 9 | UserNameOrEmail = userNameOrEmail; 10 | } 11 | 12 | public string UserNameOrEmail { get; } 13 | } 14 | -------------------------------------------------------------------------------- /src/Modules/Identity/FoodDelivery.Modules.Identity/Identity/Features/VerifyEmail/Exceptions/VerificationTokenIsInvalidException.cs: -------------------------------------------------------------------------------- 1 | using BuildingBlocks.Core.Exception.Types; 2 | 3 | namespace FoodDelivery.Modules.Identity.Identity.Features.VerifyEmail.Exceptions; 4 | 5 | public class VerificationTokenIsInvalidException : BadRequestException 6 | { 7 | public VerificationTokenIsInvalidException(string userId) : base( 8 | $"verification token is invalid for userId '{userId}'.") 9 | { 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /src/BuildingBlocks/BuildingBlocks.Core/CQRS/Command/InternalCommand.cs: -------------------------------------------------------------------------------- 1 | using BuildingBlocks.Abstractions.CQRS.Command; 2 | using BuildingBlocks.Core.Types; 3 | 4 | namespace BuildingBlocks.Core.CQRS.Command; 5 | 6 | public abstract record InternalCommand : IInternalCommand 7 | { 8 | public Guid Id { get; protected set; } = Guid.NewGuid(); 9 | 10 | public DateTime OccurredOn { get; protected set; } = DateTime.Now; 11 | 12 | public string Type { get { return TypeMapper.GetFullTypeName(GetType()); } } 13 | } 14 | -------------------------------------------------------------------------------- /src/BuildingBlocks/BuildingBlocks.Core/CQRS/Query/PageRequest.cs: -------------------------------------------------------------------------------- 1 | using BuildingBlocks.Abstractions.CQRS; 2 | using BuildingBlocks.Abstractions.CQRS.Query; 3 | 4 | namespace BuildingBlocks.Core.CQRS.Query; 5 | 6 | public record PageRequest : IPageRequest 7 | { 8 | public int Page { get; init; } = 1; 9 | public int PageSize { get; init; } = 20; 10 | public IList? Includes { get; init; } 11 | public IList? Filters { get; init; } 12 | public IList? Sorts { get; init; } 13 | } 14 | -------------------------------------------------------------------------------- /src/Modules/Catalogs/FoodDelivery.Modules.Catalogs/Categories/GuardExtensions.cs: -------------------------------------------------------------------------------- 1 | using Ardalis.GuardClauses; 2 | using FoodDelivery.Modules.Catalogs.Categories.Exceptions.Application; 3 | 4 | namespace FoodDelivery.Modules.Catalogs.Categories; 5 | 6 | public static class GuardExtensions 7 | { 8 | public static void ExistsCategory(this IGuardClause guardClause, bool exists, long categoryId) 9 | { 10 | if (exists == false) 11 | throw new CategoryNotFoundException(categoryId); 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /src/Modules/Catalogs/FoodDelivery.Modules.Catalogs/Products/Exceptions/Application/ProductNotFoundException.cs: -------------------------------------------------------------------------------- 1 | using BuildingBlocks.Core.Exception.Types; 2 | 3 | namespace FoodDelivery.Modules.Catalogs.Products.Exceptions.Application; 4 | 5 | public class ProductNotFoundException : NotFoundException 6 | { 7 | public ProductNotFoundException(long id) : base($"Product with id '{id}' not found") 8 | { 9 | } 10 | 11 | public ProductNotFoundException(string message) : base(message) 12 | { 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /src/Modules/Customers/FoodDelivery.Modules.Customers/Shared/Contracts/ICustomersDbContext.cs: -------------------------------------------------------------------------------- 1 | using FoodDelivery.Modules.Customers.Customers.Models; 2 | using Microsoft.EntityFrameworkCore; 3 | 4 | namespace FoodDelivery.Modules.Customers.Shared.Contracts; 5 | 6 | public interface ICustomersDbContext 7 | { 8 | DbSet Set() 9 | where TEntity : class; 10 | 11 | public DbSet Customers { get; } 12 | Task SaveChangesAsync(CancellationToken cancellationToken = default); 13 | } 14 | -------------------------------------------------------------------------------- /src/BuildingBlocks/BuildingBlocks.Abstractions/Caching/ICacheProvider.cs: -------------------------------------------------------------------------------- 1 | namespace BuildingBlocks.Abstractions.Caching; 2 | 3 | public interface ICacheProvider 4 | { 5 | Task GetAsync(string key); 6 | T? Get(string key); 7 | Task SetAsync(string key, object data, int? cacheTime = null); 8 | void Set(string key, object data, int? cacheTime = null); 9 | Task IsSetAsync(string key); 10 | bool IsSet(string key); 11 | Task RemoveAsync(string key); 12 | void Remove(string key); 13 | } 14 | -------------------------------------------------------------------------------- /src/BuildingBlocks/BuildingBlocks.Core/Domain/Exceptions/DomainException.cs: -------------------------------------------------------------------------------- 1 | using System.Net; 2 | using BuildingBlocks.Core.Exception.Types; 3 | 4 | namespace BuildingBlocks.Core.Domain.Exceptions; 5 | 6 | /// 7 | /// Exception type for domain exceptions. 8 | /// 9 | public class DomainException : CustomException 10 | { 11 | public DomainException(string message, HttpStatusCode statusCode = HttpStatusCode.BadRequest) : base(message) 12 | { 13 | StatusCode = statusCode; 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /src/Modules/Customers/FoodDelivery.Modules.Customers/Customers/Exceptions/Domain/CustomerNotActiveException.cs: -------------------------------------------------------------------------------- 1 | using BuildingBlocks.Core.Exception.Types; 2 | 3 | namespace FoodDelivery.Modules.Customers.Customers.Exceptions.Domain; 4 | 5 | internal class CustomerNotActiveException : AppException 6 | { 7 | public long CustomerId { get; } 8 | 9 | public CustomerNotActiveException(long customerId) : base($"Customer with ID: '{customerId}' is not active.") 10 | { 11 | CustomerId = customerId; 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /src/BuildingBlocks/BuildingBlocks.Abstractions/Scheduling/ICommandScheduler.cs: -------------------------------------------------------------------------------- 1 | using BuildingBlocks.Abstractions.CQRS.Command; 2 | 3 | namespace BuildingBlocks.Abstractions.Scheduling; 4 | 5 | public interface ICommandScheduler 6 | { 7 | Task ScheduleAsync( 8 | IInternalCommand internalCommandCommand, 9 | CancellationToken cancellationToken = default); 10 | 11 | Task ScheduleAsync( 12 | IInternalCommand[] internalCommandCommands, 13 | CancellationToken cancellationToken = default); 14 | } 15 | -------------------------------------------------------------------------------- /src/Modules/Catalogs/FoodDelivery.Modules.Catalogs/Categories/Exceptions/Application/CategoryNotFoundException.cs: -------------------------------------------------------------------------------- 1 | using BuildingBlocks.Core.Exception.Types; 2 | 3 | namespace FoodDelivery.Modules.Catalogs.Categories.Exceptions.Application; 4 | 5 | public class CategoryNotFoundException : NotFoundException 6 | { 7 | public CategoryNotFoundException(long id) : base($"Category with id '{id}' not found.") 8 | { 9 | } 10 | 11 | public CategoryNotFoundException(string message) : base(message) 12 | { 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /src/Modules/Catalogs/FoodDelivery.Modules.Catalogs/Suppliers/Exceptions/Application/SupplierNotFoundException.cs: -------------------------------------------------------------------------------- 1 | using BuildingBlocks.Core.Exception.Types; 2 | 3 | namespace FoodDelivery.Modules.Catalogs.Suppliers.Exceptions.Application; 4 | 5 | public class SupplierNotFoundException : NotFoundException 6 | { 7 | public SupplierNotFoundException(long id) : base($"Supplier with id '{id}' not found") 8 | { 9 | } 10 | 11 | public SupplierNotFoundException(string message) : base(message) 12 | { 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /src/BuildingBlocks/BuildingBlocks.Abstractions/CQRS/Query/IQueryProcessor.cs: -------------------------------------------------------------------------------- 1 | namespace BuildingBlocks.Abstractions.CQRS.Query; 2 | 3 | public interface IQueryProcessor 4 | { 5 | Task SendAsync(IQuery query, CancellationToken cancellationToken = default) 6 | where TResponse : notnull; 7 | 8 | IAsyncEnumerable SendAsync( 9 | IStreamQuery query, 10 | CancellationToken cancellationToken = default) 11 | where TResponse : notnull; 12 | } 13 | -------------------------------------------------------------------------------- /src/BuildingBlocks/BuildingBlocks.Email/BuildingBlocks.Email.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | -------------------------------------------------------------------------------- /src/Modules/Customers/FoodDelivery.Modules.Customers/Customers/Exceptions/Application/CustomerNotFoundException.cs: -------------------------------------------------------------------------------- 1 | using BuildingBlocks.Core.Exception.Types; 2 | 3 | namespace FoodDelivery.Modules.Customers.Customers.Exceptions.Application; 4 | 5 | public class CustomerNotFoundException : NotFoundException 6 | { 7 | public CustomerNotFoundException(string message) : base(message) 8 | { 9 | } 10 | 11 | public CustomerNotFoundException(long id) : base($"Customer with id '{id}' not found.") 12 | { 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /src/Modules/Customers/FoodDelivery.Modules.Customers/Customers/Exceptions/Domain/InvalidNationalityException.cs: -------------------------------------------------------------------------------- 1 | using BuildingBlocks.Core.Exception.Types; 2 | 3 | namespace FoodDelivery.Modules.Customers.Customers.Exceptions.Domain; 4 | 5 | public class InvalidNationalityException : BadRequestException 6 | { 7 | public string Nationality { get; } 8 | 9 | public InvalidNationalityException(string nationality) : base($"Nationality: '{nationality}' is invalid.") 10 | { 11 | Nationality = nationality; 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /src/Modules/Customers/FoodDelivery.Modules.Customers/Products/ProductId.cs: -------------------------------------------------------------------------------- 1 | using Ardalis.GuardClauses; 2 | using BuildingBlocks.Abstractions.Domain; 3 | 4 | namespace FoodDelivery.Modules.Customers.Products; 5 | 6 | public record ProductId : AggregateId 7 | { 8 | public ProductId(long value) : base(value) 9 | { 10 | } 11 | 12 | public static implicit operator long(ProductId id) => Guard.Against.Null(id.Value, nameof(id.Value)); 13 | 14 | public static implicit operator ProductId(long id) => new(id); 15 | } 16 | -------------------------------------------------------------------------------- /src/BuildingBlocks/BuildingBlocks.Web/SlugifyParameterTransformer.cs: -------------------------------------------------------------------------------- 1 | using System.Text.RegularExpressions; 2 | using Microsoft.AspNetCore.Routing; 3 | 4 | namespace BuildingBlocks.Web; 5 | 6 | public class SlugifyParameterTransformer : IOutboundParameterTransformer 7 | { 8 | public string TransformOutbound(object value) 9 | { 10 | // Slugify value 11 | return value == null 12 | ? null 13 | : Regex.Replace(value.ToString() ?? string.Empty, "([a-z])([A-Z])", "$1-$2").ToLower(); 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /src/Modules/Identity/FoodDelivery.Modules.Identity/Identity/Features/RefreshingToken/InvalidRefreshTokenException.cs: -------------------------------------------------------------------------------- 1 | using BuildingBlocks.Core.Exception.Types; 2 | using FoodDelivery.Modules.Identity.Shared.Models; 3 | 4 | namespace FoodDelivery.Modules.Identity.Identity.Features.RefreshingToken; 5 | 6 | public class InvalidRefreshTokenException : BadRequestException 7 | { 8 | public InvalidRefreshTokenException(Shared.Models.RefreshToken? refreshToken) : base($"refresh token {refreshToken?.Token} is invalid!") 9 | { 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /src/Modules/Identity/FoodDelivery.Modules.Identity/Shared/Exceptions/UserNotFoundException.cs: -------------------------------------------------------------------------------- 1 | using BuildingBlocks.Core.Exception.Types; 2 | 3 | namespace FoodDelivery.Modules.Identity.Shared.Exceptions; 4 | 5 | public class UserNotFoundException : NotFoundException 6 | { 7 | public UserNotFoundException(string emailOrUserName) : base($"User with email or username: '{emailOrUserName}' not found.") 8 | { 9 | } 10 | 11 | public UserNotFoundException(Guid id) : base($"User with id: '{id}' not found.") 12 | { 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /src/BuildingBlocks/BuildingBlocks.Abstractions/Domain/EventSourcing/IHaveEventSourcedAggregateVersion.cs: -------------------------------------------------------------------------------- 1 | namespace BuildingBlocks.Abstractions.Domain.EventSourcing; 2 | 3 | public interface IHaveEventSourcedAggregateVersion : IHaveAggregateVersion 4 | { 5 | /// 6 | /// The current version is set to original version when the aggregate is loaded from the store. 7 | /// It should increase for each state transition performed within the scope of the current operation. 8 | /// 9 | long CurrentVersion { get; } 10 | } 11 | -------------------------------------------------------------------------------- /src/BuildingBlocks/BuildingBlocks.Abstractions/Persistence/EventStore/Projections/IReadProjectionPublisher.cs: -------------------------------------------------------------------------------- 1 | using BuildingBlocks.Abstractions.CQRS.Event.Internal; 2 | 3 | namespace BuildingBlocks.Abstractions.Persistence.EventStore.Projections; 4 | 5 | public interface IReadProjectionPublisher 6 | { 7 | Task PublishAsync(IStreamEvent streamEvent, CancellationToken cancellationToken = default); 8 | 9 | Task PublishAsync(IStreamEvent streamEvent, CancellationToken cancellationToken = default) 10 | where T : IDomainEvent; 11 | } 12 | -------------------------------------------------------------------------------- /src/Modules/Customers/FoodDelivery.Modules.Customers/Customers/Exceptions/Domain/UnsupportedNationalityException.cs: -------------------------------------------------------------------------------- 1 | using BuildingBlocks.Core.Exception.Types; 2 | 3 | namespace FoodDelivery.Modules.Customers.Customers.Exceptions.Domain; 4 | 5 | public class UnsupportedNationalityException : BadRequestException 6 | { 7 | public string Nationality { get; } 8 | 9 | public UnsupportedNationalityException(string nationality) : base($"Nationality: '{nationality}' is unsupported.") 10 | { 11 | Nationality = nationality; 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /src/BuildingBlocks/BuildingBlocks.Abstractions/Persistence/EventStore/ExpectedStreamVersion.cs: -------------------------------------------------------------------------------- 1 | namespace BuildingBlocks.Abstractions.Persistence.EventStore; 2 | 3 | public record ExpectedStreamVersion(long Value) 4 | { 5 | public static readonly ExpectedStreamVersion NoStream = new(-1); 6 | public static readonly ExpectedStreamVersion Any = new(-2); 7 | } 8 | 9 | public record StreamReadPosition(long Value) 10 | { 11 | public static readonly StreamReadPosition Start = new(0L); 12 | } 13 | 14 | public record StreamTruncatePosition(long Value); 15 | -------------------------------------------------------------------------------- /src/BuildingBlocks/BuildingBlocks.Core/Extensions/ObjectExtensions.cs: -------------------------------------------------------------------------------- 1 | using System.Web; 2 | 3 | namespace BuildingBlocks.Core.Extensions; 4 | 5 | public static class ObjectExtensions 6 | { 7 | public static string GetQueryString(this object obj) 8 | { 9 | var properties = from p in obj.GetType().GetProperties() 10 | where p.GetValue(obj, null) != null 11 | select p.Name + "=" + HttpUtility.UrlEncode(p.GetValue(obj, null).ToString()); 12 | 13 | return string.Join("&", properties.ToArray()); 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /src/BuildingBlocks/BuildingBlocks.Abstractions/Persistence/IUnitOfWork.cs: -------------------------------------------------------------------------------- 1 | namespace BuildingBlocks.Abstractions.Persistence; 2 | 3 | /// 4 | /// The unit of work pattern. 5 | /// 6 | public interface IUnitOfWork : IDisposable 7 | { 8 | Task BeginTransactionAsync(CancellationToken cancellationToken = default); 9 | Task CommitAsync(CancellationToken cancellationToken = default); 10 | } 11 | 12 | public interface IUnitOfWork : IUnitOfWork 13 | where TContext : class 14 | { 15 | TContext Context { get; } 16 | } 17 | -------------------------------------------------------------------------------- /src/BuildingBlocks/BuildingBlocks.Core/CQRS/Query/ListQuery.cs: -------------------------------------------------------------------------------- 1 | using BuildingBlocks.Abstractions.CQRS; 2 | using BuildingBlocks.Abstractions.CQRS.Query; 3 | 4 | namespace BuildingBlocks.Core.CQRS.Query; 5 | 6 | public record ListQuery : IListQuery 7 | where TResponse : notnull 8 | { 9 | public IList? Includes { get; init; } 10 | public IList? Filters { get; init; } 11 | public IList? Sorts { get; init; } 12 | public int Page { get; init; } 13 | public int PageSize { get; init; } 14 | } 15 | -------------------------------------------------------------------------------- /src/BuildingBlocks/BuildingBlocks.Core/Utils/IpUtilities.cs: -------------------------------------------------------------------------------- 1 | using System.Net; 2 | using System.Net.Sockets; 3 | 4 | namespace BuildingBlocks.Core.Utils; 5 | 6 | public static class IpUtilities 7 | { 8 | public static string GetIpAddress() 9 | { 10 | var host = Dns.GetHostEntry(Dns.GetHostName()); 11 | foreach (var ip in host.AddressList) 12 | { 13 | if (ip.AddressFamily == AddressFamily.InterNetwork) 14 | return ip.ToString(); 15 | } 16 | 17 | return string.Empty; 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /src/Modules/Customers/FoodDelivery.Modules.Customers/Customers/Exceptions/Domain/CustomerAlreadyVerifiedException.cs: -------------------------------------------------------------------------------- 1 | using BuildingBlocks.Core.Exception.Types; 2 | 3 | namespace FoodDelivery.Modules.Customers.Customers.Exceptions.Domain; 4 | 5 | internal class CustomerAlreadyVerifiedException : AppException 6 | { 7 | public long CustomerId { get; } 8 | 9 | public CustomerAlreadyVerifiedException(long customerId) 10 | : base($"Customer with Id: '{customerId}' already verified.") 11 | { 12 | CustomerId = customerId; 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /src/Modules/Customers/FoodDelivery.Modules.Customers/RestockSubscriptions/Dtos/RestockSubscriptionReadDto.cs: -------------------------------------------------------------------------------- 1 | namespace FoodDelivery.Modules.Customers.RestockSubscriptions.Dtos; 2 | 3 | public class RestockSubscriptionReadDto 4 | { 5 | public string CustomerName { get; init; } = null!; 6 | public string CustomerId { get; init; } = null!; 7 | public string ProductId { get; init; } = null!; 8 | public string ProductName { get; init; } = null!; 9 | public bool Processed { get; init; } 10 | public DateTime? ProcessedTime { get; init; } 11 | } 12 | -------------------------------------------------------------------------------- /src/BuildingBlocks/BuildingBlocks.Abstractions/CQRS/Event/Internal/IDomainEvent.cs: -------------------------------------------------------------------------------- 1 | namespace BuildingBlocks.Abstractions.CQRS.Event.Internal; 2 | 3 | /// 4 | /// The domain event interface. 5 | /// 6 | public interface IDomainEvent : IEvent 7 | { 8 | /// 9 | /// Gets the identifier of the aggregate which has generated the event. 10 | /// 11 | dynamic AggregateId { get; } 12 | long AggregateSequenceNumber { get; } 13 | 14 | public IDomainEvent WithAggregate(dynamic aggregateId, long version); 15 | } 16 | -------------------------------------------------------------------------------- /src/BuildingBlocks/BuildingBlocks.Abstractions/Persistence/EventStore/IStreamEvent.cs: -------------------------------------------------------------------------------- 1 | using BuildingBlocks.Abstractions.CQRS.Event; 2 | using BuildingBlocks.Abstractions.CQRS.Event.Internal; 3 | 4 | namespace BuildingBlocks.Abstractions.Persistence.EventStore; 5 | 6 | public interface IStreamEvent : IEvent 7 | { 8 | public IDomainEvent Data { get; } 9 | 10 | public IStreamEventMetadata? Metadata { get; } 11 | } 12 | 13 | public interface IStreamEvent : IStreamEvent 14 | where T : IDomainEvent 15 | { 16 | public new T Data { get; } 17 | } 18 | -------------------------------------------------------------------------------- /src/Modules/Customers/FoodDelivery.Modules.Customers/Customers/Exceptions/Domain/CustomerAlreadyCompletedException.cs: -------------------------------------------------------------------------------- 1 | using BuildingBlocks.Core.Exception.Types; 2 | 3 | namespace FoodDelivery.Modules.Customers.Customers.Exceptions.Domain; 4 | 5 | internal class CustomerAlreadyCompletedException : AppException 6 | { 7 | public long CustomerId { get; } 8 | 9 | public CustomerAlreadyCompletedException(long customerId) 10 | : base($"Customer with ID: '{customerId}' already completed.") 11 | { 12 | CustomerId = customerId; 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /src/Packages.props: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 7.0.0 5 | 6.0.2 6 | 6.0.5 7 | 7.0.0 8 | 7.0.0 9 | 7.0.0 10 | 8.0.0 11 | 12 | 13 | -------------------------------------------------------------------------------- /src/BuildingBlocks/BuildingBlocks.Security/BuildingBlocks.Security.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | -------------------------------------------------------------------------------- /src/BuildingBlocks/BuildingBlocks.Abstractions/CQRS/Command/ICommandProcessor.cs: -------------------------------------------------------------------------------- 1 | namespace BuildingBlocks.Abstractions.CQRS.Command; 2 | 3 | public interface ICommandProcessor 4 | { 5 | Task SendAsync(ICommand command, CancellationToken cancellationToken = default) 6 | where TResult : notnull; 7 | 8 | Task ScheduleAsync(IInternalCommand internalCommandCommand, CancellationToken cancellationToken = default); 9 | Task ScheduleAsync(IInternalCommand[] internalCommandCommands, CancellationToken cancellationToken = default); 10 | } 11 | -------------------------------------------------------------------------------- /src/BuildingBlocks/BuildingBlocks.Abstractions/CQRS/Event/IAggregatesDomainEventsRequestStore.cs: -------------------------------------------------------------------------------- 1 | using BuildingBlocks.Abstractions.CQRS.Event.Internal; 2 | using BuildingBlocks.Abstractions.Domain; 3 | 4 | namespace BuildingBlocks.Abstractions.CQRS.Event; 5 | 6 | public interface IAggregatesDomainEventsRequestStore 7 | { 8 | IReadOnlyList AddEventsFromAggregate(T aggregate) 9 | where T : IHaveAggregate; 10 | 11 | void AddEvents(IReadOnlyList events); 12 | 13 | IReadOnlyList GetAllUncommittedEvents(); 14 | } 15 | -------------------------------------------------------------------------------- /src/Modules/Catalogs/FoodDelivery.Modules.Catalogs/Products/ValueObjects/ProductId.cs: -------------------------------------------------------------------------------- 1 | using Ardalis.GuardClauses; 2 | using BuildingBlocks.Abstractions.Domain; 3 | 4 | namespace FoodDelivery.Modules.Catalogs.Products.ValueObjects; 5 | 6 | public record ProductId : AggregateId 7 | { 8 | public ProductId(long value) : base(value) 9 | { 10 | Guard.Against.NegativeOrZero(value, nameof(value)); 11 | } 12 | 13 | public static implicit operator long(ProductId id) => id.Value; 14 | 15 | public static implicit operator ProductId(long id) => new(id); 16 | } 17 | -------------------------------------------------------------------------------- /src/Modules/Customers/FoodDelivery.Modules.Customers/Shared/Clients/Catalogs/ICatalogApiClient.cs: -------------------------------------------------------------------------------- 1 | using FoodDelivery.Modules.Customers.Shared.Clients.Catalogs.Dtos; 2 | 3 | namespace FoodDelivery.Modules.Customers.Shared.Clients.Catalogs; 4 | 5 | // Ref: http://www.kamilgrzybek.com/design/modular-monolith-integration-styles/ 6 | // https://docs.microsoft.com/en-us/azure/architecture/patterns/anti-corruption-layer 7 | public interface ICatalogApiClient 8 | { 9 | Task GetProductByIdAsync(long id, CancellationToken cancellationToken = default); 10 | } 11 | -------------------------------------------------------------------------------- /tests/modules/Customers/FoodDelivery.Modules.Customers.IntegrationTests/FoodDelivery.Modules.Customers.IntegrationTests.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | PreserveNewest 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | -------------------------------------------------------------------------------- /src/BuildingBlocks/BuildingBlocks.Abstractions/CQRS/Query/IQueryHandler.cs: -------------------------------------------------------------------------------- 1 | using MediatR; 2 | 3 | namespace BuildingBlocks.Abstractions.CQRS.Query; 4 | 5 | public interface IQueryHandler : IRequestHandler 6 | where TQuery : IQuery 7 | where TResponse : notnull 8 | { 9 | } 10 | 11 | // https://jimmybogard.com/mediatr-10-0-released/ 12 | public interface IStreamQueryHandler : IStreamRequestHandler 13 | where TQuery : IStreamQuery 14 | where TResponse : notnull 15 | { 16 | } 17 | -------------------------------------------------------------------------------- /src/Modules/Identity/FoodDelivery.Modules.Identity/Identity/Dtos/RefreshTokenDto.cs: -------------------------------------------------------------------------------- 1 | namespace FoodDelivery.Modules.Identity.Identity.Dtos; 2 | 3 | public class RefreshTokenDto 4 | { 5 | public Guid UserId { get; set; } 6 | public string Token { get; set; } 7 | public DateTime CreatedAt { get; set; } 8 | public DateTime ExpireAt { get; set; } 9 | public string CreatedByIp { get; set; } 10 | public bool IsExpired { get; set; } 11 | public bool IsRevoked { get; set; } 12 | public bool IsActive { get; set; } 13 | public DateTime? RevokedAt { get; set; } 14 | } 15 | -------------------------------------------------------------------------------- /src/BuildingBlocks/BuildingBlocks.Abstractions/Domain/EventSourcing/IEventSourcedAggregate.cs: -------------------------------------------------------------------------------- 1 | using BuildingBlocks.Abstractions.Persistence.EventStore; 2 | 3 | namespace BuildingBlocks.Abstractions.Domain.EventSourcing; 4 | 5 | public interface IEventSourcedAggregate : IEntity, IHaveEventSourcingAggregate 6 | { 7 | } 8 | 9 | public interface IEventSourcedAggregate : IEventSourcedAggregate 10 | where TIdentity : Identity 11 | { 12 | } 13 | 14 | public interface IEventSourcedAggregate : IEventSourcedAggregate 15 | { 16 | } 17 | -------------------------------------------------------------------------------- /src/Modules/Catalogs/FoodDelivery.Modules.Catalogs/Products/Models/ProductView.cs: -------------------------------------------------------------------------------- 1 | namespace FoodDelivery.Modules.Catalogs.Products.Models; 2 | 3 | public class ProductView 4 | { 5 | public long ProductId { get; set; } 6 | public string ProductName { get; set; } = default!; 7 | public long CategoryId { get; set; } 8 | public string CategoryName { get; set; } = default!; 9 | public long SupplierId { get; set; } 10 | public string SupplierName { get; set; } = default!; 11 | public long BrandId { get; set; } 12 | public string BrandName { get; set; } = default!; 13 | } 14 | -------------------------------------------------------------------------------- /src/Modules/Customers/FoodDelivery.Modules.Customers/RestockSubscriptions/Dtos/RestockSubscriptionDto.cs: -------------------------------------------------------------------------------- 1 | namespace FoodDelivery.Modules.Customers.RestockSubscriptions.Dtos; 2 | 3 | public class RestockSubscriptionDto 4 | { 5 | public long Id { get; init; } 6 | public long CustomerId { get; init; } 7 | public string Email { get; init; } 8 | public long ProductId { get; init; } 9 | public string ProductName { get; init; } 10 | public DateTime Created { get; init; } 11 | public bool Processed { get; init; } 12 | public DateTime? ProcessedTime { get; init; } 13 | } 14 | -------------------------------------------------------------------------------- /src/Modules/Orders/FoodDelivery.Modules.Orders/Orders/ValueObjects/OrderId.cs: -------------------------------------------------------------------------------- 1 | using Ardalis.GuardClauses; 2 | using BuildingBlocks.Abstractions.Domain; 3 | 4 | namespace FoodDelivery.Modules.Orders.Orders.ValueObjects; 5 | 6 | public record OrderId : AggregateId 7 | { 8 | public OrderId(long value) : base(value) 9 | { 10 | Guard.Against.NegativeOrZero(value, nameof(value)); 11 | } 12 | 13 | public static implicit operator long(OrderId id) => Guard.Against.Null(id.Value, nameof(id.Value)); 14 | 15 | public static implicit operator OrderId(long id) => new(id); 16 | } 17 | -------------------------------------------------------------------------------- /src/BuildingBlocks/BuildingBlocks.Core/Domain/AuditableEntity.cs: -------------------------------------------------------------------------------- 1 | using BuildingBlocks.Abstractions.Domain; 2 | 3 | namespace BuildingBlocks.Core.Domain; 4 | 5 | public class AuditableEntity : Entity, IAuditableEntity 6 | { 7 | public DateTime? LastModified { get; protected set; } 8 | public int? LastModifiedBy { get; protected set; } 9 | } 10 | 11 | public abstract class AuditableEntity : AuditableEntity 12 | where TIdentity : Identity 13 | { 14 | } 15 | 16 | public class AuditableEntity : AuditableEntity, long> 17 | { 18 | } 19 | -------------------------------------------------------------------------------- /src/Api/FoodDelivery.Api/Extensions/ServiceCollectionExtensions/ServiceCollectionExtensions.Options.cs: -------------------------------------------------------------------------------- 1 | using BuildingBlocks.Core.Web; 2 | 3 | namespace FoodDelivery.Api.Extensions.ServiceCollectionExtensions; 4 | 5 | public static partial class ServiceCollectionExtensions 6 | { 7 | public static IServiceCollection AddApplicationOptions(this IServiceCollection services, IConfiguration configuration) 8 | { 9 | services.AddOptions().Bind(configuration.GetSection(nameof(AppOptions))) 10 | .ValidateDataAnnotations(); 11 | 12 | return services; 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /tests/shared/Tests.Shared/OptionsHelper.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.Extensions.Configuration; 2 | using Tests.Shared.Helpers; 3 | 4 | namespace Tests.Shared; 5 | 6 | public static class OptionsHelper 7 | { 8 | public static TOptions GetOptions(string section, string? settingsFileName = null) 9 | where TOptions : class, new() 10 | { 11 | var configuration = new TOptions(); 12 | 13 | ConfigurationHelper.BuildConfiguration(settingsFileName) 14 | .GetSection(section) 15 | .Bind(configuration); 16 | 17 | return configuration; 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /src/BuildingBlocks/BuildingBlocks.Abstractions/Messaging/MessageHandlerDelegate.cs: -------------------------------------------------------------------------------- 1 | using BuildingBlocks.Abstractions.Messaging.Context; 2 | 3 | namespace BuildingBlocks.Abstractions.Messaging; 4 | 5 | public delegate Task MessageHandler( 6 | IConsumeContext context, 7 | CancellationToken cancellationToken = default) 8 | where TMessage : class, IMessage; 9 | 10 | public delegate Task MessageHandlerAck( 11 | IConsumeContext context, 12 | CancellationToken cancellationToken = default) 13 | where TMessage : class, IMessage; 14 | -------------------------------------------------------------------------------- /src/BuildingBlocks/BuildingBlocks.Security/ApiKey/Authorization/OnlyAdminsAuthorizationHandler.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.AspNetCore.Authorization; 2 | 3 | namespace BuildingBlocks.Security.ApiKey.Authorization; 4 | 5 | public class OnlyAdminsAuthorizationHandler : AuthorizationHandler 6 | { 7 | protected override Task HandleRequirementAsync( 8 | AuthorizationHandlerContext context, 9 | OnlyAdminsRequirement requirement) 10 | { 11 | if (context.User.IsInRole(Roles.Admin)) context.Succeed(requirement); 12 | 13 | return Task.CompletedTask; 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /src/Modules/Customers/FoodDelivery.Modules.Customers/RestockSubscriptions/Features/CreatingRestockSubscription/Exceptions/ProductHaveStockException.cs: -------------------------------------------------------------------------------- 1 | using BuildingBlocks.Core.Exception.Types; 2 | 3 | namespace FoodDelivery.Modules.Customers.RestockSubscriptions.Features.CreatingRestockSubscription.Exceptions; 4 | 5 | public class ProductHaveStockException : AppException 6 | { 7 | public ProductHaveStockException(long productId, int quantity, string name) : base( 8 | $@"Product with Id '{productId}' and name '{name}' already has available stock of '{quantity}' items.") 9 | { 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /src/BuildingBlocks/BuildingBlocks.Core/Domain/AuditAggregate.cs: -------------------------------------------------------------------------------- 1 | using BuildingBlocks.Abstractions.Domain; 2 | 3 | namespace BuildingBlocks.Core.Domain; 4 | 5 | public abstract class AuditAggregate : Aggregate, IAuditableEntity 6 | { 7 | public DateTime? LastModified { get; protected set; } 8 | public int? LastModifiedBy { get; protected set; } 9 | } 10 | 11 | public abstract class AuditAggregate : AuditAggregate 12 | where TIdentity : Identity 13 | { 14 | } 15 | 16 | public abstract class AuditAggregate : AuditAggregate, long> 17 | { 18 | } 19 | -------------------------------------------------------------------------------- /.github/workflows/release-drafter.yml: -------------------------------------------------------------------------------- 1 | # # https://johanneskonings.dev/github/2021/02/28/github_automatic_releases_and-changelog/ 2 | # # https://tiagomichaelsousa.dev/articles/stop-writing-your-changelogs-manually 3 | # name: Release Drafter 4 | 5 | # on: 6 | # push: 7 | # branches: 8 | # - main 9 | 10 | # jobs: 11 | # update_release_draft: 12 | # name: Release drafter 13 | # runs-on: ubuntu-latest 14 | 15 | # steps: 16 | # - name: Update Release Draft 17 | # uses: release-drafter/release-drafter@v5 18 | # env: 19 | # GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} 20 | -------------------------------------------------------------------------------- /src/Modules/Catalogs/FoodDelivery.Modules.Catalogs/Suppliers/Features/SupplierDeleted/Events/External/SupplierDeleted.cs: -------------------------------------------------------------------------------- 1 | using BuildingBlocks.Abstractions.CQRS.Event; 2 | using BuildingBlocks.Core.Messaging; 3 | 4 | namespace FoodDelivery.Modules.Catalogs.Suppliers.Features.SupplierDeleted.Events.External; 5 | 6 | public record SupplierDeleted(long Id) : IntegrationEvent; 7 | 8 | public class SupplierDeletedConsumer : IEventHandler 9 | { 10 | public Task Handle(SupplierDeleted notification, CancellationToken cancellationToken) 11 | { 12 | return Task.CompletedTask; 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /src/Api/FoodDelivery.Api/Extensions/ApplicationBuilderExtensions/ApplicationBuilderExtensions.Cors.cs: -------------------------------------------------------------------------------- 1 | namespace FoodDelivery.Api.Extensions.ApplicationBuilderExtensions; 2 | 3 | public static partial class ApplicationBuilderExtensions 4 | { 5 | /// 6 | /// Register CORS. 7 | /// 8 | public static IApplicationBuilder UseAppCors(this IApplicationBuilder app) 9 | { 10 | app.UseCors(p => 11 | { 12 | p.AllowAnyOrigin(); 13 | p.WithMethods("GET"); 14 | p.AllowAnyHeader(); 15 | }); 16 | 17 | return app; 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /src/BuildingBlocks/BuildingBlocks.Core/Messaging/MessageHeaders.cs: -------------------------------------------------------------------------------- 1 | namespace BuildingBlocks.Core.Messaging; 2 | 3 | public static class MessageHeaders 4 | { 5 | public const string MessageId = "message-id"; 6 | public const string CorrelationId = "correlation-id"; 7 | public const string CausationId = "causation-id"; 8 | public const string TraceId = "trace-id"; 9 | public const string SpanId = "span-id"; 10 | public const string ParentSpanId = "parent-id"; 11 | public const string Name = "name"; 12 | public const string Type = "type"; 13 | public const string Created = "created"; 14 | } 15 | -------------------------------------------------------------------------------- /src/BuildingBlocks/BuildingBlocks.Security/ApiKey/Authorization/OnlyCustomersAuthorizationHandler.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.AspNetCore.Authorization; 2 | 3 | namespace BuildingBlocks.Security.ApiKey.Authorization; 4 | 5 | public class OnlyCustomersAuthorizationHandler : AuthorizationHandler 6 | { 7 | protected override Task HandleRequirementAsync( 8 | AuthorizationHandlerContext context, 9 | OnlyCustomersRequirement requirement) 10 | { 11 | if (context.User.IsInRole(Roles.Customer)) context.Succeed(requirement); 12 | 13 | return Task.CompletedTask; 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /src/Modules/Customers/FoodDelivery.Modules.Customers/Customers/ValueObjects/CustomerId.cs: -------------------------------------------------------------------------------- 1 | using Ardalis.GuardClauses; 2 | using BuildingBlocks.Abstractions.Domain; 3 | 4 | namespace FoodDelivery.Modules.Customers.Customers.ValueObjects; 5 | 6 | public record CustomerId : AggregateId 7 | { 8 | public CustomerId(long value) : base(value) 9 | { 10 | Guard.Against.NegativeOrZero(value, nameof(value)); 11 | } 12 | 13 | public static implicit operator long(CustomerId id) => Guard.Against.Null(id.Value, nameof(id.Value)); 14 | 15 | public static implicit operator CustomerId(long id) => new(id); 16 | } 17 | -------------------------------------------------------------------------------- /src/BuildingBlocks/BuildingBlocks.Core/CQRS/Event/Internal/DomainEvent.cs: -------------------------------------------------------------------------------- 1 | using BuildingBlocks.Abstractions.CQRS.Event.Internal; 2 | 3 | namespace BuildingBlocks.Core.CQRS.Event.Internal; 4 | 5 | public abstract record DomainEvent : Event, IDomainEvent 6 | { 7 | public dynamic AggregateId { get; protected set; } = null!; 8 | public long AggregateSequenceNumber { get; protected set; } 9 | 10 | public virtual IDomainEvent WithAggregate(dynamic aggregateId, long version) 11 | { 12 | AggregateId = aggregateId; 13 | AggregateSequenceNumber = version; 14 | 15 | return this; 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /src/BuildingBlocks/BuildingBlocks.Security/ApiKey/AuthenticationBuilderExtensions.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.AspNetCore.Authentication; 2 | 3 | namespace BuildingBlocks.Security.ApiKey; 4 | 5 | public static class AuthenticationBuilderExtensions 6 | { 7 | public static AuthenticationBuilder AddApiKeySupport( 8 | this AuthenticationBuilder authenticationBuilder, 9 | Action options) 10 | { 11 | return authenticationBuilder.AddScheme( 12 | ApiKeyAuthenticationOptions.DefaultScheme, options); 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /src/Api/FoodDelivery.Api/FoodDelivery.Api.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /src/BuildingBlocks/BuildingBlocks.Logging/BaggageEnricher.cs: -------------------------------------------------------------------------------- 1 | using System.Diagnostics; 2 | using Serilog.Core; 3 | using Serilog.Events; 4 | 5 | namespace BuildingBlocks.Logging; 6 | 7 | public class BaggageEnricher : ILogEventEnricher 8 | { 9 | public void Enrich( 10 | LogEvent logEvent, 11 | ILogEventPropertyFactory propertyFactory) 12 | { 13 | if (Activity.Current == null) 14 | return; 15 | 16 | foreach (var (key, value) in Activity.Current.Baggage) 17 | { 18 | logEvent.AddPropertyIfAbsent(propertyFactory.CreateProperty(key, value)); 19 | } 20 | } 21 | } -------------------------------------------------------------------------------- /src/BuildingBlocks/BuildingBlocks.Resiliency/PolicyOptions.cs: -------------------------------------------------------------------------------- 1 | using BuildingBlocks.Resiliency.CircuitBreaker; 2 | using BuildingBlocks.Resiliency.Retry; 3 | using BuildingBlocks.Resiliency.Timeout; 4 | 5 | namespace BuildingBlocks.Resiliency; 6 | 7 | // Ref: https://anthonygiretti.com/2019/03/26/best-practices-with-httpclient-and-retry-policies-with-polly-in-net-core-2-part-2/ 8 | public class PolicyOptions : ICircuitBreakerPolicyOptions, IRetryPolicyOptions, ITimeoutPolicyOptions 9 | { 10 | public int RetryCount { get; set; } 11 | public int BreakDuration { get; set; } 12 | public int TimeOutDuration { get; set; } 13 | } 14 | -------------------------------------------------------------------------------- /src/Modules/Catalogs/FoodDelivery.Modules.Catalogs/Suppliers/Features/SupplierCreated/Events/External/SupplierCreated.cs: -------------------------------------------------------------------------------- 1 | using BuildingBlocks.Abstractions.CQRS.Event; 2 | using BuildingBlocks.Core.Messaging; 3 | 4 | namespace FoodDelivery.Modules.Catalogs.Suppliers.Features.SupplierCreated.Events.External; 5 | 6 | public record SupplierCreated(long Id, string Name) : IntegrationEvent; 7 | 8 | 9 | public class SupplierCreatedConsumer : IEventHandler 10 | { 11 | public Task Handle(SupplierCreated notification, CancellationToken cancellationToken) 12 | { 13 | return Task.CompletedTask; 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /src/Modules/Catalogs/FoodDelivery.Modules.Catalogs/Suppliers/Features/SupplierUpdated/Events/External/SupplierUpdated.cs: -------------------------------------------------------------------------------- 1 | using BuildingBlocks.Abstractions.CQRS.Event; 2 | using BuildingBlocks.Core.Messaging; 3 | 4 | namespace FoodDelivery.Modules.Catalogs.Suppliers.Features.SupplierUpdated.Events.External; 5 | 6 | public record SupplierUpdated(long Id, string Name) : IntegrationEvent; 7 | 8 | 9 | public class SupplierUpdatedConsumer : IEventHandler 10 | { 11 | public Task Handle(SupplierUpdated notification, CancellationToken cancellationToken) 12 | { 13 | return Task.CompletedTask; 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /src/BuildingBlocks/BuildingBlocks.Abstractions/Messaging/ContextItems.cs: -------------------------------------------------------------------------------- 1 | namespace BuildingBlocks.Abstractions.Messaging; 2 | 3 | public class ContextItems 4 | { 5 | private readonly Dictionary _items = new(); 6 | 7 | public ContextItems AddItem(string key, object? value) 8 | { 9 | _items.TryAdd(key, value); 10 | return this; 11 | } 12 | 13 | public T? TryGetItem(string key) 14 | { 15 | if (_items.TryGetValue(key, out var result)) 16 | { 17 | return result is T type ? type : default; 18 | } 19 | 20 | return default; 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /src/BuildingBlocks/BuildingBlocks.Core/CQRS/Event/Event.cs: -------------------------------------------------------------------------------- 1 | using BuildingBlocks.Abstractions.CQRS.Event; 2 | using BuildingBlocks.Core.Types; 3 | 4 | namespace BuildingBlocks.Core.CQRS.Event; 5 | 6 | public abstract record Event : IEvent 7 | { 8 | public Guid EventId { get; protected set; } = Guid.NewGuid(); 9 | 10 | public long EventVersion { get; protected set; } = -1; 11 | 12 | public DateTime OccurredOn { get; protected set; } = DateTime.Now; 13 | 14 | public DateTimeOffset TimeStamp { get; protected set; } = DateTimeOffset.Now; 15 | 16 | public string EventType => TypeMapper.GetFullTypeName(GetType()); 17 | } 18 | -------------------------------------------------------------------------------- /src/BuildingBlocks/BuildingBlocks.Integration.Hangfire/BuildingBlocks.Integration.Hangfire.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | -------------------------------------------------------------------------------- /src/BuildingBlocks/BuildingBlocks.Security/ApiKey/Authorization/OnlyThirdPartiesAuthorizationHandler.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.AspNetCore.Authorization; 2 | 3 | namespace BuildingBlocks.Security.ApiKey.Authorization; 4 | 5 | public class OnlyThirdPartiesAuthorizationHandler : AuthorizationHandler 6 | { 7 | protected override Task HandleRequirementAsync( 8 | AuthorizationHandlerContext context, 9 | OnlyThirdPartiesRequirement requirement) 10 | { 11 | if (context.User.IsInRole(Roles.ThirdParty)) context.Succeed(requirement); 12 | 13 | return Task.CompletedTask; 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /src/Modules/Customers/FoodDelivery.Modules.Customers/RestockSubscriptions/ValueObjects/RestockSubscriptionId.cs: -------------------------------------------------------------------------------- 1 | using Ardalis.GuardClauses; 2 | using BuildingBlocks.Abstractions.Domain; 3 | 4 | namespace FoodDelivery.Modules.Customers.RestockSubscriptions.ValueObjects; 5 | 6 | public record RestockSubscriptionId : AggregateId 7 | { 8 | public RestockSubscriptionId(long value) : base(value) 9 | { 10 | } 11 | 12 | public static implicit operator long(RestockSubscriptionId id) => Guard.Against.Null(id.Value, nameof(id.Value)); 13 | 14 | public static implicit operator RestockSubscriptionId(long id) => new(id); 15 | } 16 | -------------------------------------------------------------------------------- /.devcontainer/scripts/setup-fonts.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # Download MesloLGM Nerd Font 4 | wget https://github.com/ryanoasis/nerd-fonts/releases/download/v3.0.2/Meslo.zip -O MesloLGM.zip 5 | 6 | # Extract the font files 7 | unzip MesloLGM.zip -d MesloLGM 8 | 9 | # Create the fonts directory if it doesn't exist 10 | mkdir -p ~/.local/share/fonts 11 | 12 | # Move the font files to the fonts directory 13 | mv MesloLGM/*.ttf ~/.local/share/fonts/ 14 | 15 | # Update the font cache 16 | fc-cache -fv 17 | 18 | # Clean up 19 | rm -rf MesloLGM.zip MesloLGM 20 | 21 | # Verify installation 22 | fc-list | grep "MesloLGM" 23 | 24 | echo "Font setup completed." -------------------------------------------------------------------------------- /src/BuildingBlocks/BuildingBlocks.Core/CQRS/Query/QueryHandler.cs: -------------------------------------------------------------------------------- 1 | using BuildingBlocks.Abstractions.CQRS.Query; 2 | 3 | namespace BuildingBlocks.Core.CQRS.Query; 4 | 5 | public abstract class QueryHandler : IQueryHandler 6 | where TQuery : IQuery 7 | where TResponse : notnull 8 | { 9 | protected abstract Task HandleQueryAsync(TQuery query, CancellationToken cancellationToken = default); 10 | 11 | public Task Handle(TQuery request, CancellationToken cancellationToken) 12 | { 13 | return HandleQueryAsync(request, cancellationToken); 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /src/BuildingBlocks/BuildingBlocks.Core/Persistence/EventStore/StreamEvent.cs: -------------------------------------------------------------------------------- 1 | using BuildingBlocks.Abstractions.CQRS.Event.Internal; 2 | using BuildingBlocks.Abstractions.Persistence.EventStore; 3 | using BuildingBlocks.Core.CQRS.Event; 4 | 5 | namespace BuildingBlocks.Core.Persistence.EventStore; 6 | 7 | public record StreamEvent 8 | (IDomainEvent Data, IStreamEventMetadata? Metadata = null) : Event, IStreamEvent; 9 | 10 | public record StreamEvent(T Data, IStreamEventMetadata? Metadata = null) 11 | : StreamEvent(Data, Metadata), IStreamEvent 12 | where T : IDomainEvent 13 | { 14 | public new T Data => (T)base.Data; 15 | } 16 | -------------------------------------------------------------------------------- /src/BuildingBlocks/BuildingBlocks.Security/Jwt/JwtOptions.cs: -------------------------------------------------------------------------------- 1 | namespace BuildingBlocks.Security.Jwt; 2 | 3 | public class JwtOptions 4 | { 5 | public string? Algorithm { get; set; } 6 | public string? Issuer { get; set; } 7 | public string SecretKey { get; set; } = null!; 8 | public string? Audience { get; set; } 9 | public double TokenLifeTimeSecond { get; set; } 10 | public GoogleExternalLogin? GoogleLoginConfigs { get; set; } 11 | 12 | public class GoogleExternalLogin 13 | { 14 | public string ClientId { get; set; } = null!; 15 | public string ClientSecret { get; set; } = null!; 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /src/BuildingBlocks/BuildingBlocks.Security/Jwt/readme.md: -------------------------------------------------------------------------------- 1 | # Create SSL Certificate 2 | 3 | [document](https://docs.microsoft.com/en-us/aspnet/core/security/docker-compose-https) 4 | 5 | ## Windows using Linux containers 6 | 7 | Generate certificate and configure local machine: 8 | 9 | ```bash 10 | # in the current folder 11 | dotnet dev-certs https -ep aspnetapp.pfx -p test123 12 | dotnet dev-certs https --trust 13 | ``` 14 | 15 | ```powershell 16 | dotnet dev-certs https -ep $env:USERPROFILE\.aspnet\https\aspnetapp.pfx -p test123 17 | ``` 18 | 19 | ```cmd 20 | dotnet dev-certs https -ep %USERPROFILE%\.aspnet\https\aspnetapp.pfx -p test123 21 | ``` -------------------------------------------------------------------------------- /src/Modules/Catalogs/FoodDelivery.Modules.Catalogs/Brands/Configs.cs: -------------------------------------------------------------------------------- 1 | using BuildingBlocks.Abstractions.Persistence; 2 | using FoodDelivery.Modules.Catalogs.Brands.Data; 3 | 4 | namespace FoodDelivery.Modules.Catalogs.Brands; 5 | 6 | internal static class Configs 7 | { 8 | internal static IServiceCollection AddBrandsServices(this IServiceCollection services) 9 | { 10 | services.AddScoped(); 11 | 12 | return services; 13 | } 14 | 15 | internal static IEndpointRouteBuilder MapBrandsEndpoints(this IEndpointRouteBuilder endpoints) 16 | { 17 | return endpoints; 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /src/Modules/Customers/FoodDelivery.Modules.Customers/RestockSubscriptions/Features/CreatingRestockSubscription/Exceptions/ProductAlreadySubscribedException.cs: -------------------------------------------------------------------------------- 1 | using System.Net; 2 | using BuildingBlocks.Core.Exception.Types; 3 | 4 | namespace FoodDelivery.Modules.Customers.RestockSubscriptions.Features.CreatingRestockSubscription.Exceptions; 5 | 6 | public class ProductAlreadySubscribedException : AppException 7 | { 8 | public ProductAlreadySubscribedException(long productId, string productName) 9 | : base($"Product with Id '{productId}' and Name '{productName}' is already subscribed", HttpStatusCode.Conflict) 10 | { 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /src/Modules/Orders/FoodDelivery.Modules.Orders/Shared/Data/OrderReadDbContext.cs: -------------------------------------------------------------------------------- 1 | using BuildingBlocks.Persistence.Mongo; 2 | using FoodDelivery.Modules.Orders.Orders.Models.Reads; 3 | using Humanizer; 4 | using Microsoft.Extensions.Options; 5 | using MongoDB.Driver; 6 | 7 | namespace FoodDelivery.Modules.Orders.Shared.Data; 8 | 9 | public class OrderReadDbContext : MongoDbContext 10 | { 11 | public OrderReadDbContext(IOptions options) : base(options) 12 | { 13 | Orders = GetCollection(nameof(Orders).Underscore()); 14 | } 15 | 16 | public IMongoCollection Orders { get; } 17 | } 18 | -------------------------------------------------------------------------------- /src/BuildingBlocks/BuildingBlocks.Core/Exception/Types/CustomException.cs: -------------------------------------------------------------------------------- 1 | using System.Net; 2 | 3 | namespace BuildingBlocks.Core.Exception.Types; 4 | 5 | public class CustomException : System.Exception 6 | { 7 | public CustomException( 8 | string message, 9 | HttpStatusCode statusCode = HttpStatusCode.InternalServerError, 10 | List errors = default) : base(message) 11 | { 12 | ErrorMessages = errors; 13 | StatusCode = statusCode; 14 | } 15 | 16 | public IEnumerable ErrorMessages { get; protected set; } 17 | 18 | public HttpStatusCode StatusCode { get; protected set; } 19 | } 20 | -------------------------------------------------------------------------------- /tests/Directory.Build.props: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | net8.0 6 | latest 7 | enable 8 | enable 9 | CS1587,CS1591,CS1998,NU5105 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | -------------------------------------------------------------------------------- /src/BuildingBlocks/BuildingBlocks.Web/Module/CompositionRoot.cs: -------------------------------------------------------------------------------- 1 | using BuildingBlocks.Abstractions.Web.Module; 2 | 3 | namespace BuildingBlocks.Web.Module; 4 | 5 | public class CompositionRoot : ICompositionRoot 6 | { 7 | public CompositionRoot(IServiceProvider serviceProvider, IModuleDefinition module) 8 | { 9 | ServiceProvider = serviceProvider; 10 | ModuleDefinition = module; 11 | } 12 | 13 | public IServiceProvider ServiceProvider { get; } 14 | public IModuleDefinition ModuleDefinition { get; } 15 | 16 | public IServiceScope CreateScope() 17 | { 18 | return ServiceProvider.CreateScope(); 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /src/BuildingBlocks/BuildingBlocks.Core/CQRS/Command/NullCommandScheduler.cs: -------------------------------------------------------------------------------- 1 | using BuildingBlocks.Abstractions.CQRS.Command; 2 | using BuildingBlocks.Abstractions.Scheduling; 3 | 4 | namespace BuildingBlocks.Core.CQRS.Command; 5 | 6 | public class NullCommandScheduler : ICommandScheduler 7 | { 8 | public Task ScheduleAsync(IInternalCommand internalCommandCommand, CancellationToken cancellationToken = default) 9 | { 10 | return Task.CompletedTask; 11 | } 12 | 13 | public Task ScheduleAsync(IInternalCommand[] internalCommandCommands, CancellationToken cancellationToken = default) 14 | { 15 | return Task.CompletedTask; 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /src/BuildingBlocks/BuildingBlocks.Resiliency/Extensions/HttpClientBuilderExtensions.cs: -------------------------------------------------------------------------------- 1 | namespace BuildingBlocks.Resiliency.Extensions; 2 | 3 | public static partial class HttpClientBuilderExtensions 4 | { 5 | public static IHttpClientBuilder AddCustomPolicyHandlers( 6 | this IHttpClientBuilder httpClientBuilder, 7 | Func? builder = null) 8 | { 9 | var result = httpClientBuilder 10 | .AddRetryPolicyHandler() 11 | .AddCircuitBreakerHandler(); 12 | 13 | if (builder is { }) 14 | result = builder.Invoke(result); 15 | 16 | return result; 17 | } 18 | } -------------------------------------------------------------------------------- /src/Modules/Catalogs/FoodDelivery.Modules.Catalogs/Suppliers/Configs.cs: -------------------------------------------------------------------------------- 1 | using BuildingBlocks.Abstractions.Persistence; 2 | using FoodDelivery.Modules.Catalogs.Suppliers.Data; 3 | 4 | namespace FoodDelivery.Modules.Catalogs.Suppliers; 5 | 6 | internal static class Configs 7 | { 8 | internal static IServiceCollection AddSuppliersServices(this IServiceCollection services) 9 | { 10 | services.AddScoped(); 11 | 12 | return services; 13 | } 14 | 15 | internal static IEndpointRouteBuilder MapSuppliersEndpoints(this IEndpointRouteBuilder endpoints) 16 | { 17 | return endpoints; 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /src/BuildingBlocks/BuildingBlocks.Abstractions/Persistence/Mongo/IMongoDbContext.cs: -------------------------------------------------------------------------------- 1 | using MongoDB.Driver; 2 | 3 | namespace BuildingBlocks.Abstractions.Persistence.Mongo; 4 | 5 | public interface IMongoDbContext : IDisposable 6 | { 7 | IMongoCollection GetCollection(string? name = null); 8 | Task SaveChangesAsync(CancellationToken cancellationToken = default); 9 | Task BeginTransactionAsync(CancellationToken cancellationToken = default); 10 | Task CommitTransactionAsync(CancellationToken cancellationToken = default); 11 | Task RollbackTransaction(CancellationToken cancellationToken = default); 12 | void AddCommand(Func func); 13 | } 14 | -------------------------------------------------------------------------------- /src/BuildingBlocks/BuildingBlocks.Core/Domain/Entity.cs: -------------------------------------------------------------------------------- 1 | using BuildingBlocks.Abstractions.Domain; 2 | 3 | namespace BuildingBlocks.Core.Domain; 4 | 5 | public abstract class Entity : IEntity 6 | { 7 | protected Entity(TId id) => Id = id; 8 | protected Entity() { } 9 | 10 | public TId Id { get; protected set; } 11 | 12 | public DateTime Created { get; protected set; } 13 | public int? CreatedBy { get; protected set; } 14 | } 15 | 16 | public abstract class Entity : Entity 17 | where TIdentity : Identity 18 | { 19 | } 20 | 21 | public abstract class Entity : Entity, IEntity 22 | { 23 | } 24 | -------------------------------------------------------------------------------- /src/Modules/Catalogs/FoodDelivery.Modules.Catalogs/Shared/Data/CatalogReadDbContext.cs: -------------------------------------------------------------------------------- 1 | using BuildingBlocks.Persistence.Mongo; 2 | using FoodDelivery.Modules.Catalogs.Products.Models.Read; 3 | using Humanizer; 4 | using Microsoft.Extensions.Options; 5 | using MongoDB.Driver; 6 | 7 | namespace FoodDelivery.Modules.Catalogs.Shared.Data; 8 | 9 | public class CatalogReadDbContext : MongoDbContext 10 | { 11 | public CatalogReadDbContext(IOptions options) : base(options) 12 | { 13 | Products = GetCollection(nameof(Products).Underscore()); 14 | } 15 | 16 | public IMongoCollection Products { get; } 17 | } 18 | -------------------------------------------------------------------------------- /src/BuildingBlocks/BuildingBlocks.Abstractions/Scheduling/IScheduler.cs: -------------------------------------------------------------------------------- 1 | using MediatR; 2 | 3 | namespace BuildingBlocks.Abstractions.Scheduling; 4 | 5 | public interface IScheduler 6 | { 7 | void Enqueue(T request) 8 | where T : class, IRequest; 9 | 10 | void Enqueue(object request); 11 | 12 | void Schedule(T request, DateTimeOffset scheduleAt) 13 | where T : class, IRequest; 14 | 15 | void Schedule(object request, DateTimeOffset scheduleAt); 16 | 17 | void ScheduleRecurring(T request, string cornExpression) 18 | where T : class, IRequest; 19 | 20 | void ScheduleRecurring(object request, string cornExpression); 21 | } 22 | -------------------------------------------------------------------------------- /src/Modules/Catalogs/FoodDelivery.Modules.Catalogs/Categories/Configs.cs: -------------------------------------------------------------------------------- 1 | using BuildingBlocks.Abstractions.Persistence; 2 | using FoodDelivery.Modules.Catalogs.Categories.Data; 3 | 4 | namespace FoodDelivery.Modules.Catalogs.Categories; 5 | 6 | internal static class Configs 7 | { 8 | internal static IServiceCollection AddCategoriesServices(this IServiceCollection services) 9 | { 10 | services.AddScoped(); 11 | 12 | return services; 13 | } 14 | 15 | internal static IEndpointRouteBuilder MapCategoriesEndpoints(this IEndpointRouteBuilder endpoints) 16 | { 17 | 18 | return endpoints; 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /src/BuildingBlocks/BuildingBlocks.Persistence.EfCore.Postgres/BuildingBlocks.Persistence.EfCore.Postgres.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | -------------------------------------------------------------------------------- /src/Modules/Orders/FoodDelivery.Modules.Orders/Orders/ValueObjects/CustomerInfo.cs: -------------------------------------------------------------------------------- 1 | using BuildingBlocks.Core.Domain; 2 | 3 | namespace FoodDelivery.Modules.Orders.Orders.ValueObjects; 4 | 5 | public class CustomerInfo : ValueObject 6 | { 7 | public string Name { get; private set; } 8 | public long CustomerId { get; private set; } 9 | 10 | public static CustomerInfo Create(string name, long customerId) 11 | { 12 | return new CustomerInfo { Name = name, CustomerId = customerId }; 13 | } 14 | 15 | protected override IEnumerable GetEqualityComponents() 16 | { 17 | yield return Name; 18 | yield return CustomerId; 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /src/Api/FoodDelivery.Api/appsettings.json: -------------------------------------------------------------------------------- 1 | { 2 | "LoggerOptions": { 3 | "level": "Information", 4 | "LogTemplate": "{Timestamp:yyyy-MM-dd HH:mm:ss.fff} {Level} - {Message:lj}{NewLine}{Exception}" 5 | }, 6 | "AppOptions": { 7 | "Name": "Food Delivery Api", 8 | "Description": "Food Delivery Api", 9 | "ApiAddress": "http://localhost:5000" 10 | }, 11 | "JwtOptions": { 12 | "SecretKey": "50d14aWf9FrMwc7SOLoz", 13 | "Audience": "food-delivery-api", 14 | "Issuer": "food-delivery-identity", 15 | "TokenLifeTimeSecond": 3600 16 | }, 17 | "PolicyOptions": { 18 | "RetryCount": 3, 19 | "BreakDuration": 30, 20 | "TimeOutDuration": 15 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /tests/shared/Tests.Shared/Fixtures/DelegateHttpClientFactory.cs: -------------------------------------------------------------------------------- 1 | namespace Tests.Shared.Fixtures; 2 | 3 | public class DelegateHttpClientFactory : IHttpClientFactory 4 | { 5 | private readonly Func _httpClientProvider; 6 | 7 | public DelegateHttpClientFactory(Func httpClientProvider) 8 | { 9 | _httpClientProvider = httpClientProvider; 10 | } 11 | 12 | public HttpClient CreateClient(string name) 13 | { 14 | if (name == "k8s-cluster-service" || name == "health-checks-webhooks" || name == "health-checks") 15 | return new HttpClient(); 16 | 17 | return _httpClientProvider(name); 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /src/Api/FoodDelivery.Api/appsettings.test.json: -------------------------------------------------------------------------------- 1 | { 2 | "LoggerOptions": { 3 | "level": "Information", 4 | "LogTemplate": "{Timestamp:yyyy-MM-dd HH:mm:ss.fff} {Level} - {Message:lj}{NewLine}{Exception}" 5 | }, 6 | "AppOptions": { 7 | "Name": "Food Delivery Api", 8 | "Description": "Food Delivery Api", 9 | "ApiAddress": "http://localhost:5000" 10 | }, 11 | "JwtOptions": { 12 | "SecretKey": "50d14aWf9FrMwc7SOLoz", 13 | "Audience": "food-delivery-api", 14 | "Issuer": "food-delivery-identity", 15 | "TokenLifeTimeSecond": 3600 16 | }, 17 | "PolicyOptions": { 18 | "RetryCount": 3, 19 | "BreakDuration": 30, 20 | "TimeOutDuration": 15 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /tests/modules/Customers/FoodDelivery.Modules.Customers.IntegrationTests/CustomerIntegrationTestBase.cs: -------------------------------------------------------------------------------- 1 | using FoodDelivery.Api; 2 | using FoodDelivery.Modules.Customers.Shared.Data; 3 | using Tests.Shared.Fixtures; 4 | using Xunit.Abstractions; 5 | 6 | namespace FoodDelivery.Modules.Customers.IntegrationTests; 7 | 8 | public class CustomerModuleTestIntegrationTestBase : ModuleTestBase 9 | { 10 | public CustomerModuleTestIntegrationTestBase(IntegrationTestFixture integrationTestFixture, 11 | ITestOutputHelper outputHelper) : base(integrationTestFixture, outputHelper) 12 | { 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /src/Api/FoodDelivery.Api/appsettings.docker.json: -------------------------------------------------------------------------------- 1 | { 2 | "LoggerOptions": { 3 | "level": "Information", 4 | "LogTemplate": "{Timestamp:yyyy-MM-dd HH:mm:ss.fff} {Level} - {Message:lj}{NewLine}{Exception}" 5 | }, 6 | "AppOptions": { 7 | "Name": "Food Delivery Api", 8 | "Description": "Food Delivery Api", 9 | "ApiAddress": "http://localhost:5000" 10 | }, 11 | "JwtOptions": { 12 | "SecretKey": "50d14aWf9FrMwc7SOLoz", 13 | "Audience": "food-delivery-api", 14 | "Issuer": "food-delivery-identity", 15 | "TokenLifeTimeSecond": 3600 16 | }, 17 | "PolicyOptions": { 18 | "RetryCount": 3, 19 | "BreakDuration": 30, 20 | "TimeOutDuration": 15 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /src/BuildingBlocks/BuildingBlocks.Security/Jwt/IJwtService.cs: -------------------------------------------------------------------------------- 1 | using System.Security.Claims; 2 | using Microsoft.IdentityModel.Tokens; 3 | 4 | namespace BuildingBlocks.Security.Jwt; 5 | 6 | public interface IJwtService 7 | { 8 | string GenerateJwtToken( 9 | string userName, 10 | string email, 11 | string userId, 12 | bool? isVerified = null, 13 | string? fullName = null, 14 | string? refreshToken = null, 15 | IReadOnlyList? usersClaims = null, 16 | IReadOnlyList? rolesClaims = null, 17 | IReadOnlyList? permissionsClaims = null); 18 | 19 | ClaimsPrincipal? GetPrincipalFromToken(string token); 20 | } 21 | -------------------------------------------------------------------------------- /src/Modules/Catalogs/FoodDelivery.Modules.Catalogs/Products/Features/ChangingProductBrand/ChangeProductBrand.cs: -------------------------------------------------------------------------------- 1 | using BuildingBlocks.Abstractions.CQRS.Command; 2 | 3 | namespace FoodDelivery.Modules.Catalogs.Products.Features.ChangingProductBrand; 4 | 5 | internal record ChangeProductBrand : ITxCommand; 6 | 7 | internal class ChangeProductBrandHandler : 8 | ICommandHandler 9 | { 10 | public Task Handle( 11 | ChangeProductBrand command, 12 | CancellationToken cancellationToken) 13 | { 14 | return Task.FromResult(null!); 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /src/BuildingBlocks/BuildingBlocks.Core/Types/MachineInstanceInfo.cs: -------------------------------------------------------------------------------- 1 | using Ardalis.GuardClauses; 2 | using BuildingBlocks.Abstractions.Types; 3 | 4 | namespace BuildingBlocks.Core.Types; 5 | 6 | public record MachineInstanceInfo : IMachineInstanceInfo 7 | { 8 | public MachineInstanceInfo(Guid clientId, string clientGroup) 9 | { 10 | Guard.Against.NullOrEmpty(clientGroup, nameof(clientGroup)); 11 | 12 | ClientId = clientId; 13 | ClientGroup = clientGroup; 14 | } 15 | 16 | public Guid ClientId { get; } 17 | public string ClientGroup { get; } 18 | 19 | internal static MachineInstanceInfo New() => new(Guid.NewGuid(), AppDomain.CurrentDomain.FriendlyName); 20 | } 21 | -------------------------------------------------------------------------------- /.github/workflows/multi-labeler.yml: -------------------------------------------------------------------------------- 1 | ## https://github.com/fuxingloh/multi-labeler 2 | #name: Multi Labeler 3 | #on: 4 | # pull_request: 5 | # types: [ opened, edited, synchronize, ready_for_review ] 6 | # branches: [ develop, main ] 7 | # 8 | #jobs: 9 | # labeler: 10 | # name: Labeler 11 | # runs-on: ubuntu-latest 12 | # steps: 13 | # # follows semantic versioning. Lock to different version: v1, v1.5, v1.5.0 or use a commit hash. 14 | # - uses: fuxingloh/multi-labeler@v1 15 | # with: 16 | # github-token: ${{secrets.GITHUB_TOKEN}} # optional, default to '${{ github.token }}' 17 | # config-path: .github/multi-labeler.yml # optional, default to '.github/labeler.yml' 18 | -------------------------------------------------------------------------------- /tests/shared/Tests.Shared/Extensions/WebApplicationFactoryExtensions.cs: -------------------------------------------------------------------------------- 1 | using System.Net.Http.Headers; 2 | using Microsoft.AspNetCore.Mvc.Testing; 3 | 4 | namespace Tests.Shared.Extensions; 5 | 6 | public static class WebApplicationFactoryExtensions 7 | { 8 | public static HttpClient CreateClientWithTestAuth(this WebApplicationFactory factory) where T : class 9 | { 10 | var client = factory.CreateClient(new WebApplicationFactoryClientOptions 11 | { 12 | AllowAutoRedirect = false 13 | }); 14 | 15 | client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue(Constants.AuthConstants.Scheme); 16 | 17 | return client; 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /src/Modules/Orders/FoodDelivery.Modules.Orders/Orders/Models/Order.cs: -------------------------------------------------------------------------------- 1 | using BuildingBlocks.Core.Domain; 2 | using FoodDelivery.Modules.Orders.Orders.ValueObjects; 3 | 4 | namespace FoodDelivery.Modules.Orders.Orders.Models; 5 | 6 | public class Order : Aggregate 7 | { 8 | public CustomerInfo Customer { get; private set; } = null!; 9 | public ProductInfo Product { get; private set; } = null!; 10 | 11 | public static Order Create(CustomerInfo customerInfo, ProductInfo productInfo) 12 | { 13 | //TODO: Complete order domain model 14 | return new Order 15 | { 16 | Customer = customerInfo, 17 | Product = productInfo 18 | }; 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /src/BuildingBlocks/BuildingBlocks.Web/Plugin/WebApplicationPluginAttribute.cs: -------------------------------------------------------------------------------- 1 | namespace BuildingBlocks.Web.Plugin; 2 | 3 | [AttributeUsage(AttributeTargets.Assembly, Inherited = false, AllowMultiple = true)] 4 | public sealed class WebApplicationPluginAttribute : Attribute 5 | { 6 | public WebApplicationPluginAttribute(Type pluginType) 7 | { 8 | if (!(pluginType.IsClass && !pluginType.IsAbstract && typeof(WebApplicationPlugin).IsAssignableFrom(pluginType))) 9 | { 10 | throw new NotSupportedException($"{pluginType} is not a supported {nameof(WebApplicationPlugin)}"); 11 | } 12 | 13 | PluginType = pluginType; 14 | } 15 | 16 | public Type PluginType { get; } 17 | } 18 | -------------------------------------------------------------------------------- /.github/release.yml: -------------------------------------------------------------------------------- 1 | # https://docs.github.com/en/repositories/releasing-projects-on-github/automatically-generated-release-notes 2 | # https://github.com/bcoe/conventional-release-labels 3 | # https://dev.to/github/how-to-automatically-generate-release-notes-for-your-project-2ng8 4 | changelog: 5 | exclude: 6 | labels: 7 | - ignore-for-release 8 | categories: 9 | - title: Features 10 | labels: 11 | - feature 12 | - title: Fixes 13 | labels: 14 | - bug 15 | - fix 16 | - enhancement 17 | - title: Enhancement 18 | labels: 19 | - refactor 20 | - enhancement 21 | - title: Other Changes 22 | labels: 23 | - "*" -------------------------------------------------------------------------------- /src/BuildingBlocks/BuildingBlocks.Core/Persistence/EventStore/StreamEventExtensions.cs: -------------------------------------------------------------------------------- 1 | using BuildingBlocks.Abstractions.CQRS.Event.Internal; 2 | using BuildingBlocks.Abstractions.Persistence.EventStore; 3 | using BuildingBlocks.Core.Utils; 4 | 5 | namespace BuildingBlocks.Core.Persistence.EventStore; 6 | 7 | public static class StreamEventExtensions 8 | { 9 | public static IStreamEvent ToStreamEvent( 10 | this IDomainEvent domainEvent, 11 | IStreamEventMetadata? metadata) 12 | { 13 | return ReflectionUtilities.CreateGenericType( 14 | typeof(StreamEvent<>), 15 | new[] { domainEvent.GetType() }, 16 | domainEvent, 17 | metadata); 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /src/BuildingBlocks/BuildingBlocks.Core/Domain/Exceptions/BusinessRuleValidationException.cs: -------------------------------------------------------------------------------- 1 | using BuildingBlocks.Abstractions.Domain; 2 | 3 | namespace BuildingBlocks.Core.Domain.Exceptions; 4 | 5 | public class BusinessRuleValidationException : DomainException 6 | { 7 | public IBusinessRule BrokenRule { get; } 8 | 9 | public string Details { get; } 10 | 11 | public BusinessRuleValidationException(IBusinessRule brokenRule) 12 | : base(brokenRule.Message) 13 | { 14 | BrokenRule = brokenRule; 15 | Details = brokenRule.Message; 16 | } 17 | 18 | public override string ToString() 19 | { 20 | return $"{BrokenRule.GetType().FullName}: {BrokenRule.Message}"; 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /src/BuildingBlocks/BuildingBlocks.Email/Options/EmailOptions.cs: -------------------------------------------------------------------------------- 1 | namespace BuildingBlocks.Email.Options; 2 | 3 | public class EmailOptions 4 | { 5 | public MailKitOptions? MimeKitOptions { get; set; } 6 | public SendGridOptions? SendGridOptions { get; set; } 7 | public string From { get; set; } = default!; 8 | public string? DisplayName { get; set; } 9 | public bool Enable { get; set; } 10 | } 11 | 12 | public class MailKitOptions 13 | { 14 | public string? Host { get; set; } 15 | public int Port { get; set; } 16 | public string? UserName { get; set; } 17 | public string? Password { get; set; } 18 | } 19 | 20 | public class SendGridOptions 21 | { 22 | public string? ApiKey { get; set; } 23 | } 24 | -------------------------------------------------------------------------------- /tests/modules/Identity/FoodDelivery.Modules.Identity.IntegrationTests/FoodDelivery.Modules.Identity.IntegrationTests.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | PreserveNewest 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | -------------------------------------------------------------------------------- /src/BuildingBlocks/BuildingBlocks.Abstractions/Caching/IInvalidateCachePolicy.cs: -------------------------------------------------------------------------------- 1 | using MediatR; 2 | 3 | namespace BuildingBlocks.Abstractions.Caching; 4 | 5 | public interface IInvalidateCachePolicy 6 | where TRequest : IRequest 7 | { 8 | string GetCacheKey(TRequest request) 9 | { 10 | var r = new { request }; 11 | var props = r.request.GetType().GetProperties().Select(pi => $"{pi.Name}:{pi.GetValue(r.request, null)}"); 12 | return $"{typeof(TRequest).FullName}{{{string.Join(",", props)}}}"; 13 | } 14 | } 15 | 16 | public interface IInvalidateCachePolicy : IInvalidateCachePolicy 17 | where TRequest : IRequest 18 | { 19 | } 20 | -------------------------------------------------------------------------------- /src/BuildingBlocks/BuildingBlocks.Abstractions/Messaging/Acknowledgement.cs: -------------------------------------------------------------------------------- 1 | namespace BuildingBlocks.Abstractions.Messaging; 2 | 3 | public abstract class Acknowledgement 4 | { 5 | } 6 | 7 | public class Ack : Acknowledgement 8 | { 9 | } 10 | 11 | public class Nack : Acknowledgement 12 | { 13 | public Nack(Exception exception, bool requeue = true) 14 | { 15 | Requeue = requeue; 16 | Exception = exception; 17 | } 18 | 19 | public bool Requeue { get; } 20 | public Exception Exception { get; } 21 | } 22 | 23 | public class Reject : Acknowledgement 24 | { 25 | public bool Requeue { get; } 26 | 27 | public Reject(bool requeue = true) 28 | { 29 | Requeue = requeue; 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /src/BuildingBlocks/BuildingBlocks.Abstractions/Messaging/Context/IConsumeContext.cs: -------------------------------------------------------------------------------- 1 | using System.Diagnostics; 2 | 3 | namespace BuildingBlocks.Abstractions.Messaging.Context; 4 | 5 | public interface IConsumeContext : IConsumeContext 6 | where TMessage : class, IMessage 7 | { 8 | new TMessage Message { get; } 9 | } 10 | 11 | public interface IConsumeContext 12 | { 13 | object Message { get; } 14 | IDictionary Headers { get; } 15 | ActivityContext? ParentContext { get; set; } 16 | Guid MessageId { get; } 17 | string MessageType { get; } 18 | ContextItems Items { get; } 19 | int PayloadSize { get; } 20 | ulong Version { get; } 21 | DateTime Created { get; } 22 | } 23 | --------------------------------------------------------------------------------