Series: From Code to Cloud: Building a Production-Ready .NET Application
By: Farrukh Rehman - Senior .NET Full Stack Developer / Team Lead
LinkedIn: https://linkedin.com/in/farrukh-rehman
GitHub: https://github.com/farrukh1212cs
Source Code Backend : https://github.com/farrukh1212cs/ECommerce-Backend.git
Source Code Frontend : https://github.com/farrukh1212cs/ECommerce-Frontend.git
Introduction
In this lecture, we’ll focus on implementing the Application Layer —
this is the layer that connects our Domain logic (repositories) to the API layer.
It contains:
- DTOs (Data Transfer Objects) → used to safely send/receive data between layers.
- Services → contain business workflows using domain repositories.
The goal is to keep:
- The Domain layer independent and pure (entities + repository interfaces).
- The Infrastructure layer focused only on persistence (EF Core).
- The Application layer responsible for business use cases.
Folder Structure Overview
ECommerce.Application/DTOs/ProductDto.cs
namespace ECommerce.Application.DTOs;
public class ProductDto
{
public Guid Id { get; set; }
public string Name { get; set; } = string.Empty;
public string Description { get; set; } = string.Empty;
public decimal Price { get; set; }
public int Stock { get; set; }
}
ECommerce.Application/DTOs/CustomerDto.cs
namespace ECommerce.Application.DTOs;
public class CustomerDto
{
public Guid Id { get; set; }
public string FirstName { get; set; } = string.Empty;
public string LastName { get; set; } = string.Empty;
public string Email { get; set; } = string.Empty;
public string PhoneNumber { get; set; } = string.Empty;
public string FullName => $"{FirstName} {LastName}";
}
ECommerce.Application/DTOs/OrderDto.cs
using ECommerce.Domain.Enums;
namespace ECommerce.Application.DTOs;
public class OrderDto
{
public Guid Id { get; set; }
public Guid CustomerId { get; set; }
public DateTime OrderDate { get; set; }
public decimal TotalAmount { get; set; }
public OrderStatus Status { get; set; }
public List<OrderItemDto> Items { get; set; } = new();
}
ECommerce.Application/DTOs/OrderItemDto.cs
namespace ECommerce.Application.DTOs;
public class OrderItemDto
{
public Guid Id { get; set; }
public Guid OrderId { get; set; }
public Guid ProductId { get; set; }
public string ProductName { get; set; } = string.Empty;
public decimal UnitPrice { get; set; }
public int Quantity { get; set; }
public decimal TotalPrice => UnitPrice * Quantity;
}
Service Interfaces
We’ll now define service interfaces that describe the available operations for each entity.
ECommerce.Application/Services/Interfaces/IProductService.cs
using ECommerce.Application.DTOs;
namespace ECommerce.Application.Services.Interfaces;
public interface IProductService
{
Task<IEnumerable<ProductDto>> GetAllAsync();
Task<ProductDto?> GetByIdAsync(Guid id);
Task<ProductDto> AddAsync(ProductDto dto);
Task UpdateAsync(ProductDto dto);
Task DeleteAsync(Guid id);
}
ECommerce.Application/Services/Interfaces/ICustomerService.cs
using ECommerce.Application.DTOs;
namespace ECommerce.Application.Services.Interfaces;
public interface ICustomerService
{
Task<IEnumerable<CustomerDto>> GetAllAsync();
Task<CustomerDto?> GetByIdAsync(Guid id);
Task<CustomerDto> AddAsync(CustomerDto dto);
Task UpdateAsync(CustomerDto dto);
Task DeleteAsync(Guid id);
}
ECommerce.Application/Services/Interfaces/IOrderService.cs
using ECommerce.Application.DTOs;
namespace ECommerce.Application.Services.Interfaces;
public interface IOrderService
{
Task<IEnumerable<OrderDto>> GetAllAsync();
Task<OrderDto?> GetByIdAsync(Guid id);
Task<OrderDto> AddAsync(OrderDto dto);
Task UpdateAsync(OrderDto dto);
Task DeleteAsync(Guid id);
}
ECommerce.Application/Services/Interfaces/IOrderItemService.cs
uusing ECommerce.Application.DTOs;
namespace ECommerce.Application.Services.Interfaces;
public interface IOrderItemService
{
Task<IEnumerable<OrderItemDto>> GetByOrderIdAsync(Guid orderId);
Task<IEnumerable<OrderItemDto>> GetAllAsync();
}
Service Implementations
Next, we’ll implement concrete service classes that depend on repository interfaces from the Domain layer.
ECommerce.Application/Services/Implementations/ProductService.cs
using ECommerce.Application.DTOs;
using ECommerce.Application.Services.Interfaces;
using ECommerce.Domain.Entities;
using ECommerce.Domain.Repositories;
namespace ECommerce.Application.Services.Implementations;
public class ProductService : IProductService
{
private readonly IProductRepository _repository;
public ProductService(IProductRepository repository)
{
_repository = repository;
}
public async Task<IEnumerable<ProductDto>> GetAllAsync()
{
var products = await _repository.GetAllAsync();
return products.Select(p => new ProductDto
{
Id = p.Id,
Name = p.Name,
Description = p.Description,
Price = p.Price,
Stock = p.Stock
});
}
public async Task<ProductDto?> GetByIdAsync(Guid id)
{
var p = await _repository.GetByIdAsync(id);
if (p == null) return null;
return new ProductDto
{
Id = p.Id,
Name = p.Name,
Description = p.Description,
Price = p.Price,
Stock = p.Stock
};
}
public async Task<ProductDto> AddAsync(ProductDto dto)
{
var entity = new Product
{
Name = dto.Name,
Description = dto.Description,
Price = dto.Price,
Stock = dto.Stock
};
await _repository.AddAsync(entity);
dto.Id = entity.Id;
return dto;
}
public async Task UpdateAsync(ProductDto dto)
{
var entity = await _repository.GetByIdAsync(dto.Id);
if (entity == null) return;
entity.Name = dto.Name;
entity.Description = dto.Description;
entity.Price = dto.Price;
entity.Stock = dto.Stock;
await _repository.UpdateAsync(entity);
}
public async Task DeleteAsync(Guid id) => await _repository.DeleteAsync(id);
}
ECommerce.Application/Services/Implementations/CustomerService.cs
using ECommerce.Application.DTOs;
using ECommerce.Application.Services.Interfaces;
using ECommerce.Domain.Entities;
using ECommerce.Domain.Repositories;
namespace ECommerce.Application.Services.Implementations;
public class CustomerService : ICustomerService
{
private readonly ICustomerRepository _repository;
public CustomerService(ICustomerRepository repository)
{
_repository = repository;
}
public async Task<IEnumerable<CustomerDto>> GetAllAsync()
{
var customers = await _repository.GetAllAsync();
return customers.Select(c => new CustomerDto
{
Id = c.Id,
FirstName = c.FirstName,
LastName = c.LastName,
Email = c.Email,
PhoneNumber = c.PhoneNumber
});
}
public async Task<CustomerDto?> GetByIdAsync(Guid id)
{
var c = await _repository.GetByIdAsync(id);
if (c == null) return null;
return new CustomerDto
{
Id = c.Id,
FirstName = c.FirstName,
LastName = c.LastName,
Email = c.Email,
PhoneNumber = c.PhoneNumber
};
}
public async Task<CustomerDto> AddAsync(CustomerDto dto)
{
var entity = new Customer
{
FirstName = dto.FirstName,
LastName = dto.LastName,
Email = dto.Email,
PhoneNumber = dto.PhoneNumber
};
await _repository.AddAsync(entity);
dto.Id = entity.Id;
return dto;
}
public async Task UpdateAsync(CustomerDto dto)
{
var entity = await _repository.GetByIdAsync(dto.Id);
if (entity == null) return;
entity.FirstName = dto.FirstName;
entity.LastName = dto.LastName;
entity.Email = dto.Email;
entity.PhoneNumber = dto.PhoneNumber;
await _repository.UpdateAsync(entity);
}
public async Task DeleteAsync(Guid id) => await _repository.DeleteAsync(id);
}
ECommerce.Application/Services/Implementations/OrderService.cs
using ECommerce.Application.DTOs;
using ECommerce.Application.Services.Interfaces;
using ECommerce.Domain.Entities;
using ECommerce.Domain.Repositories;
namespace ECommerce.Application.Services.Implementations;
public class OrderService : IOrderService
{
private readonly IOrderRepository _repository;
private readonly ICustomerRepository _customerRepository;
private readonly IProductRepository _productRepository;
public OrderService(
IOrderRepository repository,
ICustomerRepository customerRepository,
IProductRepository productRepository)
{
_repository = repository;
_customerRepository = customerRepository;
_productRepository = productRepository;
}
public async Task<IEnumerable<OrderDto>> GetAllAsync()
{
var orders = await _repository.GetAllAsync();
return orders.Select(o => new OrderDto
{
Id = o.Id,
CustomerId = o.CustomerId,
OrderDate = o.OrderDate,
TotalAmount = o.TotalAmount,
Status = o.Status,
Items = o.Items.Select(i => new OrderItemDto
{
Id = i.Id,
ProductId = i.ProductId,
ProductName = i.ProductName,
Quantity = i.Quantity,
UnitPrice = i.UnitPrice
}).ToList()
});
}
public async Task<OrderDto?> GetByIdAsync(Guid id)
{
var o = await _repository.GetByIdAsync(id);
if (o == null) return null;
return new OrderDto
{
Id = o.Id,
CustomerId = o.CustomerId,
OrderDate = o.OrderDate,
TotalAmount = o.TotalAmount,
Status = o.Status,
Items = o.Items.Select(i => new OrderItemDto
{
Id = i.Id,
ProductId = i.ProductId,
ProductName = i.ProductName,
Quantity = i.Quantity,
UnitPrice = i.UnitPrice
}).ToList()
};
}
public async Task<OrderDto> AddAsync(OrderDto dto)
{
var entity = new Order
{
CustomerId = dto.CustomerId,
OrderDate = dto.OrderDate
};
foreach (var itemDto in dto.Items)
{
var product = await _productRepository.GetByIdAsync(itemDto.ProductId);
if (product == null) continue;
entity.Items.Add(new OrderItem
{
ProductId = product.Id,
ProductName = product.Name,
Quantity = itemDto.Quantity,
UnitPrice = product.Price
});
}
entity.CalculateTotal();
await _repository.AddAsync(entity);
dto.Id = entity.Id;
dto.TotalAmount = entity.TotalAmount;
return dto;
}
public async Task UpdateAsync(OrderDto dto)
{
var order = await _repository.GetByIdAsync(dto.Id);
if (order == null) return;
order.Status = dto.Status;
await _repository.UpdateAsync(order);
}
public async Task DeleteAsync(Guid id) => await _repository.DeleteAsync(id);
}
ECommerce.Application/Services/Implementations/OrderItemService.cs
using ECommerce.Application.DTOs;
using ECommerce.Application.Services.Interfaces;
using ECommerce.Domain.Repositories;
namespace ECommerce.Application.Services.Implementations;
public class OrderItemService : IOrderItemService
{
private readonly IOrderItemRepository _repository;
public OrderItemService(IOrderItemRepository repository)
{
_repository = repository;
}
public async Task<IEnumerable<OrderItemDto>> GetByOrderIdAsync(Guid orderId)
{
var items = await _repository.GetByOrderIdAsync(orderId);
return items.Select(i => new OrderItemDto
{
Id = i.Id,
OrderId = i.OrderId,
ProductId = i.ProductId,
ProductName = i.ProductName,
Quantity = i.Quantity,
UnitPrice = i.UnitPrice
});
}
public async Task<IEnumerable<OrderItemDto>> GetAllAsync()
{
var items = await _repository.GetAllAsync();
return items.Select(i => new OrderItemDto
{
Id = i.Id,
OrderId = i.OrderId,
ProductId = i.ProductId,
ProductName = i.ProductName,
Quantity = i.Quantity,
UnitPrice = i.UnitPrice
});
}
}
Updated Program.cs
using ECommerce.Application.Services.Implementations;
using ECommerce.Application.Services.Interfaces;
using ECommerce.Domain.Repositories;
using ECommerce.Infrastructure.Repositories;
var builder = WebApplication.CreateBuilder(args);
// ------------------------------------------------------
// Add Controllers
// ------------------------------------------------------
builder.Services.AddControllers();
// ------------------------------------------------------
// Repository Registrations
// ------------------------------------------------------
builder.Services.AddScoped<IProductRepository, ProductRepository>();
builder.Services.AddScoped<ICustomerRepository, CustomerRepository>();
builder.Services.AddScoped<IOrderRepository, OrderRepository>();
builder.Services.AddScoped<IOrderItemRepository, OrderItemRepository>();
// ------------------------------------------------------
// Service Registrations (Application Layer)
// ------------------------------------------------------
builder.Services.AddScoped<IProductService, ProductService>();
builder.Services.AddScoped<ICustomerService, CustomerService>();
builder.Services.AddScoped<IOrderService, OrderService>();
builder.Services.AddScoped<IOrderItemService, OrderItemService>();
// ------------------------------------------------------
// Swagger
// ------------------------------------------------------
builder.Services.AddEndpointsApiExplorer();
builder.Services.AddSwaggerGen();
var app = builder.Build();
// ------------------------------------------------------
// Middleware Pipeline
// ------------------------------------------------------
if (app.Environment.IsDevelopment())
{
app.UseSwagger();
app.UseSwaggerUI();
}
app.UseHttpsRedirection();
app.UseAuthorization();
app.MapControllers();
app.Run();
app.MapControllers();
app.Run();
Next Lecture Preview
Lecture 2E : Implementing Controllers (API Layer)
Expose RESTful endpoints for Product, Customer, Order, and OrderItem using the services we built in the Application Layer.



Top comments (0)