Skip to content

Extract Product bounded context into the Product service - #89

Open
devin-ai-integration[bot] wants to merge 1 commit into
mainfrom
devin/1789050390-product-service
Open

devin-ai-integration[bot] wants to merge 1 commit into
mainfrom
devin/1789050390-product-service

Conversation

@devin-ai-integration

Copy link
Copy Markdown
Contributor

Summary

Extracts the Product bounded context out of quickapp-monolith into a self-contained service under src/Services/Product, per docs/extraction-boundaries.md (PR #87).

  • Domain: Product / ProductCategory ported from QuickApp.Core/Models/Shop/. Intra-context navigations kept (Product ↔ ProductCategory, Product.Parent/Children); the cross-context Product.OrderDetails navigation is dropped — order lines reference products by ProductId only.
  • Infrastructure: ProductDbContext now maps AppProducts / AppProductCategories with the monolith's column constraints (Name(100), Description(500), Icon(256), prices decimal(18,2), parent Restrict, category Cascade) and stamps audit fields in SaveChanges(Async). EF Core migration InitialProductSchema + EF-backed repositories + an idempotent ProductDbSeeder (categories None/Cars, products BMW M6 / Nissan Patrol / one uncategorised sample) so the gateway returns rows on a fresh volume.
  • API: full CRUD the monolith never had — GET|POST /api/products, GET|PUT|DELETE /api/products/{id} and the same set under /api/products/categories. Wire shape stays ProductVM-compatible (incl. flattened productCategoryName), plus parentId/productCategoryId which CRUD needs for assignment. Creating/updating a product with an unknown productCategoryId returns 400.
  • Startup retries migrate+seed (10 × 3s) because compose depends_on does not wait for Postgres readiness:
for (var attempt = 1; ; attempt++)
    try { await db.Database.MigrateAsync(); await ProductDbSeeder.SeedAsync(db); break; }
    catch (Exception ex) when (attempt < maxAttempts) { /* log + delay 3s */ }
  • Tests: Product.Tests (16 tests) over WebApplicationFactory, covering the read path and every CRUD verb for products and categories plus seeder idempotency. ProductDatabaseFixture starts a Testcontainers Postgres and falls back to the EF in-memory provider when Docker is unavailable.

No changes to src/ApiGateway/** — the existing YARP route /api/products/{**catch-all}http://product-service:5004/ already works.

Verification

dotnet build Services/Product/Product.API/Product.API.csproj -c Release → 0 warnings, 0 errors.
dotnet test Services/Product/Product.Tests/Product.Tests.csproj -c Release → Passed! Failed: 0, Passed: 16, Skipped: 0.

Through the gateway on port 5000 (compose stack):

$ curl -s -i http://localhost:5000/api/products
HTTP/1.1 200 OK
Content-Type: application/json; charset=utf-8
Server: Kestrel

[{"id":1,"name":"BMW M6","description":"Yet another masterpiece from the world's best car manufacturer","icon":null,"buyingPrice":109775.00,"sellingPrice":114234.00,"unitsInStock":12,"isActive":true,"isDiscontinued":false,"parentId":null,"productCategoryId":2,"productCategoryName":"Cars"},
 {"id":2,"name":"Nissan Patrol",...,"productCategoryName":"Cars"},
 {"id":3,"name":"Uncategorised sample",...,"productCategoryName":"None"}]

$ curl -s -o /dev/null -w "%{http_code}\n" http://localhost:5000/api/products/1
200
$ curl -s -X POST http://localhost:5000/api/products -H 'Content-Type: application/json' \
    -d '{"name":"Audi RS7","buyingPrice":95000,"sellingPrice":105000,"unitsInStock":3,"isActive":true,"isDiscontinued":false,"productCategoryId":2}'
201 {"id":4,"name":"Audi RS7",...,"productCategoryName":"Cars"}
$ curl -s -X PUT http://localhost:5000/api/products/4 -H 'Content-Type: application/json' -d '{...,"name":"Audi RS7 Sportback",...}'
200 {"id":4,"name":"Audi RS7 Sportback",...}
$ curl -s -o /dev/null -w "%{http_code}\n" -X DELETE http://localhost:5000/api/products/4
204
$ curl -s http://localhost:5000/api/products/categories
200 [{"id":2,"name":"Cars",...},{"id":1,"name":"None",...}]

Notes / known issues

  • docker compose -f src/docker-compose.yml up --build cannot boot the whole stack on main: notification-service fails to build because Notification.API.csproj references ..\..\Shared\... instead of ..\..\..\Shared\... (pre-existing, out of this PR's allowed scope — flagged as the boundaries doc requires). Everything else (postgres, rabbitmq, identity, customer, order, product, api-gateway) builds and boots, and the curl evidence above was captured against that stack.
  • Expected trivial conflicts with the Customer extraction (Extract Customer bounded context into the Customer service #88), to be resolved by rebasing onto main after it merges: src/Microservices.sln (this PR only adds Product.Tests) and the one-line <ProjectReference> path fix in src/Services/Product/Product.API/Product.API.csproj (..\..\Shared..\..\..\Shared), which this build needs.

Link to Devin session: https://partner-workshops.devinenterprise.com/sessions/ac94963abb93407fb387a85677193555
Open in Devin Desktop: https://partner-workshops.devinenterprise.com/desktop/session/ac94963abb93407fb387a85677193555?variant=devin
Requested by: @DhrovS

@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

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