Previous lesson: Mastering Prompt Engineering .NET 🧱 Lesson 2C - Implementing Repository Interfaces (Domain & Infrastructure Layers)

🚀 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

The goal is to keep

Folder Structure Overview

1

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

3

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.