• Stars
    star
    268
  • Rank 148,104 (Top 3 %)
  • Language
    C#
  • License
    MIT License
  • Created about 2 years ago
  • Updated 2 months ago

Reviews

There are no reviews yet. Be the first to send feedback to the community and the maintainers!

Repository Details

๐ŸŒญ A practical food delivery modular monolith, built with .Net 7, Domain-Driven Design, CQRS, Vertical Slice Architecture, Event-Driven Architecture, and the latest technologies.

๐Ÿชฃ ECommerce Modular Monolith

.NET Gitpod ready-to-code

ecommerce-modular-monolith

ECommerce Modular Monolith is a fictional ecommerce sample, built with .Net Core and different software architecture and technologies like Modular Monolith Architecture, Vertical Slice Architecture , CQRS Pattern, Domain Driven Design (DDD), Event Driven Architecture. For communication between independent modules, we use asynchronous messaging with using our In-Memory Broker, and sometimes we use synchronous communication for real-time communications with using REST and gRPC calls.

This application ported to microservices architecture in another repository which is available in ecommerce-microservices-sample repository.

๐ŸŒ€ Keep in mind this repository is work in progress and will be complete over time ๐Ÿš€

โญ Support

If you like feel free to โญ this repository, It helps out :)

Thanks a bunch for supporting me!

Table of Contents

Plan

This project is in progress, New features will be added over time.

High-level plan is represented in the table

Feature Status
Building Blocks Completed โœ”๏ธ
API Completed โœ”๏ธ
Identity Module Completed โœ”๏ธ
Customer Module Completed โœ”๏ธ
Catalog Module Completed โœ”๏ธ
Order Module In Progress ๐Ÿ‘ทโ€
Shipping Module Not Started ๐Ÿšฉ
Payment Module Not Started ๐Ÿšฉ

Technologies - Libraries

  • โœ”๏ธ .NET 7 - .NET Framework and .NET Core, including ASP.NET and ASP.NET Core
  • โœ”๏ธ Npgsql Entity Framework Core Provider - Npgsql has an Entity Framework (EF) Core provider. It behaves like other EF Core providers (e.g. SQL Server), so the general EF Core docs apply here as well
  • โœ”๏ธ FluentValidation - Popular .NET validation library for building strongly-typed validation rules
  • โœ”๏ธ Swagger & Swagger UI - Swagger tools for documenting API's built on ASP.NET Core
  • โœ”๏ธ Serilog - Simple .NET logging with fully-structured events
  • โœ”๏ธ Polly - Polly is a .NET resilience and transient-fault-handling library that allows developers to express policies such as Retry, Circuit Breaker, Timeout, Bulkhead Isolation, and Fallback in a fluent and thread-safe manner
  • โœ”๏ธ Scrutor - Assembly scanning and decoration extensions for Microsoft.Extensions.DependencyInjection
  • โœ”๏ธ Opentelemetry-dotnet - The OpenTelemetry .NET Client
  • โœ”๏ธ DuendeSoftware IdentityServer - The most flexible and standards-compliant OpenID Connect and OAuth 2.x framework for ASP.NET Core
  • โœ”๏ธ Newtonsoft.Json - Json.NET is a popular high-performance JSON framework for .NET
  • โœ”๏ธ AspNetCore.Diagnostics.HealthChecks - Enterprise HealthChecks for ASP.NET Core Diagnostics Package
  • โœ”๏ธ Microsoft.AspNetCore.Authentication.JwtBearer - Handling Jwt Authentication and authorization in .Net Core
  • โœ”๏ธ NSubstitute - A friendly substitute for .NET mocking libraries.
  • โœ”๏ธ StyleCopAnalyzers - An implementation of StyleCop rules using the .NET Compiler Platform
  • โœ”๏ธ AutoMapper - Convention-based object-object mapper in .NET.
  • โœ”๏ธ Hellang.Middleware.ProblemDetails - A middleware for handling exception in .Net Core
  • โœ”๏ธ IdGen - Twitter Snowflake-alike ID generator for .Net

The Domain And Bounded Context - Modules Boundary

ECommerce Modular Monolith is a simple ecommerce api sample that has the basic business scenario for online purchasing with some dedicated modules. There are six possible Bounded context or Module for above business:

  • Identity Module: the Identity Module, uses to authenticate and authorize users through a token. Also, this module is responsible for creating users and their corresponding roles and permission with using .Net Core Identity and Jwt authentication and authorization. I will add also Identity Server in future for this module. Each of Administrator, Customer and Supplier are a User, actually a IdentityUser. To be a User, User Registration is required. Each User is assigned one or more User Role. Each User Role has set of Permissions. A Permission defines whether User can invoke a particular action or not.

  • Catalog Module: The Catalog Module presents the ability to add items to our ecommerce, It can be electronics, foods, books or anything else. Items can be grouped into categories and catalogs. A catalog is defined as a list of items that a company showcases online. the catalog is a collection of items, which can be grouped into categories. An item can be assigned to only one category or be direct child of a catalog without any category. Buyer can browse the products list with supported filtering and sorting by product name and price. customer can see the detail of the product on the product list and in the detail page, can see a name, description, available product in the inventory,...

  • Customers Module: This module is responsible for managing our customers information, track the activities and subscribing to get notification for out of stock products

  • Order Module: The Orders module main purpose is to ecommerce order details and manage orders created by users on client side. This module is not designed to be a full order processing system like ERP but serves as storage for customer orders details and can be synchronized with different external processing systems. Some of this module responsibilities are Saving orders, Saving order drafts, Ability to view and manage fulfillment, packages, Change discounts

  • Payment Module: The payment module is responsible for payment process of our customer with different payment process and managing and tracking our payment history

  • Shipping Module: The Shipping module provides the ability to extend shipping provider list with custom providers and also provides an interface and API for managing these shipping providers. Some of shipping module capabilities are Register Shipping methods, Edit Shipping method, Shipment details, Shipping settings

Application Architecture

The bellow architecture shows that there is one Public API (API Project or API Gateway in microservice world) which host all of our internal modules and accessible for the clients and this is done via HTTP request/response. The API project then routes the HTTP request to the corresponding modules. here our API project instead of Http Calls or Network Calls our modules we have some In-Memory Calls for calling our internal modules. In our application this is responsibility of GatewayProcessor class in minimal apis or CustomServiceBasedControllerActivator in normal controllers. In our API there is no code it just hosts our modules and it uses routes which defined in each module in vertical slice architecture for example CreateProductEndpoint in catalogs module. And when this endpoint reached by user http request from the API, inner this Endpoint we use GatewayProcessor<CatalogModuleConfiguration> for sending a In-Memory request to our module with using a dedicated Composition Root. Behind the scenes this GatewayProcessor for each module, uses a dedicate Composition Root or a Root Service Provider and it is responsibility of CompositionRootRegistry for preserving and creating composition root for each modules.

When In-Memory Call reached to the internal module, the module should process this request autonomous. Actually in modular monolith each of our modules should treat like a microservice with completely autonomous behavior. For reaching this goal we should use separated Composition Root for each module and actually for each composition root we have a separated Service Provider. (read more here...)

Composition Root: With using separate Composition Root for module we can reach to autonomy also a given module can create its own object dependency graph or Dependency Container, i.e. it should have its own Composition Root.

Each module is running within its own Composition Root or its own Service Provider and has directly access to its own local Database or Schema and its dependencies such as files, Mappers, etc. All these dependencies are only accessible for that module and not other modules. In fact modules are decoupled from each other and are autonomous (Not physically but virtually). also This approach makes migrating to the microservice easier for each module when we need that for example, scaling purpose. In this case we can extract given module to a separated microservice and our modular monolith will communicate with this service maybe with different broker like rabbitmq.

In this architecture modules should talk each other asynchronously most of the cases unless, we need the data immediately for example getting some data and sending to user. For async communications between modules we use a In-Memory Broker but we could use other message brokers depending on the needs and for sync communication we use REST calls or gRPC calls.

Modules are event based which means they can publish and/or subscribe to any events occurring in the setup. By using this approach for communicating between modules, each module does not need to know about the other modules or handle errors occurred in other modules.

In this architecture we use CQRS Pattern for separating read and write model beside of other CQRS Advantages. Here for now I don't use Event Sourcing for simplicity but I will use it in future for syncing read and write side with sending streams and using Projection Feature for some subscribers to syncing their data through sent streams and creating our Custom Read Models in subscribers side.

Here I have a write model that uses a postgres database for handling better Consistency and ACID Transaction guaranty. beside o this write side I use a read side model that uses MongoDB for better performance of our read side without any joins with suing some nested document in our document also better scalability with some good scaling features of MongoDB.

For syncing our read side and write side we have 2 options with using Event Driven Architecture (without using events streams in event sourcing):

  • If our Read Sides are in Same Service, during saving data in write side I save a Internal Command record in my Command Processor storage (like something we do in outbox pattern) and after commenting write side, our command processor manager reads unsent commands and sends them to their Command Handlers in same corresponding service and this handlers could save their read models in our MongoDb database as a read side.

  • If our Read Sides are in Another Services we publish an integration event (with saving this message in the outbox) after committing our write side and all of our Subscribers could get this event and save it in their read models (MongoDB).

All of this is optional in the application and it is possible to only use what that the service needs. Eg. if the service does not want to Use DDD because of business is very simple and it is mostly CRUD we can use Data Centric Architecture or If our application is not Task based instead of CQRS and separating read side and write side again we can just use a simple CRUD based application.

Here I used Outbox for Guaranteed Delivery and can be used as a landing zone for integration events before they are published to the message broker .

Outbox pattern ensures that a message was sent (e.g. to a queue) successfully at least once. With this pattern, instead of directly publishing a message to the queue, we ecommerce it in the temporary storage (e.g. database table) for preventing missing any message and some retry mechanism in any failure (At-least-once Delivery). For example When we save data as part of one transaction in our service, we also save messages (Integration Events) that we later want to process in another microservices as part of the same transaction. The list of messages to be processed is called a StoreMessage with Message Delivery Type Outbox that are part of our MessagePersistence service. This infrastructure also supports Inbox Message Delivery Type and Internal Message Delivery Type (Internal Processing).

Also we have a background service MessagePersistenceBackgroundService that periodically checks the our StoreMessages in the database and try to send the messages to the broker with using our MessagePersistenceService service. After it gets confirmation of publishing (e.g. ACK from the broker) it marks the message as processed to avoid resending. However, it is possible that we will not be able to mark the message as processed due to communication error, for example broker is unavailable. In this case our MessagePersistenceBackgroundService try to resend the messages that not processed and it is actually At-Least-Once delivery. We can be sure that message will be sent once, but can be sent multiple times too! Thatโ€™s why another name for this approach is Once-Or-More delivery. We should remember this and try to design receivers of our messages as Idempotents, which means:

In Messaging this concepts translates into a message that has the same effect whether it is received once or multiple times. This means that a message can safely be resent without causing any problems even if the receiver receives duplicates of the same message.

For handling Idempotency and Exactly-once Delivery in receiver side, we could use Inbox Pattern.

This pattern is similar to Outbox Pattern. Itโ€™s used to handle incoming messages (e.g. from a queue) for unique processing of a single message only once (even with executing multiple time). Accordingly, we have a table in which weโ€™re storing incoming messages. Contrary to outbox pattern, we first save the messages in the database, then weโ€™re returning ACK to queue. If save succeeded, but we didnโ€™t return ACK to queue, then delivery will be retried. Thatโ€™s why we have at-least-once delivery again. After that, an inbox background process runs and will process the inbox messages that not processed yet. also we can prevent executing a message with specific MessgaeIdmultiple times. after executing our inbox message for example with calling our subscribed event handlers we send a ACK to the queue when they succeeded. (Inbox part of the system is in progress, I will cover this part soon as possible)

Application Structure

In this project I used vertical slice architecture or Restructuring to a Vertical Slice Architecture also I used feature folder structure in this project.

  • We treat each request as a distinct use case or slice, encapsulating and grouping all concerns from front-end to back.
  • When We adding or changing a feature in an application in n-tire architecture, we are typically touching many different "layers" in an application. we are changing the user interface, adding fields to models, modifying validation, and so on. Instead of coupling across a layer, we couple vertically along a slice and each change affects only one slice.
  • We Minimize coupling between slices, and maximize coupling in a slice.
  • With this approach, each of our vertical slices can decide for itself how to best fulfill the request. New features only add code, we're not changing shared code and worrying about side effects. For implementing vertical slice architecture using cqrs pattern is a good match.

Also here I used CQRS for decompose my features to very small parts that makes our application:

  • maximize performance, scalability and simplicity.
  • adding new feature to this mechanism is very easy without any breaking change in other part of our codes. New features only add code, we're not changing shared code and worrying about side effects.
  • easy to maintain and any changes only affect on one command or query (or a slice) and avoid any breaking changes on other parts
  • it gives us better separation of concerns and cross cutting concern (with help of MediatR behavior pipelines) in our code instead of a big service class for doing a lot of things.

With using CQRS, our code will be more aligned with SOLID principles, especially with:

  • Single Responsibility rule - because logic responsible for a given operation is enclosed in its own type.
  • Open-Closed rule - because to add new operation you donโ€™t need to edit any of the existing types, instead you need to add a new file with a new type representing that operation.

Here instead of some Technical Splitting for example a folder or layer for our services, controllers and data models which increase dependencies between our technical splitting and also jump between layers or folders, We cut each business functionality into some vertical slices, and inner each of these slices we have Technical Folders Structure specific to that feature (command, handlers, infrastructure, repository, controllers, data models, ...).

Usually, when we work on a given functionality we need some technical things for example:

  • API endpoint (Controller)
  • Request Input (Dto)
  • Request Output (Dto)
  • Some class to handle Request, For example Command and Command Handler or Query and Query Handler
  • Data Model

Now we could all of these things beside each other and it decrease jumping and dependencies between some layers or folders.

Keeping such a split works great with CQRS. It segregates our operations and slices the application code vertically instead of horizontally. In Our CQRS pattern each command/query handler is a separate slice. This is where you can reduce coupling between layers. Each handler can be a separated code unit, even copy/pasted. Thanks to that, we can tune down the specific method to not follow general conventions (e.g. use custom SQL query or even different storage). In a traditional layered architecture, when we change the core generic mechanism in one layer, it can impact all methods.

High Level Structure

Catalogs Module Structure

Vertical Slice Flow in Modules

For implementing vertical slice architecture in each module, I have one project containing all codes related to specific module functionality, for example for implementing Catalog Module related functionally, I have ECommerce.Modules.Catalogs project and for hosting all internal modules we use ECommerce.Api project, actually this api project act like a gateway in micorservices world but instead of Network Calling internal module it will uses In-memory Calling with using GatewayProcessor and using separated Composition Root or service provider for each module.

  • ECommerce.Api is responsible for Hosting modules and configuring our web api, running the application on top of .net core and actually serving our modules slices to outside of world.
  • ECommerce.Modules.Catalogs is responsible for putting all slices (features) based on our functionality in some slices, for example we put all Features or Slices related to

Each modules should implement IModuleDefinition for example For Catalog Module we have:

public class CatalogModuleConfiguration : IModuleDefinition
{
    public const string CatalogModulePrefixUri = "api/v1/catalogs";
    public const string ModuleName = "Catalogs";

    public void AddModuleServices(IServiceCollection services, IConfiguration configuration)
    {
        services.AddInfrastructure(configuration);
        services.AddStorage(configuration);

        services.AddBrandsServices();
        services.AddCategoriesServices();
        services.AddSuppliersServices();
        services.AddProductsServices();
    }

