Last active
December 18, 2024 00:15
-
-
Save davepcallan/6ec66f9a7ad77d9ac10a68daac6b57a8 to your computer and use it in GitHub Desktop.
Vertical Slice Architecture example - UseCases -> CreateOrder.cs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// 1. Order Entity | |
namespace VerticalSliceExample.Domain; | |
public class Order | |
{ | |
public int Id { get; set; } | |
public int CustomerId { get; set; } | |
public Dictionary<int, int> Products { get; set; } = new(); // ProductId -> Quantity | |
public decimal TotalAmount { get; private set; } | |
public DateTime CreatedAt { get; set; } = DateTime.UtcNow; | |
public void CalculateTotal(Dictionary<int, decimal> productPrices) | |
{ | |
TotalAmount = 0; | |
foreach (var product in Products) | |
{ | |
if (productPrices.TryGetValue(product.Key, out var price)) | |
{ | |
TotalAmount += price * product.Value; | |
} | |
else | |
{ | |
throw new InvalidOperationException($"Product with ID {product.Key} not found in pricing data."); | |
} | |
} | |
} | |
} | |
// 2. DbContext | |
namespace VerticalSliceExample.Persistence; | |
using Microsoft.EntityFrameworkCore; | |
using VerticalSliceExample.Domain; | |
public class AppDbContext(DbContextOptions<AppDbContext> options) : DbContext(options) | |
{ | |
public DbSet<Order> Orders { get; set; } | |
} | |
// 3. Use Case: CreateOrder | |
namespace VerticalSliceExample.UseCases.CreateOrder; | |
using Microsoft.AspNetCore.Mvc; | |
using System.ComponentModel.DataAnnotations; | |
using VerticalSliceExample.Domain; | |
using VerticalSliceExample.Persistence; | |
public record Request(int CustomerId, Dictionary<int, int> Products) : IValidatableObject | |
{ | |
public IEnumerable<ValidationResult> Validate(ValidationContext validationContext) | |
{ | |
if (CustomerId <= 0) | |
yield return new ValidationResult("CustomerId must be greater than zero.", new[] { nameof(CustomerId) }); | |
if (Products == null || Products.Count == 0) | |
yield return new ValidationResult("At least one product must be included in the order.", new[] { nameof(Products) }); | |
foreach (var (productId, quantity) in Products) | |
{ | |
if (productId <= 0) | |
yield return new ValidationResult("Product ID must be greater than zero.", new[] { nameof(Products) }); | |
if (quantity <= 0) | |
yield return new ValidationResult($"Quantity for product {productId} must be greater than zero.", new[] { nameof(Products) }); | |
} | |
} | |
} | |
public record Response(int OrderId, decimal TotalAmount, string Message); | |
[ApiController] | |
[Route("api/orders")] | |
public class CreateOrderController(AppDbContext dbContext, ILogger<CreateOrderController> logger) : ControllerBase | |
{ | |
[HttpPost] | |
public async Task<IActionResult> CreateOrder([FromBody] Request request) | |
{ | |
// Validate the incoming request | |
if (!ModelState.IsValid) | |
return BadRequest(ModelState); | |
// Mocked product prices (in real-world, fetch from Product table or service) | |
var productPrices = new Dictionary<int, decimal> | |
{ | |
{ 1, 100.00m }, // Product ID -> Price | |
{ 2, 50.00m }, | |
{ 3, 20.00m } | |
}; | |
try | |
{ | |
// Create and validate the Order | |
var order = new Order | |
{ | |
CustomerId = request.CustomerId, | |
Products = request.Products | |
}; | |
order.CalculateTotal(productPrices); // Calculate total price | |
// Persist the order | |
dbContext.Orders.Add(order); | |
await dbContext.SaveChangesAsync(); | |
// Return response | |
var response = new Response(order.Id, order.TotalAmount, "Order created successfully."); | |
return CreatedAtAction(nameof(CreateOrder), new { id = order.Id }, response); | |
} | |
catch (Exception ex) // Catching a more generic exception | |
{ | |
// Log the error | |
logger.LogError(ex, "An error occurred while creating the order."); | |
// Return a generic error message (don't expose internal details) | |
return StatusCode(500, new { Message = "An unexpected error occurred. Please try again later." }); | |
} | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment