Setup repo: merge all services, fix DB config, add solution file#210
Setup repo: merge all services, fix DB config, add solution file#210devin-ai-integration[bot] wants to merge 18 commits into
Conversation
- .NET 8 Web API with EF Core + PostgreSQL - InventoryItem model (standalone, no Product navigation) - InventoryDbContext with seed data for ProductIds 1-5 - InventorySvc with CRUD + ReserveStock method - InventoryController with all endpoints including /reserve - Program.cs with health, metrics, CORS, Swagger - Multi-stage Dockerfile
- .NET 8 Web API with PostgreSQL (EF Core) - Product CRUD endpoints (GET all, GET by id, GET by category, POST) - Health check, Prometheus metrics, Swagger, CORS - Seed data with 5 products - Multi-stage Dockerfile
- .NET 8 Web API extracted from OrderManager monolith
- Customer model, DbContext, seed data, service, and controller
- PostgreSQL via EF Core, health checks, Prometheus metrics, Swagger
- Multi-stage Dockerfile targeting port 8080
- CRUD endpoints: GET/POST /api/customers, GET /api/customers/{id}
- ApiGateway.csproj with Yarp.ReverseProxy, prometheus-net, Swashbuckle - Program.cs with YARP routing, env var overrides, health/metrics/CORS/Swagger - appsettings.json with route/cluster config for 4 microservices - Multi-stage Dockerfile targeting .NET 8 Alpine
- README.md: Document microservices architecture, 6 services, local dev with docker-compose, deployment info - .gitignore: Standard .NET + Node.js ignores - docker-compose.yml: All 6 services with correct ports, env vars, and dependencies
…in/1775601781-setup-repo
…n/1775601781-setup-repo
…vin/1775601781-setup-repo
…1775601781-setup-repo
…75601781-setup-repo
…775601781-setup-repo
…ix frontend port mapping - Replace Npgsql.EntityFrameworkCore.PostgreSQL with Microsoft.EntityFrameworkCore.Sqlite in all 4 services - Update Program.cs in CustomerService, ProductService, InventoryService, OrderService to use UseSqlite() - Add EnsureCreated() to OrderService for database initialization - Fix docker-compose.yml web-frontend port mapping from 80 to 8080 (matching nginx config) - Add EShop.sln solution file for unified build management
🤖 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:
|
| foreach (var (productId, quantity) in items) | ||
| { | ||
| var product = await _productClient.GetProductAsync(productId) | ||
| ?? throw new ArgumentException($"Product {productId} not found"); | ||
|
|
||
| var reserveResult = await _inventoryClient.ReserveStockAsync(productId, quantity) | ||
| ?? throw new InvalidOperationException($"Insufficient stock for {product.Name}"); | ||
|
|
||
| order.Items.Add(new OrderItem | ||
| { | ||
| ProductId = productId, | ||
| Quantity = quantity, | ||
| UnitPrice = product.Price | ||
| }); | ||
| } |
There was a problem hiding this comment.
🔴 No inventory compensation when multi-item order creation fails partway through
In CreateOrderAsync, the loop iterates over items and calls _inventoryClient.ReserveStockAsync for each one, which permanently deducts stock in the inventory service's database (src/InventoryService/Services/InventoryService.cs:56). If a later item fails (e.g., product not found at line 55-56 or insufficient stock at line 58-59), an exception is thrown but the inventory already reserved for earlier items is never rolled back. There is no compensation/restock logic and no try-catch in the loop. This causes permanent inventory data loss — stock is deducted with no corresponding order created. The README mentions a "saga pattern" but no compensation is implemented.
Prompt for agents
The CreateOrderAsync method in src/OrderService/Services/OrderService.cs reserves inventory for each item sequentially in a loop (lines 53-67). If any item fails after previous items have had their stock reserved, the already-reserved inventory is permanently lost because there is no compensation logic.
To fix this, you need to track which reservations have been made and add a try-catch around the loop that calls a restock/release endpoint on the inventory service for all previously reserved items when a failure occurs. The InventoryClient (src/OrderService/Clients/InventoryClient.cs) needs a new method like ReleaseStockAsync that calls the existing restock endpoint at POST /api/inventory/product/{productId}/restock. Then in CreateOrderAsync, maintain a list of (productId, quantity) for successful reservations, and in the catch block iterate through that list calling the release method for each.
Alternatively, consider implementing a two-phase approach: validate all products exist and check stock levels first, then reserve all in a second pass, with compensation on any failure.
Was this helpful? React with 👍 or 👎 to provide feedback.
There was a problem hiding this comment.
Valid observation about the missing saga compensation. This is pre-existing application logic from the feature branch — the lack of rollback in CreateOrderAsync is a design limitation, not a regression introduced by this setup PR. The README's mention of "saga pattern" appears aspirational. Leaving this as-is since the scope of this PR is infrastructure/setup fixes, not business logic changes.
- inventory-service: ProductService__BaseUrl -> PRODUCT_SERVICE_URL - order-service: CustomerService__BaseUrl -> CUSTOMER_SERVICE_URL - order-service: ProductService__BaseUrl -> PRODUCT_SERVICE_URL - order-service: InventoryService__BaseUrl -> INVENTORY_SERVICE_URL
- Enable Swagger unconditionally in InventoryService and ProductService (matching CustomerService/OrderService) - Add try-catch for ArgumentException in InventoryController.Restock (matching Reserve action) - Add try-catch for ArgumentException/InvalidOperationException in OrdersController.Create and UpdateStatus
Summary
Consolidates all microservice code (previously split across individual feature branches) into a single working branch and fixes issues preventing the application from running under Docker Compose.
Key changes on top of the merged feature branch code:
CustomerService,ProductService,InventoryService,OrderService) switched fromNpgsql.EntityFrameworkCore.PostgreSQLtoMicrosoft.EntityFrameworkCore.Sqlite. The original code referenced PostgreSQL viaUseNpgsql()but thedocker-compose.ymldefined no PostgreSQL container, causing 3 services to crash on startup (exit code 139)./data/*.dbinside each service'sProgram.cs, matching the Docker volume mounts.EnsureCreated()added toOrderService/Program.cs— the other 3 services already call this in theirSeedData.Initialize()methods, but OrderService lacked it.docker-compose.ymlport fix: Web-frontend mapping changed from4200:80to4200:8080to match the nginx config inside the container (which listens on 8080, not 80).docker-compose.ymlenv var fix: Environment variable names corrected to match the flat config keys the code reads (CUSTOMER_SERVICE_URL,PRODUCT_SERVICE_URL,INVENTORY_SERVICE_URL) instead of the ASP.NET hierarchical format (CustomerService__BaseUrl, etc.) which was silently ignored.EShop.slnadded for unifieddotnet build/dotnet testacross all projects.InventoryServiceandProductService— these two services gated Swagger behindIsDevelopment(), but Docker containers default to Production.CustomerServiceandOrderServicealready enabled Swagger unconditionally; now all 4 are consistent.InventoryController.RestockandOrdersController.Create/UpdateStatus— added try-catch blocks forArgumentExceptionandInvalidOperationException, returning proper 404/400 responses instead of 500s. This matches the existing pattern inInventoryController.Reserve.Verified locally:
dotnet build EShop.sln— 0 warnings, 0 errorsdotnet test EShop.sln— 3/3 tests passeddocker compose up --build— all 6 containers healthy/api/customers,/api/products,/api/inventory,/api/orders)localhost:4200with all 4 pages rendering data from backend servicesReview & Testing Checklist for Human
docker-compose.yml. This PR substitutes SQLite as a pragmatic fix. If PostgreSQL was the intended database, apostgresservice should be added todocker-compose.ymlinstead, and these SQLite changes should be reverted./datapaths: Each service usesPath.Combine("/data", "xxx.db")— this works inside Docker (volumes mount to/data) but will fail if services are run locally outside Docker without creating that directory. Consider whether anappsettings.jsonconnection string fallback is needed.HasColumnType("decimal(18,2)")inOrderDbContext: This PostgreSQL-specific column type annotation is still present. EF Core's SQLite provider silently ignores it, but it could cause confusion or issues if migrating back to PostgreSQL later.docker compose up --buildand verify all 6 services come up healthy, then navigatelocalhost:4200→ Products, Customers, Inventory, and Orders pages to confirm data loads. Also verify Swagger is accessible on all 4 service ports (/swagger).Notes
.csproj, 4×Program.cs, 2× controller files,docker-compose.yml, and the newEShop.sln.Microsoft.EntityFrameworkCore.InMemory(not SQLite), so the SQLite swap does not affect test correctness.OrderService.CreateOrderAsynclacks saga compensation for inventory reservations on partial failure — this is pre-existing application logic from the feature branch, not introduced by this PR.Link to Devin session: https://partner-workshops.devinenterprise.com/sessions/d268d727d3dc4e298b9a2bc8f25c7371