    public async Task ConfigureModule(
        IApplicationBuilder app,
        IConfiguration configuration,
        ILogger logger,
        IWebHostEnvironment environment)
    {
        ServiceActivator.Configure(app.ApplicationServices);

        app.SubscribeAllMessageFromAssemblyOfType<CatalogRoot>();

        await app.ApplyDatabaseMigrations(logger);
        await app.SeedData(logger, environment);
    }

    public void MapEndpoints(IEndpointRouteBuilder endpoints)
    {
        endpoints.MapProductsEndpoints();

        endpoints.MapGet("/", (HttpContext context) =>
        {
            var requestId = context.Request.Headers.TryGetValue("X-Request-Id", out var requestIdHeader)
                ? requestIdHeader.FirstOrDefault()
                : string.Empty;

            return $"Catalogs Service Apis, RequestId: {requestId}";
        }).ExcludeFromDescription();
    }
}

In API project Program.cs file as entry point of our application, for loading and configuring modules I created a ModuleExtensions and for adding modules services we should add AddModulesServices(useCompositionRootForModules: true) in program file, and because in modular monolith we want to use separated composition root for each module and it creates separate dependency container for each module with autonomous dependency management. AddModulesServices method will create separated composition root (service provider) for each module and will Add it to CompositionRootRegistry. All modules composition root preserve in the CompositionRootRegistry static class and we can retrieve corresponding module CompositionRoot class by calling GetByModule() on CompositionRootRegistry class.

With calling AddModulesServices() in program file, it calls all modules AddModuleServices method, for example for Module Catalog it is:

public class CatalogModuleConfiguration : IModuleDefinition
{
    public void AddModuleServices(IServiceCollection services, IConfiguration configuration)
    {
        services.AddInfrastructure(configuration);
        services.AddStorage(configuration);

        services.AddBrandsServices();
        services.AddCategoriesServices();
        services.AddSuppliersServices();
        services.AddProductsServices();
    }
}

Then for configuring modules middleware we should call ConfigureModules() in program file and it calls all modules ConfigureModule method, for example for catalog module it is:

public class CatalogModuleConfiguration : IModuleDefinition
{
    public async Task ConfigureModule(
        IApplicationBuilder app,
        IConfiguration configuration,
        ILogger logger,
        IWebHostEnvironment environment)
    {
        ServiceActivator.Configure(app.ApplicationServices);

        app.SubscribeAllMessageFromAssemblyOfType<CatalogRoot>();

        await app.ApplyDatabaseMigrations(logger);
        await app.SeedData(logger, environment);
    }
}

And finally for configuring modules endpoint we should call MapModulesEndpoints() in program file and it calls all modules MapEndpoints method, for example for catalog module it is:

public class CatalogModuleConfiguration : IModuleDefinition
{
    public void MapEndpoints(IEndpointRouteBuilder endpoints)
    {
        endpoints.MapProductsEndpoints();

        endpoints.MapGet("/", (HttpContext context) =>
        {
            var requestId = context.Request.Headers.TryGetValue("X-Request-Id", out var requestIdHeader)
                ? requestIdHeader.FirstOrDefault()
                : string.Empty;

            return $"Catalogs Service Apis, RequestId: {requestId}";
        }).ExcludeFromDescription();
    }
}

Note: We don't have separated API project for each module in vertical slice architecture because they are not microervice and shouldn't host separately, so for hosting all modules with vertical slice architecture we just use one API project.

In Catalog module for Product functionalities in Products folder or other functionalities in other folders, we have a Shared Folder that contains some infrastructure things will share between all slices (for example Data-Context, ServiceCollectionExtensions.Persistence).

In vertical slice flow, we treat each request as a slice. For example for CreatingProduct feature or slice, Our flow will start with a Endpoint with name CreateProductEndpoint, actually our API project, will route user api request to this specific endpoint in catalog module. Now for running minimal api route handler in module specific composition root, we should use GatewayProcessor inner our Route Handler, because as default routes will run in application root service provider instead of specific module service provider. Now this GatewayProcessor will get corresponding service provider for specific module (here, Catalogs module) from the CompositionRootRegistry and then execute command or query of top of its new service provider.

// POST api/v1/catalog/products
public static class CreateProductEndpoint
{
    internal static IEndpointRouteBuilder MapCreateProductsEndpoint(this IEndpointRouteBuilder endpoints)
    {
        endpoints.MapPost($"{ProductsConfigs.ProductsPrefixUri}", CreateProducts)
            .WithTags(ProductsConfigs.Tag)
            .RequireAuthorization()
            .Produces<CreateProductResult>(StatusCodes.Status201Created)
            .Produces(StatusCodes.Status401Unauthorized)
            .Produces(StatusCodes.Status400BadRequest)
            .WithName("CreateProduct")
            .WithDisplayName("Create a new product.");

        return endpoints;
    }

    private static Task<IResult> CreateProducts(
        CreateProductRequest request,
        IGatewayProcessor<CatalogModuleConfiguration> gatewayProcessor,
        CancellationToken cancellationToken)
    {
        Guard.Against.Null(request, nameof(request));

        return gatewayProcessor.ExecuteCommand(async (commandProcessor, mapper) =>
        {
            var command = mapper.Map<CreateProduct>(request);
            var result = await commandProcessor.SendAsync(command, cancellationToken);

            return Results.CreatedAtRoute("GetProductById", new {id = result.Product.Id}, result);
        });
    }
}

In this endpoint we use CQRS and pass CreateProduct command to our command processor for executing and route to corresponding CreateProductHandler command handler.

public record CreateProduct(
    string Name,
    decimal Price,
    int Stock,
    int RestockThreshold,
    int MaxStockThreshold,
    ProductStatus Status,
    int Width,
    int Height,
    int Depth,
    string Size,
    ProductColor Color,
    long CategoryId,
    long SupplierId,
    long BrandId,
    string? Description = null,
    IEnumerable<CreateProductImageRequest>? Images = null) : ITxCreateCommand<CreateProductResult>
{
    public long Id { get; init; } = SnowFlakIdGenerator.NewId();
}

public class CreateProductHandler : ICommandHandler<CreateProduct, CreateProductResult>
{
    private readonly ILogger<CreateProductHandler> _logger;
    private readonly IMapper _mapper;
    private readonly ICatalogDbContext _catalogDbContext;

    public CreateProductHandler(
        ICatalogDbContext catalogDbContext,
        IMapper mapper,
        ILogger<CreateProductHandler> logger)
    {
        _logger = Guard.Against.Null(logger, nameof(logger));
        _mapper = Guard.Against.Null(mapper, nameof(mapper));
        _catalogDbContext = Guard.Against.Null(catalogDbContext, nameof(catalogDbContext));
    }

    public async Task<CreateProductResult> Handle(
        CreateProduct command,
        CancellationToken cancellationToken)
    {
        Guard.Against.Null(command, nameof(command));

        var images = command.Images?.Select(x =>
            new ProductImage(SnowFlakIdGenerator.NewId(), x.ImageUrl, x.IsMain, command.Id)).ToList();

        var category = await _catalogDbContext.FindCategoryAsync(command.CategoryId);
        Guard.Against.NotFound(category, new CategoryDomainException(command.CategoryId));

        var brand = await _catalogDbContext.FindBrandAsync(command.BrandId);
        Guard.Against.NotFound(brand, new BrandNotFoundException(command.BrandId));

        var supplier = await _catalogDbContext.FindSupplierByIdAsync(command.SupplierId);
        Guard.Against.NotFound(supplier, new SupplierNotFoundException(command.SupplierId));

        // await _domainEventDispatcher.DispatchAsync(cancellationToken, new Events.Domain.CreatingProduct());
        var product = Product.Create(
            command.Id,
            command.Name,
            Stock.Create(command.Stock, command.RestockThreshold, command.MaxStockThreshold),
            command.Status,
            Dimensions.Create(command.Width, command.Height, command.Depth),
            command.Size,
            command.Color,
            command.Description,
            command.Price,
            category!.Id,
            supplier!.Id,
            brand!.Id,
            images);

        await _catalogDbContext.Products.AddAsync(product, cancellationToken: cancellationToken);

        await _catalogDbContext.SaveChangesAsync(cancellationToken);

        var created = await _catalogDbContext.Products
            .Include(x => x.Brand)
            .Include(x => x.Category)
            .Include(x => x.Supplier)
            .SingleOrDefaultAsync(x => x.Id == product.Id, cancellationToken: cancellationToken);

        var productDto = _mapper.Map<ProductDto>(created);

        _logger.LogInformation("Product a with ID: '{ProductId} created.'", command.Id);

        return new CreateProductResult(productDto);
    }
}

This command handler will execute in a transaction with using EfTxBehavior pipeline, because CreateProduct inherits from ITxCreateCommand.

And in the end of this handler before Committing Transaction we publish our domain events to their handlers with help of DomainEventPublisher. Also after publishing our domain event handlers, if We have a valid EventMapper for mapping our domain events to integration events we can get their corresponding Integration Events for example ProductEventMapper is a event mapping file for products functionality.

These integration events will Save in the persistence message store, with help of MessagePersistenceService as StoreMessage with MessageDeliveryType Outbox for guaranty delivery before committing. After Committing Transaction our MessagePersistenceBackgroundService will send, StoreMessage with delivery type outbox to message broker.

Prerequisites

  1. This application uses Https for hosting apis, to setup a valid certificate on your machine, you can create a Self-Signed Certificate.
  2. Install git - https://git-scm.com/downloads.
  3. Install .NET Core 6.0 - https://dotnet.microsoft.com/download/dotnet/6.0.
  4. Install Visual Studio 2022, Rider or VSCode.
  5. Install docker - https://docs.docker.com/docker-for-windows/install/.
  6. Make sure that you have ~10GB disk space.
  7. Clone Project https://github.com/mehdihadeli/ecommerce-modular-monolith-sample, make sure that's compiling
  8. Run the docker-compose.infrastructure.yaml file, for running prerequisites infrastructures with docker-compose -f ./deployments/docker-compose.infrastructure.yaml -d command.
  9. Open ecommerce.sln solution.

How to Run

For Running this application we could run our application and their modules with running src/Api/ECommerce.Api/ECommerce.Api.csproj project in our Dev Environment, for me it's Rider.

For testing apis I used REST Client plugin of VSCode its related file scenarios are available in _httpclients folder. also after running api you have access to swagger open api for all modules in /swagger route path.

In this application I use a fake email sender with name of ethereal as a SMTP provider for sending email. after sending email by the application you can see the list of sent emails in ethereal messages panel. My temp username and password is available inner the all of appsettings file.

Contribution

The application is in development status. You are feel free to submit pull request or create the issue.

Project References

License

The project is under MIT license.

More Repositories

1

awesome-software-architecture

๐Ÿš€ A curated list of awesome articles, videos, and other resources to learn and practice software architecture, patterns, and principles.
3,387
star
2

go-food-delivery-microservices

๐Ÿ• A practical food delivery microservices, built with golang, domain-driven design, cqrs, event sourcing, vertical slice architecture, event-driven architecture, and the latest technologies.
Go
741
star
3

food-delivery-microservices

๐Ÿ” A practical food delivery microservices, built with .Net 7, MassTransit, Domain-Driven Design, CQRS, Vertical Slice Architecture, Event-Driven Architecture, and the latest technologies.
C#
690
star
4

awesome-dotnet-core-education

A curated list of awesome articles and resources for learning and practicing .Net Core and its related technologies.
C#
347
star
5

awesome-go-education

A curated list of awesome articles and resources for learning and practicing Go and its related technologies.
Go
327
star
6

Go-MediatR

๐Ÿšƒ A library for handling mediator pattern and simplified CQRS pattern within an event driven architecture. inspired by csharp MediatR library.
Go
184
star
7

ecommerce-microservices

๐Ÿ›๏ธ A practical e-commerce Microservices based on Domain Driven Design, Vertical Slice Architecture, CQRS pattern, Event Driven Architecture.
C#
164
star
8

vertical-slice-api-template

An asp.net core template based on .Net 8, Vertical Slice Architecture, CQRS, Minimal APIs, API Versioning and Swagger.
C#
94
star
9

awesome-dotnet-async

A curated list of awesome articles and resources to learning and practicing about async, threading, and channels in .Net platform. ๐Ÿ˜‰
C#
91
star
10

micro-bootstrap

A Full Stack framework written in .NET Core to speed up your development process in microservices and modular monolith apps. It gathers most widely used frameworks in .NET world and pack them into a simple bootstrap package.
C#
77
star
11

go-vertical-slice-template

A Golang boilerplate template, based on Vertical Slice Architecture and CQRS pattern with using Echo, Gorm, Zap, Viper, MediatR for CQRS and sarulabs/di for Dependency Injection.
Go
50
star
12

store-modular-monolith

๐Ÿ›’ Implementing an โ€œonline storeโ€ modular monolith application with domain-driven design and CQRS with using in-memory message broker based on .Net Core.
C#
34
star
13

game-leaderboard-microservices

๐ŸŽฎ Implementation of an imaginary Game Leader Board application, based on Microservices Architecture, Event Driven Architecture, Vertical Slice Architecture, Event Sourcing with EventStoreDB, Redis SortedSet, Redis Pub/Sub, SignalR and .Net 8.
C#
34
star
14

movie-search-application

๐ŸŽฌ A simple movie search app, built with .Net 7, Vertical Slice Architecture and using TMDB APIs and YouTube APIs for searching and details of the movies.
C#
30
star
15

distributed-chat-system

A simple distributed chat system based on .net core, signalr and NATS messaging queue.
C#
11
star
16

Jwt-Authentication-Server

This repository is an Auth Server with jwt token for .net core
C#
8
star
17

tdd-sample

A sample project demonstrating Test-Driven Development (TDD) using .Net 8 and Vertical Slice Architecture based on Minimal APIs in .NET Core
C#
8
star
18

.Net-Core-CQRS-Shopping

This project in a shopping app using DDD, CQRS, Clean Architecture, .Net Core, Unit, and E2E Testing.
C#
6
star
19

mehdihadeli

5
star
20

ecommerce-microservices-wolverine

๐Ÿ›๏ธ A practical e-commerce microservices, built with .Net 8, Wolverine, Domain-Driven Design, CQRS, Vertical Slice Architecture, Event-Driven Architecture, and the latest technologies.
5
star
21

game-microservices

This project is a leader board gaming microservices with using some cloud native tools and technologies based on .net core.
C#
4
star
22

thesaurus-application

This application is a thesaurus app for defining and searching some words and their meanings, synonyms built on top of a vertical slice architecture, cqrs, mongodb and unit and integration testing in backend and using angular in frontend.
C#
3
star
23

design-pattern-samples

JavaScript
2
star
24

leetcode-playground

Leetcode problems and solutions in C# and Golang
Go
2
star
25

store-microservices

Implementing an โ€œonline storeโ€ microservices application with domain-driven design and CQRS with using the latest technologies and cloud native technologies.
2
star
26

dotnet-gitpod

Shell
1
star
27

envoy-samples

Go
1
star
28

Go-Bus

1
star
29

blog-samples

C#
1
star
30

ecommerce-microservices-aspire

๐Ÿ›๏ธ A practical e-commerce microservices, built with .Net 8, Microsoft Aspire, Domain-Driven Design, CQRS, Vertical Slice Architecture, Event-Driven Architecture, and the latest technologies.
1
star
31

partners-management

C#
1
star
32

Vertical-Slice-Architecture-Sample

C#
1
star
33

energy-management

C#
1
star
34

release-note

Shell
1
star
35

blog-comments

The blog comments discussions for https://dotnetuniversity.com
1
star
36

ProblemSolving

C#
1
star