Introduction
Building an e-commerce application involves more than displaying products on a web page. The application needs to handle product catalogs, customer accounts, orders, payments, inventory, and potentially thousands of concurrent requests.
The challenge becomes more significant during high-traffic events such as Black Friday or seasonal sales. The application needs to remain responsive while maintaining security, reliability, and scalability.
.NET provides a strong foundation for this type of application. ASP.NET Core can be used to build the backend APIs, while technologies such as Entity Framework Core, Redis, SignalR, Docker, and Kubernetes can be introduced as the application's requirements grow.
In this article, we will use an e-commerce application as a real-world example to understand how these technologies can work together.
E-Commerce Application Architecture
Consider an online store where customers can:
Browse products
Search for products
Register and log in
Add products to a shopping cart
Place orders
Track order status
Receive real-time notifications
A simplified architecture could look like this:
Customer
|
Angular / React
|
HTTPS
|
ASP.NET Core Web API
|
+---------------+----------------+
| | |
Products Orders Identity
| | |
+---------------+----------------+
|
Application Layer
|
+---------------+----------------+
| | |
EF Core Redis SignalR
| | |
SQL Server Cache Layer Real-Time Events
For larger deployments, the API can run inside containers and be scaled horizontally using a container orchestration platform.
Step 1: Create an ASP.NET Core Web API
The backend can start as a standard ASP.NET Core Web API project.
Using the .NET CLI:
dotnet new webapi -n EcommerceApi
cd EcommerceApi
dotnet run
The API can expose endpoints for products, customers, carts, and orders.
For example:
GET /api/products
GET /api/products/{id}
POST /api/orders
GET /api/orders/{id}
The frontend application can communicate with these endpoints using HTTP.
Step 2: Create a Product Model
An e-commerce application needs a model to represent products.
public class Product
{
public int Id { get; set; }
public string Name { get; set; } = string.Empty;
public decimal Price { get; set; }
public int StockQuantity { get; set; }
public string Category { get; set; } = string.Empty;
}
This model contains basic product information such as the product name, price, category, and available stock.
Step 3: Configure Entity Framework Core
Entity Framework Core can be used to communicate with a relational database.
For example, a DbContext can be created as follows:
using Microsoft.EntityFrameworkCore;
public class EcommerceDbContext : DbContext
{
public EcommerceDbContext(
DbContextOptions<EcommerceDbContext> options)
: base(options)
{
}
public DbSet<Product> Products => Set<Product>();
}
Register the context in Program.cs:
builder.Services.AddDbContext<EcommerceDbContext>(
options =>
options.UseSqlServer(
builder.Configuration.GetConnectionString(
"EcommerceDatabase")));
A connection string can be stored in configuration:
{
"ConnectionStrings": {
"EcommerceDatabase": "Server=localhost;Database=EcommerceDb;Trusted_Connection=True;TrustServerCertificate=True"
}
}
In a production environment, connection strings and other secrets should be managed through an appropriate secret-management solution rather than committed to source control.
Step 4: Create a Product API
The API can expose product information to the frontend.
using Microsoft.AspNetCore.Mvc;
using Microsoft.EntityFrameworkCore;
[ApiController]
[Route("api/[controller]")]
public class ProductsController : ControllerBase
{
private readonly EcommerceDbContext _context;
public ProductsController(EcommerceDbContext context)
{
_context = context;
}
[HttpGet]
public async Task<ActionResult<List<Product>>> GetProducts()
{
var products = await _context.Products
.AsNoTracking()
.ToListAsync();
return Ok(products);
}
[HttpGet("{id:int}")]
public async Task<ActionResult<Product>> GetProduct(int id)
{
var product = await _context.Products
.AsNoTracking()
.FirstOrDefaultAsync(p => p.Id == id);
if (product == null)
return NotFound();
return Ok(product);
}
}
AsNoTracking() is useful for read-only queries because the returned entities do not need to be tracked for updates.
A request such as:
GET /api/products
could return:
[
{
"id": 1,
"name": "Laptop",
"price": 65000,
"stockQuantity": 25,
"category": "Electronics"
},
{
"id": 2,
"name": "Wireless Mouse",
"price": 1200,
"stockQuantity": 100,
"category": "Accessories"
}
]
Step 5: Handle Orders
Orders are another important part of an e-commerce application.
A simplified order model might look like this:
public class Order
{
public int Id { get; set; }
public int CustomerId { get; set; }
public decimal TotalAmount { get; set; }
public string Status { get; set; } = "Pending";
public DateTime CreatedAt { get; set; }
}
A basic order endpoint could be:
[HttpPost]
public async Task<IActionResult> CreateOrder(Order order)
{
order.CreatedAt = DateTime.UtcNow;
order.Status = "Pending";
_context.Orders.Add(order);
await _context.SaveChangesAsync();
return CreatedAtAction(
nameof(GetOrder),
new { id = order.Id },
order);
}
In a production system, order creation should involve additional validation, inventory checks, transaction handling, payment processing, and business rules. The simplified example demonstrates only the API flow.
Step 6: Improve Performance with Redis
During a large sale, popular products may be requested thousands of times.
Without caching, every request could result in another database query:
Customer
↓
API
↓
Database
A cache can reduce unnecessary database access:
Customer
↓
API
↓
Redis Cache
↓
Database only when required
A simplified caching service could look like this:
public interface IProductCache
{
Task<Product?> GetAsync(int productId);
Task SetAsync(Product product);
}
The application can first check Redis for the requested product. If the product is not available in the cache, it can retrieve the data from the database and then cache it.
Conceptually:
var product = await cache.GetAsync(productId);
if (product == null)
{
product = await database.GetProductAsync(productId);
if (product != null)
{
await cache.SetAsync(product);
}
}
The exact implementation depends on the Redis client and caching approach used by the application.
Caching should be designed carefully. Frequently changing information such as inventory requires an appropriate expiration and invalidation strategy to avoid serving stale data.
Step 7: Add Authentication and Authorization
Customer accounts require authentication and authorization.
ASP.NET Core provides authentication and authorization infrastructure that can be integrated with identity systems.
For example, an API endpoint can require an authenticated user:
[Authorize]
[HttpGet("my-orders")]
public async Task<IActionResult> GetMyOrders()
{
// Retrieve orders for the authenticated customer.
return Ok();
}
Role-based authorization can also be applied:
[Authorize(Roles = "Admin")]
[HttpDelete("{id:int}")]
public async Task<IActionResult> DeleteProduct(int id)
{
// Delete or deactivate the product.
return NoContent();
}
For token-based APIs, JWT bearer authentication can be configured when appropriate.
Authentication alone is not enough for an e-commerce application. Authorization, input validation, secure secret storage, HTTPS, logging, dependency updates, and protection of sensitive business operations are also important.
Step 8: Handle Payments Through a Payment Provider
An e-commerce application should not unnecessarily handle or store raw credit-card information.
Instead, a payment provider can be integrated into the checkout process.
A simplified flow is:
Customer
↓
Checkout
↓
E-Commerce API
↓
Payment Provider
↓
Payment Result
↓
Order Status Updated
The exact implementation depends on the selected payment provider and its SDK/API.
The application should follow the provider's security and compliance requirements rather than attempting to implement card processing itself.
Step 9: Add Real-Time Updates with SignalR
Some e-commerce features benefit from real-time communication.
For example, customers could receive an order-status update without repeatedly refreshing the page.
ASP.NET Core SignalR can be used to establish real-time communication between the server and connected clients.
A basic hub can be created as follows:
using Microsoft.AspNetCore.SignalR;
public class OrderHub : Hub
{
}
Register the hub in Program.cs:
builder.Services.AddSignalR();
var app = builder.Build();
app.MapHub<OrderHub>("/hubs/orders");
The application can then send an event when an order status changes.
For example:
Order Placed
↓
Payment Confirmed
↓
Order Packed
↓
Order Shipped
↓
Order Delivered
The frontend can receive these events and update the user interface without requiring a page refresh.
SignalR is also useful for scenarios such as inventory notifications, customer-service updates, and limited-time sale events.
Step 10: Containerize the Application
As traffic grows, the application may need to run multiple instances.
Docker can package the application and its dependencies into a container.
A simplified Dockerfile could look like this:
FROM mcr.microsoft.com/dotnet/aspnet:8.0 AS runtime
WORKDIR /app
EXPOSE 8080
FROM mcr.microsoft.com/dotnet/sdk:8.0 AS build
WORKDIR /src
COPY . .
RUN dotnet restore
RUN dotnet publish EcommerceApi.csproj \
-c Release \
-o /app/publish \
--no-restore
FROM runtime AS final
WORKDIR /app
COPY --from=build /app/publish .
ENTRYPOINT ["dotnet", "EcommerceApi.dll"]
The exact runtime and SDK versions should match the .NET version targeted by the application.
The resulting container can be deployed consistently across development, testing, and production environments.
Step 11: Scale the Application
During normal traffic, one or a few API instances may be sufficient.
During a major sale, additional instances can be added:
Load Balancer
|
+------------+------------+
| | |
API #1 API #2 API #3
| | |
+------------+------------+
|
Shared Services
/ | \
Redis Database Message Systems
Container orchestration platforms can help manage multiple application instances, health checks, service discovery, and scaling.
However, simply adding more API instances does not automatically solve every scalability problem. Database capacity, connection pools, cache design, external services, background jobs, and traffic distribution must also be considered.
Step 12: Set Up CI/CD
A CI/CD pipeline can automate testing and deployment.
A typical workflow is:
Developer
↓
Git Repository
↓
Build
↓
Automated Tests
↓
Container Build
↓
Security Checks
↓
Deployment
GitHub Actions or Azure DevOps can be used to implement such workflows.
A simplified GitHub Actions example might look like:
name: Build Ecommerce API
on:
push:
branches:
- main
jobs:
build:
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v4
- name: Setup .NET
uses: actions/setup-dotnet@v4
with:
dotnet-version: '8.0.x'
- name: Restore
run: dotnet restore
- name: Build
run: dotnet build --no-restore --configuration Release
- name: Test
run: dotnet test --no-build --configuration Release
A production pipeline would typically include additional stages for security scanning, artifact management, container publishing, deployment approvals, and environment-specific configuration.
Cross-Platform Development
One of the advantages of modern .NET is its cross-platform support.
The same ASP.NET Core application can be developed and hosted across supported operating systems.
For example:
Development
Windows / macOS / Linux
↓
ASP.NET Core
↓
Docker Image
↓
Production Environment
This provides flexibility when choosing development machines and hosting environments.
Performance Considerations
Performance is particularly important for e-commerce applications.
Several areas should be considered:
Caching
Use caching for data that is frequently read and does not need to be retrieved from the database on every request.
Database Optimization
Use appropriate indexes, efficient queries, pagination, and suitable database design.
Asynchronous APIs
Use asynchronous database and I/O operations where appropriate:
var products = await _context.Products
.AsNoTracking()
.ToListAsync();
Load Testing
Before a major sale, test realistic traffic patterns rather than assuming the system can handle a particular number of users.
Observability
Monitor:
Request latency
Error rates
Database performance
Cache hit/miss rates
CPU and memory usage
External service failures
Performance should be measured using actual application workloads rather than relying on generic performance claims.
Security Considerations
Security should be considered throughout the application architecture.
Important areas include:
HTTPS/TLS
Authentication
Authorization
Input validation
Secure secret management
Protection against common web vulnerabilities
Dependency and container updates
Logging and monitoring
Rate limiting
Secure payment-provider integration
Sensitive information should not be written to application logs.
For example, avoid logging:
Credit Card Number
Password
Access Token
Authentication Secret
Security controls should be applied based on the application's actual threat model and compliance requirements.
Benefits of Using ASP.NET Core for E-Commerce
Using ASP.NET Core as the backend provides several architectural advantages.
Cross-Platform Development
Applications can run across supported operating systems and can be packaged into containers.
Scalable API Architecture
ASP.NET Core can be used to build APIs that can be deployed as multiple instances behind a load balancer.
Strong Database Integration
Entity Framework Core provides an object-relational mapping approach for working with relational databases.
Real-Time Communication
SignalR provides an option for scenarios that require server-to-client real-time communication.
Cloud and Container Support
ASP.NET Core applications can be packaged into containers and deployed to various hosting environments.
Flexible Architecture
The application can start as a modular monolith and evolve toward more distributed components when there is a clear business or technical need.
Example Technology Stack
A possible technology stack for this e-commerce application is:
Requirement | Technology |
|---|---|
Backend API | ASP.NET Core |
Frontend | Angular or React |
ORM | Entity Framework Core |
Relational Database | SQL Server or PostgreSQL |
Caching | Redis |
Real-Time Communication | SignalR |
Authentication | ASP.NET Core Authentication/Identity |
Containerization | Docker |
Orchestration | Kubernetes or another platform |
CI/CD | GitHub Actions or Azure DevOps |
Cloud Hosting | Azure or another cloud provider |
The actual technology choices should be based on the application's requirements, team expertise, operational model, and expected workload.
Conclusion
An e-commerce platform is a good example of where several application-development concerns come together. The backend needs to handle products, customers, orders, payments, inventory, authentication, and real-time communication while remaining maintainable as the business grows.
ASP.NET Core can provide the API foundation, while Entity Framework Core can handle relational data access, Redis can reduce repeated reads, SignalR can support real-time scenarios, and Docker can provide a consistent application package for deployment.
For larger workloads, multiple application instances can be deployed behind a load balancer, while CI/CD automation can help deliver tested changes consistently.
The important lesson is that scalability does not come from a single technology. A reliable e-commerce platform requires thoughtful API design, database optimization, caching, security, observability, testing, and deployment architecture.
Starting with a well-structured ASP.NET Core application and introducing additional infrastructure when the requirements justify it provides a practical path for building an e-commerce system that can evolve with the business.

Join the conversation! Your thoughts help the community grow.