A Clean Architectureβbased e-commerce platform leveraging ASP.NET Core 6, Razor Pages, Entity Framework Core 6, MediatR, and FluentValidation. The solution is divided into five layers to enforce separation of concerns, facilitate TDD, and enable microservices extraction.
- Solution Structure
- Core Concepts
- Domain Model
- Application Layer
- Persistence Layer
- Presentation Layer
- Configuration & Startup
- Database & Migrations
- Sample Usage
- Testing & CI/CD
- Deployment
- Contributing
- License
Online_Store
βββ Shop.Presentation # Razor Pages UI (ASP.NET Core Web)
β βββ Pages
β βββ ViewModels
β βββ Startup.cs
βββ Shop.Application # CQRS handlers, DTOs, Validators
β βββ Commands
β β βββ CreateProductCommand.cs
β βββ Queries
β β βββ GetProductByIdQuery.cs
β βββ Validators
βββ Shop.Domain # Entities, ValueObjects, Interfaces
β βββ Entities
β β βββ Product.cs
β β βββ Category.cs
β β βββ Order.cs
β βββ ValueObjects
β β βββ Money.cs
β βββ Interfaces
β βββ IProductRepository.cs
βββ Shop.Persistence # EF Core DbContext, Repositories, Migrations
β βββ Configurations # IEntityTypeConfiguration<T> implementations
β βββ Repositories
β βββ ApplicationDbContext.cs
βββ Shop.Ccommon # Shared middleware, constants, extensions
Note:
ClassLibrary1
is deprecated β merge its utilities intoShop.Ccommon
or remove entirely.
- Clean Architecture: Layers depend inward only, enforcing
Presentation β Application β Domain β Persistence
. - CQRS with MediatR: Segregate
Commands
(state changes) fromQueries
(reads). - Entity Framework Core: Code-First approach with
IEntityTypeConfiguration
for fluent model configuration. - FluentValidation: Strongly-typed validators for each command/request.
- Dependency Injection: All services registered in
Startup.ConfigureServices
.
public class Product : BaseEntity
{
public string Name { get; private set; }
public string Description { get; private set; }
public Money Price { get; private set; }
public int Stock { get; private set; }
public Guid CategoryId { get; private set; }
public Category Category { get; private set; }
}
public struct Money
{
public decimal Amount { get; }
public string Currency { get; }
public Money(decimal amount, string currency) { Amount = amount; Currency = currency; }
}
public class Category : BaseEntity { public string Title { get; private set; } }
- BaseEntity holds
Id
,CreatedAt
,UpdatedAt
. - Relationships configured via Fluent API in
Configurations/ProductConfiguration
.
- Commands (e.g.,
CreateProductCommand : IRequest<Guid>
) - Queries (e.g.,
GetProductByIdQuery : IRequest<ProductDto>
) - Handlers implement
IRequestHandler<,>
. - DTOs decouple domain from UI.
- Validators (
CreateProductCommandValidator
uses FluentValidation rules).
Example: CreateProductCommand
```csharp public class CreateProductCommand : IRequest { public string Name { get; set; } public string Description { get; set; } public decimal Price { get; set; } public string Currency { get; set; } public int Stock { get; set; } public Guid CategoryId { get; set; } }public class CreateProductCommandHandler : IRequestHandler<CreateProductCommand, Guid> { private readonly IProductRepository _repo; public CreateProductCommandHandler(IProductRepository repo) => _repo = repo;
public async Task<Guid> Handle(CreateProductCommand cmd, CancellationToken ct)
{
var product = new Product(cmd.Name, cmd.Description,
new Money(cmd.Price, cmd.Currency), cmd.Stock, cmd.CategoryId);
await _repo.AddAsync(product);
await _repo.UnitOfWork.SaveChangesAsync(ct);
return product.Id;
}
}
</details>
## πΎ Persistence Layer
- **ApplicationDbContext** inherits `DbContext`.
- Fluent API configurations in `Configurations/` folder:
```csharp
public class ProductConfiguration : IEntityTypeConfiguration<Product>
{
public void Configure(EntityTypeBuilder<Product> builder)
{
builder.HasKey(p => p.Id);
builder.OwnsOne(p => p.Price);
builder.Property(p => p.Name).IsRequired().HasMaxLength(100);
// ...
}
}
- Repositories implement domain interfaces; e.g.,
EfProductRepository
.
- Razor Pages under
Pages/Products
,Pages/Orders
. - PageModel classes inject
IMediator
to send commands/queries. - Auto-generated TagHelpers for common controls.
- _Layout.cshtml includes Bootstrap and site navigation.
public class CreateModel : PageModel
{
private readonly IMediator _mediator;
public CreateModel(IMediator mediator) => _mediator = mediator;
[BindProperty] public CreateProductCommand Input { get; set; }
public async Task<IActionResult> OnPostAsync()
{
if (!ModelState.IsValid) return Page();
var id = await _mediator.Send(Input);
return RedirectToPage("Details", new { id });
}
}
In Shop.Presentation/Startup.cs
:
public void ConfigureServices(IServiceCollection services)
{
services.AddRazorPages();
services.AddDbContext<ApplicationDbContext>(opts =>
opts.UseSqlServer(Configuration.GetConnectionString("DefaultConnection")));
services.AddMediatR(typeof(CreateProductCommand));
services.AddValidatorsFromAssembly(typeof(CreateProductCommandValidator).Assembly);
services.AddScoped<IProductRepository, EfProductRepository>();
services.AddCors();
}
public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
app.UseExceptionHandling(); // Custom middleware
app.UseRouting();
app.UseAuthorization();
app.UseEndpoints(endpoints => { endpoints.MapRazorPages(); });
}
cd Shop.Persistence
dotnet ef migrations add InitialCreate
dotnet ef database update
- Migration files in
Shop.Persistence/Migrations
. - Seeding in
ApplicationDbContext.OnModelCreating
.
-
Create a Category via SQL seed or UI.
-
Add a Product:
POST /products Content-Type: application/json { "name": "Laptop", "description": "High-end gaming laptop", "price": 1499.99, "currency": "USD", "stock": 10, "categoryId": "<existing-guid>" }
-
View Products at
/Products/Index
.
-
Unit Tests: Scaffold
Shop.Tests
(xUnit + Moq). Example:[Fact] public async Task CreateProduct_SavesToRepository() { /* Arrange, Act, Assert */ }
-
GitHub Actions workflow (
.github/workflows/dotnet.yml
) runsdotnet test
on PRs.
- Docker: Add
Dockerfile
with multi-stage build. - Kubernetes: Provide Helm charts for microservice extraction.
- Cloud: Deploy via Azure App Service or AWS EKS.
- Fork and clone.
- Create branch:
feature/awesome
- Commit, push, and open PR.
- Ensure all tests pass.
Distributed under the MIT License. See LICENSE.