Skip to content

Sperolina Online Shop is a modern and scalable e-commerce platform, developed using ASP.NET with Razor Pages and a Clean Architecture pattern. The system is designed with microservices to ensure high flexibility, maintainability, and scalability.

Notifications You must be signed in to change notification settings

PariCoderDeveloper/Online_Store

Repository files navigation

Build Status License: MIT

Sperolina Online Store

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.


πŸ“‹ Table of Contents

  1. Solution Structure
  2. Core Concepts
  3. Domain Model
  4. Application Layer
  5. Persistence Layer
  6. Presentation Layer
  7. Configuration & Startup
  8. Database & Migrations
  9. Sample Usage
  10. Testing & CI/CD
  11. Deployment
  12. Contributing
  13. License

πŸ—‚ Solution Structure

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 into Shop.Ccommon or remove entirely.

🧩 Core Concepts

  • Clean Architecture: Layers depend inward only, enforcing Presentation β†’ Application β†’ Domain β†’ Persistence.
  • CQRS with MediatR: Segregate Commands (state changes) from Queries (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.

🏷 Domain Model

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.

πŸš€ Application Layer

  • 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.

πŸ’» Presentation Layer

  • 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 });
    }
}

βš™οΈ Configuration & Startup

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(); });
}

πŸ—„ Database & Migrations

cd Shop.Persistence
dotnet ef migrations add InitialCreate
dotnet ef database update
  • Migration files in Shop.Persistence/Migrations.
  • Seeding in ApplicationDbContext.OnModelCreating.

πŸ“‘ Sample Usage

  1. Create a Category via SQL seed or UI.

  2. 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>"
    }
  3. View Products at /Products/Index.

πŸ§ͺ Testing & CI/CD

  • Unit Tests: Scaffold Shop.Tests (xUnit + Moq). Example:

    [Fact]
    public async Task CreateProduct_SavesToRepository()
    { /* Arrange, Act, Assert */ }
  • GitHub Actions workflow (.github/workflows/dotnet.yml) runs dotnet test on PRs.

πŸš€ Deployment

  • Docker: Add Dockerfile with multi-stage build.
  • Kubernetes: Provide Helm charts for microservice extraction.
  • Cloud: Deploy via Azure App Service or AWS EKS.

🀝 Contributing

  1. Fork and clone.
  2. Create branch: feature/awesome
  3. Commit, push, and open PR.
  4. Ensure all tests pass.

πŸ“„ License

Distributed under the MIT License. See LICENSE.

About

Sperolina Online Shop is a modern and scalable e-commerce platform, developed using ASP.NET with Razor Pages and a Clean Architecture pattern. The system is designed with microservices to ensure high flexibility, maintainability, and scalability.

Topics

Resources

Stars

Watchers

Forks