From afc4598c611fbe242b25a00c098a4eca938477f8 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Wed, 17 Sep 2025 15:57:25 +0000 Subject: [PATCH 1/3] Initial plan From 84573038896a77ab37e053643f3078a89a71a440 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Wed, 17 Sep 2025 16:04:12 +0000 Subject: [PATCH 2/3] Implement complete C# POCO data model for eCommerce with Entity Framework Co-authored-by: CESARDELATORRE <1712635+CESARDELATORRE@users.noreply.github.com> --- ECommerceDataModel/Data/ECommerceDbContext.cs | 137 ++++++++++++++++++ ECommerceDataModel/ECommerceDataModel.csproj | 20 +++ ECommerceDataModel/Models/Category.cs | 18 +++ ECommerceDataModel/Models/Customer.cs | 41 ++++++ ECommerceDataModel/Models/Order.cs | 46 ++++++ ECommerceDataModel/Models/OrderItem.cs | 28 ++++ ECommerceDataModel/Models/Product.cs | 37 +++++ ECommerceDataModel/Program.cs | 103 +++++++++++++ ECommerceDataModel/README.md | 130 +++++++++++++++++ 9 files changed, 560 insertions(+) create mode 100644 ECommerceDataModel/Data/ECommerceDbContext.cs create mode 100644 ECommerceDataModel/ECommerceDataModel.csproj create mode 100644 ECommerceDataModel/Models/Category.cs create mode 100644 ECommerceDataModel/Models/Customer.cs create mode 100644 ECommerceDataModel/Models/Order.cs create mode 100644 ECommerceDataModel/Models/OrderItem.cs create mode 100644 ECommerceDataModel/Models/Product.cs create mode 100644 ECommerceDataModel/Program.cs create mode 100644 ECommerceDataModel/README.md diff --git a/ECommerceDataModel/Data/ECommerceDbContext.cs b/ECommerceDataModel/Data/ECommerceDbContext.cs new file mode 100644 index 0000000..9da13ed --- /dev/null +++ b/ECommerceDataModel/Data/ECommerceDbContext.cs @@ -0,0 +1,137 @@ +using Microsoft.EntityFrameworkCore; +using ECommerceDataModel.Models; + +namespace ECommerceDataModel.Data; + +public class ECommerceDbContext : DbContext +{ + public DbSet Categories { get; set; } + public DbSet Products { get; set; } + public DbSet Customers { get; set; } + public DbSet Orders { get; set; } + public DbSet OrderItems { get; set; } + + public ECommerceDbContext(DbContextOptions options) : base(options) + { + } + + protected override void OnModelCreating(ModelBuilder modelBuilder) + { + base.OnModelCreating(modelBuilder); + + // Configure Category entity + modelBuilder.Entity(entity => + { + entity.HasKey(e => e.Id); + entity.HasIndex(e => e.Name).IsUnique(); + entity.Property(e => e.Name).IsRequired().HasMaxLength(100); + entity.Property(e => e.Description).HasMaxLength(500); + }); + + // Configure Product entity + modelBuilder.Entity(entity => + { + entity.HasKey(e => e.Id); + entity.HasIndex(e => e.SKU).IsUnique(); + entity.Property(e => e.Name).IsRequired().HasMaxLength(200); + entity.Property(e => e.Description).HasMaxLength(1000); + entity.Property(e => e.Price).HasColumnType("decimal(18,2)").IsRequired(); + entity.Property(e => e.StockQuantity).IsRequired(); + entity.Property(e => e.SKU).HasMaxLength(50); + entity.Property(e => e.IsActive).HasDefaultValue(true); + entity.Property(e => e.CreatedDate).HasDefaultValueSql("GETUTCDATE()"); + + // Foreign key relationship + entity.HasOne(p => p.Category) + .WithMany(c => c.Products) + .HasForeignKey(p => p.CategoryId) + .OnDelete(DeleteBehavior.Restrict); + }); + + // Configure Customer entity + modelBuilder.Entity(entity => + { + entity.HasKey(e => e.Id); + entity.HasIndex(e => e.Email).IsUnique(); + entity.Property(e => e.FirstName).IsRequired().HasMaxLength(100); + entity.Property(e => e.LastName).IsRequired().HasMaxLength(100); + entity.Property(e => e.Email).IsRequired().HasMaxLength(255); + entity.Property(e => e.Phone).HasMaxLength(20); + entity.Property(e => e.Address).HasMaxLength(255); + entity.Property(e => e.City).HasMaxLength(100); + entity.Property(e => e.PostalCode).HasMaxLength(10); + entity.Property(e => e.Country).HasMaxLength(100); + entity.Property(e => e.CreatedDate).HasDefaultValueSql("GETUTCDATE()"); + }); + + // Configure Order entity + modelBuilder.Entity(entity => + { + entity.HasKey(e => e.Id); + entity.HasIndex(e => e.OrderNumber).IsUnique(); + entity.Property(e => e.OrderNumber).IsRequired().HasMaxLength(50); + entity.Property(e => e.OrderDate).HasDefaultValueSql("GETUTCDATE()"); + entity.Property(e => e.TotalAmount).HasColumnType("decimal(18,2)").IsRequired(); + entity.Property(e => e.Status).IsRequired().HasMaxLength(50).HasDefaultValue("Pending"); + entity.Property(e => e.ShippingAddress).HasMaxLength(255); + entity.Property(e => e.ShippingCity).HasMaxLength(100); + entity.Property(e => e.ShippingPostalCode).HasMaxLength(10); + entity.Property(e => e.ShippingCountry).HasMaxLength(100); + + // Foreign key relationship + entity.HasOne(o => o.Customer) + .WithMany(c => c.Orders) + .HasForeignKey(o => o.CustomerId) + .OnDelete(DeleteBehavior.Restrict); + }); + + // Configure OrderItem entity + modelBuilder.Entity(entity => + { + entity.HasKey(e => e.Id); + entity.Property(e => e.Quantity).IsRequired(); + entity.Property(e => e.UnitPrice).HasColumnType("decimal(18,2)").IsRequired(); + entity.Property(e => e.TotalPrice).HasColumnType("decimal(18,2)").IsRequired(); + + // Foreign key relationships + entity.HasOne(oi => oi.Order) + .WithMany(o => o.OrderItems) + .HasForeignKey(oi => oi.OrderId) + .OnDelete(DeleteBehavior.Cascade); + + entity.HasOne(oi => oi.Product) + .WithMany(p => p.OrderItems) + .HasForeignKey(oi => oi.ProductId) + .OnDelete(DeleteBehavior.Restrict); + + // Composite index for performance + entity.HasIndex(e => new { e.OrderId, e.ProductId }); + }); + + // Seed data + SeedData(modelBuilder); + } + + private static void SeedData(ModelBuilder modelBuilder) + { + // Seed Categories + modelBuilder.Entity().HasData( + new Category { Id = 1, Name = "Electronics", Description = "Electronic devices and accessories" }, + new Category { Id = 2, Name = "Clothing", Description = "Fashion and apparel" }, + new Category { Id = 3, Name = "Books", Description = "Books and educational materials" } + ); + + // Seed Products + modelBuilder.Entity().HasData( + new Product { Id = 1, Name = "Laptop", Description = "High-performance laptop", Price = 999.99m, StockQuantity = 50, SKU = "LAP001", CategoryId = 1 }, + new Product { Id = 2, Name = "Smartphone", Description = "Latest smartphone model", Price = 699.99m, StockQuantity = 100, SKU = "PHN001", CategoryId = 1 }, + new Product { Id = 3, Name = "T-Shirt", Description = "Cotton t-shirt", Price = 19.99m, StockQuantity = 200, SKU = "TSH001", CategoryId = 2 }, + new Product { Id = 4, Name = "Programming Book", Description = "Learn C# programming", Price = 49.99m, StockQuantity = 75, SKU = "BK001", CategoryId = 3 } + ); + + // Seed Customer + modelBuilder.Entity().HasData( + new Customer { Id = 1, FirstName = "John", LastName = "Doe", Email = "john.doe@example.com", Phone = "123-456-7890", Address = "123 Main St", City = "Anytown", PostalCode = "12345", Country = "USA" } + ); + } +} \ No newline at end of file diff --git a/ECommerceDataModel/ECommerceDataModel.csproj b/ECommerceDataModel/ECommerceDataModel.csproj new file mode 100644 index 0000000..19dd04d --- /dev/null +++ b/ECommerceDataModel/ECommerceDataModel.csproj @@ -0,0 +1,20 @@ + + + + Exe + net8.0 + enable + enable + + + + + + + + runtime; build; native; contentfiles; analyzers; buildtransitive + all + + + + diff --git a/ECommerceDataModel/Models/Category.cs b/ECommerceDataModel/Models/Category.cs new file mode 100644 index 0000000..eab6576 --- /dev/null +++ b/ECommerceDataModel/Models/Category.cs @@ -0,0 +1,18 @@ +using System.ComponentModel.DataAnnotations; + +namespace ECommerceDataModel.Models; + +public class Category +{ + public int Id { get; set; } + + [Required] + [MaxLength(100)] + public string Name { get; set; } = string.Empty; + + [MaxLength(500)] + public string? Description { get; set; } + + // Navigation property + public virtual ICollection Products { get; set; } = new List(); +} \ No newline at end of file diff --git a/ECommerceDataModel/Models/Customer.cs b/ECommerceDataModel/Models/Customer.cs new file mode 100644 index 0000000..d82ef3c --- /dev/null +++ b/ECommerceDataModel/Models/Customer.cs @@ -0,0 +1,41 @@ +using System.ComponentModel.DataAnnotations; + +namespace ECommerceDataModel.Models; + +public class Customer +{ + public int Id { get; set; } + + [Required] + [MaxLength(100)] + public string FirstName { get; set; } = string.Empty; + + [Required] + [MaxLength(100)] + public string LastName { get; set; } = string.Empty; + + [Required] + [MaxLength(255)] + [EmailAddress] + public string Email { get; set; } = string.Empty; + + [MaxLength(20)] + public string? Phone { get; set; } + + [MaxLength(255)] + public string? Address { get; set; } + + [MaxLength(100)] + public string? City { get; set; } + + [MaxLength(10)] + public string? PostalCode { get; set; } + + [MaxLength(100)] + public string? Country { get; set; } + + public DateTime CreatedDate { get; set; } = DateTime.UtcNow; + + // Navigation property + public virtual ICollection Orders { get; set; } = new List(); +} \ No newline at end of file diff --git a/ECommerceDataModel/Models/Order.cs b/ECommerceDataModel/Models/Order.cs new file mode 100644 index 0000000..e573078 --- /dev/null +++ b/ECommerceDataModel/Models/Order.cs @@ -0,0 +1,46 @@ +using System.ComponentModel.DataAnnotations; +using System.ComponentModel.DataAnnotations.Schema; + +namespace ECommerceDataModel.Models; + +public class Order +{ + public int Id { get; set; } + + [Required] + [MaxLength(50)] + public string OrderNumber { get; set; } = string.Empty; + + public DateTime OrderDate { get; set; } = DateTime.UtcNow; + + [Required] + [Column(TypeName = "decimal(18,2)")] + public decimal TotalAmount { get; set; } + + [Required] + [MaxLength(50)] + public string Status { get; set; } = "Pending"; // Pending, Processing, Shipped, Delivered, Cancelled + + [MaxLength(255)] + public string? ShippingAddress { get; set; } + + [MaxLength(100)] + public string? ShippingCity { get; set; } + + [MaxLength(10)] + public string? ShippingPostalCode { get; set; } + + [MaxLength(100)] + public string? ShippingCountry { get; set; } + + public DateTime? ShippedDate { get; set; } + + public DateTime? DeliveredDate { get; set; } + + // Foreign key + public int CustomerId { get; set; } + + // Navigation properties + public virtual Customer Customer { get; set; } = null!; + public virtual ICollection OrderItems { get; set; } = new List(); +} \ No newline at end of file diff --git a/ECommerceDataModel/Models/OrderItem.cs b/ECommerceDataModel/Models/OrderItem.cs new file mode 100644 index 0000000..9c9cf30 --- /dev/null +++ b/ECommerceDataModel/Models/OrderItem.cs @@ -0,0 +1,28 @@ +using System.ComponentModel.DataAnnotations; +using System.ComponentModel.DataAnnotations.Schema; + +namespace ECommerceDataModel.Models; + +public class OrderItem +{ + public int Id { get; set; } + + [Required] + public int Quantity { get; set; } + + [Required] + [Column(TypeName = "decimal(18,2)")] + public decimal UnitPrice { get; set; } + + [Required] + [Column(TypeName = "decimal(18,2)")] + public decimal TotalPrice { get; set; } + + // Foreign keys + public int OrderId { get; set; } + public int ProductId { get; set; } + + // Navigation properties + public virtual Order Order { get; set; } = null!; + public virtual Product Product { get; set; } = null!; +} \ No newline at end of file diff --git a/ECommerceDataModel/Models/Product.cs b/ECommerceDataModel/Models/Product.cs new file mode 100644 index 0000000..4fd41ad --- /dev/null +++ b/ECommerceDataModel/Models/Product.cs @@ -0,0 +1,37 @@ +using System.ComponentModel.DataAnnotations; +using System.ComponentModel.DataAnnotations.Schema; + +namespace ECommerceDataModel.Models; + +public class Product +{ + public int Id { get; set; } + + [Required] + [MaxLength(200)] + public string Name { get; set; } = string.Empty; + + [MaxLength(1000)] + public string? Description { get; set; } + + [Required] + [Column(TypeName = "decimal(18,2)")] + public decimal Price { get; set; } + + [Required] + public int StockQuantity { get; set; } + + [MaxLength(50)] + public string? SKU { get; set; } + + public bool IsActive { get; set; } = true; + + public DateTime CreatedDate { get; set; } = DateTime.UtcNow; + + // Foreign key + public int CategoryId { get; set; } + + // Navigation properties + public virtual Category Category { get; set; } = null!; + public virtual ICollection OrderItems { get; set; } = new List(); +} \ No newline at end of file diff --git a/ECommerceDataModel/Program.cs b/ECommerceDataModel/Program.cs new file mode 100644 index 0000000..989e60a --- /dev/null +++ b/ECommerceDataModel/Program.cs @@ -0,0 +1,103 @@ +using Microsoft.EntityFrameworkCore; +using ECommerceDataModel.Data; +using ECommerceDataModel.Models; + +Console.WriteLine("eCommerce Data Model Demo"); +Console.WriteLine("========================"); + +// Configure Entity Framework to use in-memory database for demonstration +var options = new DbContextOptionsBuilder() + .UseInMemoryDatabase(databaseName: "ECommerceDemo") + .Options; + +using var context = new ECommerceDbContext(options); + +// Ensure database is created and seeded +await context.Database.EnsureCreatedAsync(); + +Console.WriteLine("\n1. Categories:"); +var categories = await context.Categories.Include(c => c.Products).ToListAsync(); +foreach (var category in categories) +{ + Console.WriteLine($" - {category.Name}: {category.Products.Count} products"); +} + +Console.WriteLine("\n2. Products:"); +var products = await context.Products.Include(p => p.Category).ToListAsync(); +foreach (var product in products) +{ + Console.WriteLine($" - {product.Name} ({product.Category.Name}): ${product.Price:F2} - Stock: {product.StockQuantity}"); +} + +Console.WriteLine("\n3. Customers:"); +var customers = await context.Customers.ToListAsync(); +foreach (var customer in customers) +{ + Console.WriteLine($" - {customer.FirstName} {customer.LastName} ({customer.Email})"); +} + +Console.WriteLine("\n4. Creating a sample order..."); + +// Create a sample order +var orderCustomer = await context.Customers.FirstAsync(); +var laptop = await context.Products.FirstAsync(p => p.SKU == "LAP001"); +var tshirt = await context.Products.FirstAsync(p => p.SKU == "TSH001"); + +var order = new Order +{ + OrderNumber = $"ORD-{DateTime.Now.Ticks}", + CustomerId = orderCustomer.Id, + OrderDate = DateTime.UtcNow, + Status = "Pending", + ShippingAddress = orderCustomer.Address, + ShippingCity = orderCustomer.City, + ShippingPostalCode = orderCustomer.PostalCode, + ShippingCountry = orderCustomer.Country +}; + +var orderItems = new List +{ + new OrderItem + { + ProductId = laptop.Id, + Quantity = 1, + UnitPrice = laptop.Price, + TotalPrice = laptop.Price * 1 + }, + new OrderItem + { + ProductId = tshirt.Id, + Quantity = 2, + UnitPrice = tshirt.Price, + TotalPrice = tshirt.Price * 2 + } +}; + +order.TotalAmount = orderItems.Sum(oi => oi.TotalPrice); +order.OrderItems = orderItems; + +context.Orders.Add(order); +await context.SaveChangesAsync(); + +Console.WriteLine($" Order {order.OrderNumber} created successfully!"); + +Console.WriteLine("\n5. Orders with items:"); +var orders = await context.Orders + .Include(o => o.Customer) + .Include(o => o.OrderItems) + .ThenInclude(oi => oi.Product) + .ToListAsync(); + +foreach (var ord in orders) +{ + Console.WriteLine($" Order: {ord.OrderNumber} by {ord.Customer.FirstName} {ord.Customer.LastName}"); + Console.WriteLine($" Status: {ord.Status}, Total: ${ord.TotalAmount:F2}"); + Console.WriteLine(" Items:"); + foreach (var item in ord.OrderItems) + { + Console.WriteLine($" - {item.Product.Name} x{item.Quantity} @ ${item.UnitPrice:F2} = ${item.TotalPrice:F2}"); + } +} + +Console.WriteLine("\nDemo completed successfully! The POCO data model is working correctly with Entity Framework."); +Console.WriteLine("The model includes proper relationships, data annotations, and seed data."); diff --git a/ECommerceDataModel/README.md b/ECommerceDataModel/README.md new file mode 100644 index 0000000..a965206 --- /dev/null +++ b/ECommerceDataModel/README.md @@ -0,0 +1,130 @@ +# eCommerce Data Model - C# POCO with Entity Framework + +This project demonstrates a complete POCO (Plain Old CLR Object) data model for an eCommerce system using Entity Framework Core. + +## Data Model Structure + +The eCommerce system includes the following entities: + +### 1. Category +- **Purpose**: Organizes products into categories +- **Properties**: + - `Id` (Primary Key) + - `Name` (Required, Max 100 chars, Unique) + - `Description` (Optional, Max 500 chars) +- **Relationships**: One-to-Many with Products + +### 2. Product +- **Purpose**: Represents items available for sale +- **Properties**: + - `Id` (Primary Key) + - `Name` (Required, Max 200 chars) + - `Description` (Optional, Max 1000 chars) + - `Price` (Required, Decimal 18,2) + - `StockQuantity` (Required, Integer) + - `SKU` (Optional, Max 50 chars, Unique) + - `IsActive` (Boolean, Default: true) + - `CreatedDate` (DateTime, Default: UTC Now) + - `CategoryId` (Foreign Key) +- **Relationships**: + - Many-to-One with Category + - One-to-Many with OrderItems + +### 3. Customer +- **Purpose**: Represents customers who can place orders +- **Properties**: + - `Id` (Primary Key) + - `FirstName` (Required, Max 100 chars) + - `LastName` (Required, Max 100 chars) + - `Email` (Required, Max 255 chars, Unique, Email format) + - `Phone` (Optional, Max 20 chars) + - `Address` (Optional, Max 255 chars) + - `City` (Optional, Max 100 chars) + - `PostalCode` (Optional, Max 10 chars) + - `Country` (Optional, Max 100 chars) + - `CreatedDate` (DateTime, Default: UTC Now) +- **Relationships**: One-to-Many with Orders + +### 4. Order +- **Purpose**: Represents customer orders +- **Properties**: + - `Id` (Primary Key) + - `OrderNumber` (Required, Max 50 chars, Unique) + - `OrderDate` (DateTime, Default: UTC Now) + - `TotalAmount` (Required, Decimal 18,2) + - `Status` (Required, Max 50 chars, Default: "Pending") + - `ShippingAddress` (Optional, Max 255 chars) + - `ShippingCity` (Optional, Max 100 chars) + - `ShippingPostalCode` (Optional, Max 10 chars) + - `ShippingCountry` (Optional, Max 100 chars) + - `ShippedDate` (Optional, DateTime) + - `DeliveredDate` (Optional, DateTime) + - `CustomerId` (Foreign Key) +- **Relationships**: + - Many-to-One with Customer + - One-to-Many with OrderItems + +### 5. OrderItem +- **Purpose**: Represents individual items within an order (junction table) +- **Properties**: + - `Id` (Primary Key) + - `Quantity` (Required, Integer) + - `UnitPrice` (Required, Decimal 18,2) + - `TotalPrice` (Required, Decimal 18,2) + - `OrderId` (Foreign Key) + - `ProductId` (Foreign Key) +- **Relationships**: + - Many-to-One with Order + - Many-to-One with Product + +## Entity Framework Configuration + +The `ECommerceDbContext` class provides: + +- **Fluent API Configuration**: Detailed entity configuration including constraints, indexes, and relationships +- **Cascade Delete Rules**: Configured to maintain data integrity +- **Seed Data**: Sample data for testing and demonstration +- **Database Provider Support**: Configured for SQL Server with easy adaptation for other providers + +## Key Features + +1. **Data Annotations**: Used for validation and basic configuration +2. **Fluent API**: Advanced configuration for relationships and constraints +3. **Navigation Properties**: Virtual properties for Entity Framework lazy loading +4. **Unique Constraints**: Email, SKU, and OrderNumber uniqueness +5. **Default Values**: Automatic timestamps and status defaults +6. **Foreign Key Relationships**: Proper referential integrity +7. **Index Optimization**: Composite indexes for query performance + +## Running the Demo + +```bash +cd ECommerceDataModel +dotnet restore +dotnet build +dotnet run +``` + +The demo application will: +1. Create an in-memory database +2. Apply the data model and seed data +3. Display categories, products, and customers +4. Create a sample order with multiple items +5. Demonstrate querying with relationships + +## Dependencies + +- .NET 8.0 +- Microsoft.EntityFrameworkCore (9.0.9) +- Microsoft.EntityFrameworkCore.SqlServer (9.0.9) +- Microsoft.EntityFrameworkCore.Tools (9.0.9) +- Microsoft.EntityFrameworkCore.InMemory (9.0.9) - for demo + +## Database Migration + +To use with a real SQL Server database: + +1. Update connection string in DbContext +2. Remove InMemory provider configuration +3. Run: `dotnet ef migrations add InitialCreate` +4. Run: `dotnet ef database update` \ No newline at end of file From fe0e7f2fcff25cb52148a8a80da35b9757db0cb2 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Wed, 17 Sep 2025 16:06:51 +0000 Subject: [PATCH 3/3] Add Entity Relationship Diagram and finalize documentation Co-authored-by: CESARDELATORRE <1712635+CESARDELATORRE@users.noreply.github.com> --- ECommerceDataModel/ERD.md | 70 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 70 insertions(+) create mode 100644 ECommerceDataModel/ERD.md diff --git a/ECommerceDataModel/ERD.md b/ECommerceDataModel/ERD.md new file mode 100644 index 0000000..73cfb86 --- /dev/null +++ b/ECommerceDataModel/ERD.md @@ -0,0 +1,70 @@ +# eCommerce Data Model - Entity Relationship Diagram + +``` +┌─────────────────┐ ┌─────────────────┐ +│ Category │ │ Customer │ +├─────────────────┤ ├─────────────────┤ +│ Id (PK) │ │ Id (PK) │ +│ Name │ │ FirstName │ +│ Description │ │ LastName │ +└─────────────────┘ │ Email │ + │ │ Phone │ + │ 1:N │ Address │ + ▼ │ City │ +┌─────────────────┐ │ PostalCode │ +│ Product │ │ Country │ +├─────────────────┤ │ CreatedDate │ +│ Id (PK) │ └─────────────────┘ +│ Name │ │ +│ Description │ │ 1:N +│ Price │ ▼ +│ StockQuantity │ ┌─────────────────┐ +│ SKU │ │ Order │ +│ IsActive │ ├─────────────────┤ +│ CreatedDate │ │ Id (PK) │ +│ CategoryId (FK) │ │ OrderNumber │ +└─────────────────┘ │ OrderDate │ + │ │ TotalAmount │ + │ N:M │ Status │ + │ (via OrderItem) │ ShippingAddress │ + ▼ │ ShippingCity │ +┌─────────────────┐ │ ShippingPostalCode │ +│ OrderItem │ │ ShippingCountry │ +├─────────────────┤ │ ShippedDate │ +│ Id (PK) │ │ DeliveredDate │ +│ Quantity │ │ CustomerId (FK) │ +│ UnitPrice │ └─────────────────┘ +│ TotalPrice │ ▲ +│ OrderId (FK) │ │ 1:N +│ ProductId (FK) │ │ +└─────────────────┘──────────────────┘ +``` + +## Relationships: + +1. **Category → Product** (1:N) + - One category can have many products + - Each product belongs to one category + +2. **Customer → Order** (1:N) + - One customer can place many orders + - Each order belongs to one customer + +3. **Order → OrderItem** (1:N) + - One order can have many order items + - Each order item belongs to one order + +4. **Product → OrderItem** (1:N) + - One product can appear in many order items + - Each order item references one product + +5. **Product ↔ Order** (N:M via OrderItem) + - Many-to-many relationship through OrderItem junction table + - Allows tracking quantity and pricing per order + +## Key Constraints: + +- **Unique Indexes**: Category.Name, Product.SKU, Customer.Email, Order.OrderNumber +- **Foreign Key Constraints**: All relationships enforced with restrict/cascade rules +- **Data Validation**: Required fields, max lengths, email format validation +- **Default Values**: Timestamps, boolean flags, status fields \ No newline at end of file