feat(product): T2 — EF Core persistence + ORM config (verbatim from monolith)#60
Conversation
…onolith)
- Configure ProductDbContext with DbSets for Product and ProductCategory
- Port ORM configuration verbatim from monolith:
- AppProducts table: Name indexed/required/100, Description/500, Icon unicode(false)/256,
self-referencing Parent→Children with Restrict, BuyingPrice/SellingPrice decimal(18,2)
- AppProductCategories table: Name required/100, Description/500
- Add EnsureCreated() on startup for automatic schema creation
- Verified build succeeds and tables are created correctly in PostgreSQL
🤖 Devin AI EngineerI'll be helping with this pull request! Here's what you should know: ✅ I will automatically:
Note: I can only respond to comments from users who have write access to this repository. ⚙️ Control Options:
|
| using (var scope = app.Services.CreateScope()) | ||
| { | ||
| var db = scope.ServiceProvider.GetRequiredService<ProductDbContext>(); | ||
| db.Database.EnsureCreated(); | ||
| } |
There was a problem hiding this comment.
🚩 EnsureCreated() runs unconditionally in all environments and is incompatible with future migrations
The db.Database.EnsureCreated() call at src/Services/Product/Product.API/Program.cs:19 runs in every environment (including production), not just development. This is consistent with the Notification service (src/Services/Notification/Notification.API/Program.cs:26), so it follows the existing codebase pattern. However, EnsureCreated() does not create the __EFMigrationsHistory table and is fundamentally incompatible with EF Core migrations. If migrations are added later, Database.Migrate() will attempt to apply all migrations from scratch and fail because the tables already exist. This is fine as long as the team is aware they cannot mix the two approaches. If migrations are planned, this should be changed to Database.Migrate() before any migration is created.
Was this helpful? React with 👍 or 👎 to provide feedback.
There was a problem hiding this comment.
Acknowledged — this is intentionally consistent with the Notification service pattern (src/Services/Notification/Notification.API/Program.cs:26). The task spec explicitly calls for EnsureCreated() on startup. If the team later adopts EF migrations, this can be swapped to Database.Migrate() at that time.
| // Product — verbatim from monolith | ||
| builder.Entity<Product.Domain.Entities.Product>().Property(p => p.Name).IsRequired().HasMaxLength(100); | ||
| builder.Entity<Product.Domain.Entities.Product>().HasIndex(p => p.Name); | ||
| builder.Entity<Product.Domain.Entities.Product>().Property(p => p.Description).HasMaxLength(500); | ||
| builder.Entity<Product.Domain.Entities.Product>().Property(p => p.Icon).IsUnicode(false).HasMaxLength(256); | ||
| builder.Entity<Product.Domain.Entities.Product>().HasOne(p => p.Parent).WithMany(p => p.Children).OnDelete(DeleteBehavior.Restrict); | ||
| builder.Entity<Product.Domain.Entities.Product>().Property(p => p.BuyingPrice).HasColumnType("decimal(18,2)"); | ||
| builder.Entity<Product.Domain.Entities.Product>().Property(p => p.SellingPrice).HasColumnType("decimal(18,2)"); | ||
| builder.Entity<Product.Domain.Entities.Product>().ToTable("AppProducts"); |
There was a problem hiding this comment.
🚩 Product-to-ProductCategory relationship relies on EF Core convention rather than explicit configuration
The OnModelCreating method explicitly configures the Product self-referencing relationship (Parent/Children) but does not explicitly configure the Product-to-ProductCategory relationship. The Product entity has ProductCategoryId (int, non-nullable) and ProductCategory (required) at src/Services/Product/Product.Domain/Entities/Product.cs:17-18, while ProductCategory has Products collection at src/Services/Product/Product.Domain/Entities/ProductCategory.cs:9. EF Core will discover this by convention and configure it with cascade delete (since the FK is non-nullable). This works correctly, but the delete behavior differs from the self-referencing relationship which is explicitly set to Restrict. If the monolith used Restrict for this relationship too, that nuance would be lost. The comment says 'verbatim from monolith' so this may be intentional, but worth confirming.
Was this helpful? React with 👍 or 👎 to provide feedback.
There was a problem hiding this comment.
Correct — the Product→ProductCategory relationship uses EF Core's convention (cascade delete for non-nullable FK). This matches the monolith's configuration, which also did not explicitly configure this relationship's delete behavior — only the self-referencing Parent→Children relationship had an explicit Restrict. The convention-based cascade here is intentional and verbatim from the source.
Summary
Configures the EF Core persistence layer for the Product microservice by porting the ORM mapping from the monolith verbatim into
ProductDbContext.OnModelCreating.Key changes:
ProductDbContextgainsDbSet<Product>andDbSet<ProductCategory>with fully-qualified entity names to avoid namespace ambiguity (Product.Domain.Entities.Productvs project namespace)AppProducts,AppProductCategories(monolith's "App" prefix convention)Namerequired/indexed/100,Description/500,Iconunicode(false)/256,BuyingPrice/SellingPriceasdecimal(18,2), self-referencingParent→ChildrenwithDeleteBehavior.RestrictProgram.csaddsEnsureCreated()afterbuilder.Build()so the schema is auto-created on startup (same pattern as the Notification service)Validated:
dotnet buildsucceeds, PostgreSQL 16 creates both tables with correct indexes/FKs/constraints, no forbidden entity references (Order,Customer,ApplicationUser) present.Link to Devin session: https://partner-workshops.devinenterprise.com/sessions/bc5a9156cad244339c8ad8d1601e6dc2
Requested by: @mbatchelor81