Skip to content

feat(product): T2 — EF Core persistence + ORM config (verbatim from monolith)#60

Open
devin-ai-integration[bot] wants to merge 1 commit into
devin/product-t1-domainfrom
devin/product-t2-persistence
Open

feat(product): T2 — EF Core persistence + ORM config (verbatim from monolith)#60
devin-ai-integration[bot] wants to merge 1 commit into
devin/product-t1-domainfrom
devin/product-t2-persistence

Conversation

@devin-ai-integration

@devin-ai-integration devin-ai-integration Bot commented Jun 26, 2026

Copy link
Copy Markdown
Contributor

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:

  • ProductDbContext gains DbSet<Product> and DbSet<ProductCategory> with fully-qualified entity names to avoid namespace ambiguity (Product.Domain.Entities.Product vs project namespace)
  • Table mappings: AppProducts, AppProductCategories (monolith's "App" prefix convention)
  • Column constraints ported 1:1: Name required/indexed/100, Description/500, Icon unicode(false)/256, BuyingPrice/SellingPrice as decimal(18,2), self-referencing Parent→Children with DeleteBehavior.Restrict
  • Program.cs adds EnsureCreated() after builder.Build() so the schema is auto-created on startup (same pattern as the Notification service)

Validated: dotnet build succeeds, 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


Open in Devin Review

…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
@mbatchelor81 mbatchelor81 self-assigned this Jun 26, 2026
@devin-ai-integration

Copy link
Copy Markdown
Contributor Author

🤖 Devin AI Engineer

I'll be helping with this pull request! Here's what you should know:

✅ I will automatically:

  • Address comments on this PR. Add '(aside)' to your comment to have me ignore it.
  • Look at CI failures and help fix them

Note: I can only respond to comments from users who have write access to this repository.

⚙️ Control Options:

  • Disable automatic comment, CI, and merge conflict monitoring

@devin-ai-integration devin-ai-integration Bot left a comment

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Devin Review found 2 potential issues.

Open in Devin Review

Comment on lines +16 to +20
using (var scope = app.Services.CreateScope())
{
var db = scope.ServiceProvider.GetRequiredService<ProductDbContext>();
db.Database.EnsureCreated();
}

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🚩 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.

Open in Devin Review

Was this helpful? React with 👍 or 👎 to provide feedback.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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.

Comment on lines +24 to +32
// 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");

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🚩 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.

Open in Devin Review

Was this helpful? React with 👍 or 👎 to provide feedback.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant