Skip to content

borispanasenko/ecommerce-distributed

Repository files navigation

Ecommerce Distributed

Small distributed commerce system.

Services

catalog     - products, brands, categories, variants/SKUs and current prices
inventory   - warehouses, locations, stock, reservations and stock allocation
cart        - shopping carts and cart items
ordering    - orders, product snapshots and inventory reservation references
payment     - payment records and payment simulation
fulfillment - shipments and shipment lifecycle
frontend    - Angular UI

Current status

Catalog     - working backend flow, API, tests, documentation, Docker Compose integration
Inventory   - working backend flow, API, tests, documentation, Docker Compose integration
Cart        - working backend flow, API, tests, documentation, Docker Compose integration, frontend integration
Ordering    - working backend flow, Catalog and Inventory integration, API, tests, documentation, Docker Compose integration
Payment     - working backend flow, Ordering integration, API, tests, documentation, Docker Compose integration
Fulfillment - working backend flow, Ordering integration, API, tests, documentation, Docker Compose integration
Frontend    - working catalog browsing, backend cart integration, checkout and payment flow

Catalog flow

Create brand
Create category
Create product as Draft
Add product variant/SKU
Publish product
List active products
Get product details
Get active product variant snapshot
Archive product

Catalog API

GET  /health

GET  /api/brands
POST /api/brands

GET  /api/categories
POST /api/categories

GET  /api/products
GET  /api/products/{id}
GET  /api/products/variants/{variantId}/snapshot
POST /api/products
POST /api/products/{id}/variants
POST /api/products/{id}/publish
POST /api/products/{id}/archive

Inventory flow

Create warehouse
Create storage location
Receive stock for SKU
Get stock by SKU
Reserve stock explicitly
Allocate stock reservation by SKU
Release reservation
Commit reservation
Get stock movements

Inventory API

GET  /health

GET  /api/warehouses
POST /api/warehouses

GET  /api/locations
POST /api/locations

POST /api/stock/receipts
GET  /api/stock/{sku}
GET  /api/stock/movements

POST /api/stock/reservations
POST /api/stock/reservations/allocate
POST /api/stock/reservations/{id}/release
POST /api/stock/reservations/{id}/commit

Cart flow

Create cart
Get cart details
Add product variant to cart
Increase quantity when adding the same product variant again
Update cart item quantity
Remove cart item
Clear cart

Cart API

GET    /health

POST   /api/carts
GET    /api/carts/{id}

POST   /api/carts/{id}/items
PUT    /api/carts/{id}/items/{productVariantId}
DELETE /api/carts/{id}/items/{productVariantId}

POST   /api/carts/{id}/clear

Ordering flow

Create order from product variant IDs and quantities
Load product snapshots from Catalog
Allocate Inventory stock reservations by SKU
Store inventoryReservationId on order item
Calculate line totals
Calculate order total
List orders
Get order details
Cancel order
Release Inventory reservation
Mark order as Paid
Keep Inventory reservation allocated while order is Paid
Mark paid order as Shipped
Commit Inventory reservation during mark-shipped

Ordering API

GET  /health

GET  /api/orders
GET  /api/orders/{id}
POST /api/orders
POST /api/orders/{id}/cancel
POST /api/orders/{id}/mark-paid
POST /api/orders/{id}/mark-shipped

Payment flow

Create payment for order
List payments
Get payment details
Mark payment as succeeded
Mark linked order as Paid through Ordering
Keep Inventory reservation allocated until the paid order is shipped
Mark payment as failed
Reject status changes after payment completion

Payment API

GET  /health

GET  /api/payments
GET  /api/payments/{id}
POST /api/payments
POST /api/payments/{id}/succeed
POST /api/payments/{id}/fail

Fulfillment flow

Create shipment for paid order
Validate linked order through Ordering
List shipments
Get shipment details
Ship shipment
Mark linked order as Shipped through Ordering
Commit Inventory reservation through Ordering mark-shipped
Cancel pending shipment
Reject shipment changes after terminal status

Fulfillment API

GET  /health

GET  /api/shipments
GET  /api/shipments/{id}
POST /api/shipments
POST /api/shipments/{id}/ship
POST /api/shipments/{id}/cancel

End-to-end flows

Catalog defines product variants, SKUs and current prices.
Inventory stores stock by SKU.
Cart stores product variant IDs and quantities before checkout.
Ordering creates orders from product variant IDs and quantities.
Ordering loads trusted product snapshots from Catalog.
Ordering asks Inventory to allocate stock reservations by SKU.
Ordering releases Inventory reservation when an order is cancelled.
Payment stores simulated payment records for orders.
Payment calls Ordering when a pending payment succeeds.
Ordering marks the order as Paid.
Ordering keeps Inventory reservation allocated while the order is Paid.
Fulfillment validates paid orders through Ordering.
Fulfillment stores shipment records for orders.
Fulfillment calls Ordering when a shipment is shipped.
Ordering commits Inventory reservation during mark-shipped.
Ordering marks the order as Shipped.

Current Inventory commit boundary:

Ordering allocates Inventory reservations during order creation.
Ordering releases Inventory reservations when PendingPayment orders are cancelled.
Ordering keeps Inventory reservations allocated when orders are marked as Paid.
Fulfillment triggers mark-shipped through Ordering when shipments are shipped.
Ordering commits Inventory reservations when Paid orders are marked as Shipped.
Fulfillment does not call Inventory directly.
Ordering owns inventory_reservation_id references stored on order items.

Completed scenarios:

Cart flow:
Cart is created
Product variant is added to Cart with quantity
Adding the same product variant again increases quantity
Cart item quantity can be updated
Cart item can be removed
Cart can be cleared
Cart does not reserve Inventory stock
Cart does not store trusted product names, prices, SKUs or currencies
Cancel flow:
Product exists in Catalog
Stock exists in Inventory
Order is created in Ordering from product variant ID and quantity
Ordering loads product snapshot from Catalog
Inventory stock reservation is allocated
Order stores inventoryReservationId
Order is cancelled
Inventory reservation is released
Payment success flow:
Product exists in Catalog
Stock exists in Inventory
Order is created in Ordering from product variant ID and quantity
Ordering loads product snapshot from Catalog
Inventory stock reservation is allocated
Order stores inventoryReservationId
Payment is created for the order
Payment is marked as Succeeded
Payment calls Ordering to mark the order as Paid
Order is marked as Paid
Inventory reservation remains allocated
Inventory reservation is not committed yet
Inventory on-hand stock is not decreased yet
Payment failure flow:
Payment is created
Payment is marked as Failed
Payment remains terminal Failed
Payment failure does not call Ordering
No Inventory reservation is committed by Payment failure
Shipment flow:
Product exists in Catalog
Stock exists in Inventory
Order is created in Ordering from product variant ID and quantity
Ordering loads product snapshot from Catalog
Inventory stock reservation is allocated
Payment calls Ordering to mark the order as Paid
Inventory reservation remains allocated
Shipment is created in Fulfillment for the Paid order
Fulfillment validates linked order through Ordering
Shipment is shipped
Fulfillment calls Ordering to mark the order as Shipped
Ordering commits Inventory reservation during mark-shipped
Order is marked as Shipped
Inventory on-hand stock is decreased

Local infrastructure

Start databases and services:

docker compose up -d

Docker Compose local service ports:

Catalog API     - http://localhost:5001
Ordering API    - http://localhost:5002
Inventory API   - http://localhost:5003
Payment API     - http://localhost:5004
Cart API        - http://localhost:5005
Fulfillment API - http://localhost:5006

Inside Docker Compose, services use internal service names:

Ordering    -> Catalog:  http://catalog-api:8080
Ordering    -> Inventory: http://inventory-api:8080
Payment     -> Ordering: http://ordering-api:8080
Fulfillment -> Ordering: http://ordering-api:8080

Cart Service is currently independent from other backend services.

Catalog database from host:

Host=localhost;Port=5433;Database=catalog_db;Username=postgres;Password=postgres

Ordering database from host:

Host=localhost;Port=5434;Database=ordering_db;Username=postgres;Password=postgres

Inventory database from host:

Host=localhost;Port=5435;Database=inventory_db;Username=postgres;Password=postgres

Payment database from host:

Host=localhost;Port=5436;Database=payment_db;Username=postgres;Password=postgres

Cart database from host:

Host=localhost;Port=5437;Database=cart_db;Username=postgres;Password=postgres

Fulfillment database from host:

Host=localhost;Port=5438;Database=fulfillment_db;Username=postgres;Password=postgres

Run Catalog API locally

ASPNETCORE_ENVIRONMENT=Development \
ConnectionStrings__DefaultConnection="Host=localhost;Port=5433;Database=catalog_db;Username=postgres;Password=postgres" \
dotnet run --project services/catalog/Catalog.Api/Catalog.Api.csproj

Run Inventory API locally

ASPNETCORE_ENVIRONMENT=Development \
ConnectionStrings__DefaultConnection="Host=localhost;Port=5435;Database=inventory_db;Username=postgres;Password=postgres" \
dotnet run --project services/inventory/Inventory.Api/Inventory.Api.csproj

Run Cart API locally

Cart API can be started independently.

ASPNETCORE_ENVIRONMENT=Development \
ConnectionStrings__DefaultConnection="Host=localhost;Port=5437;Database=cart_db;Username=postgres;Password=postgres" \
dotnet run --project services/cart/Cart.Api/Cart.Api.csproj

Run Ordering API locally

Ordering API requires Catalog API for product snapshots and Inventory API for stock reservations.

Run Catalog API and Inventory API first.

Then run Ordering API:

ASPNETCORE_ENVIRONMENT=Development \
ConnectionStrings__DefaultConnection="Host=localhost;Port=5434;Database=ordering_db;Username=postgres;Password=postgres" \
CatalogApi__BaseUrl="http://localhost:5001" \
InventoryApi__BaseUrl="http://localhost:5245" \
dotnet run --project services/ordering/Ordering.Api/Ordering.Api.csproj

Run Payment API locally

Payment API requires Ordering API for successful payment flow.

Run Catalog API, Inventory API and Ordering API first.

Then run Payment API:

ASPNETCORE_ENVIRONMENT=Development \
ConnectionStrings__DefaultConnection="Host=localhost;Port=5436;Database=payment_db;Username=postgres;Password=postgres" \
OrderingApi__BaseUrl="http://localhost:5172" \
dotnet run --project services/payment/Payment.Api/Payment.Api.csproj

Run Fulfillment API locally

Fulfillment API requires Ordering API for shipment creation validation and shipment shipping flow.

Run Ordering API first.

Then run Fulfillment API:

ASPNETCORE_ENVIRONMENT=Development \
ConnectionStrings__DefaultConnection="Host=localhost;Port=5438;Database=fulfillment_db;Username=postgres;Password=postgres" \
OrderingApi__BaseUrl="http://localhost:5172" \
dotnet run --project services/fulfillment/Fulfillment.Api/Fulfillment.Api.csproj

Tests

Catalog:

dotnet test services/catalog/Catalog.sln

Inventory:

dotnet test services/inventory/Inventory.sln

Cart:

dotnet test services/cart/Cart.sln

Ordering:

dotnet test services/ordering/Ordering.sln

Payment:

dotnet test services/payment/Payment.sln

Fulfillment:

dotnet test services/fulfillment/Fulfillment.sln

Manual API checks

Catalog:

services/catalog/Catalog.Api/Catalog.Api.http
services/catalog/Catalog.Api/Catalog.Api.readonly.http

Inventory:

services/inventory/Inventory.Api/Inventory.Api.http
services/inventory/Inventory.Api/Inventory.Api.readonly.http

Cart:

services/cart/Cart.Api/Cart.Api.http
services/cart/Cart.Api/Cart.Api.readonly.http

Ordering:

services/ordering/Ordering.Api/Ordering.Api.http
services/ordering/Ordering.Api/Ordering.Api.readonly.http

Payment:

services/payment/Payment.Api/Payment.Api.http
services/payment/Payment.Api/Payment.Api.readonly.http

Fulfillment:

services/fulfillment/Fulfillment.Api/Fulfillment.Api.http
services/fulfillment/Fulfillment.Api/Fulfillment.Api.readonly.http

Files ending with .readonly.http contain only safe read-only requests.

Write .http files may change local development data, but flows are designed to finish in terminal states and avoid active dangling reservations.

Documentation

Service boundaries

Catalog owns:

products
brands
categories
variants/SKUs
product images
current product prices

Inventory owns:

warehouses
storage locations
stock balances
stock movements
stock reservations
stock allocation

Cart owns:

carts
cart items
product variant references
quantities

Ordering owns:

orders
order items
order statuses
order totals
product snapshots inside orders
Inventory reservation references inside order items

Payment owns:

payments
payment statuses
payment provider references
payment failure reasons

Fulfillment owns:

shipments
shipment statuses
carrier information
tracking numbers
shipment timestamps

Boundary rules

Catalog does not store stock.

Inventory does not store product descriptions or prices.

Cart stores productVariantId and quantity only.

Cart does not store trusted product prices, product names, SKUs, currencies or stock data.

Cart does not reserve stock.

Cart does not create orders.

Cart does not process payments.

Ordering does not store live product data.

Ordering stores product snapshots so old orders do not change when Catalog data changes.

Frontend does not send trusted product prices, product names, SKUs or currencies to Ordering.

Frontend does not choose warehouse or storage location.

Ordering gets product snapshots from Catalog through Catalog API.

Inventory stores stock by SKU.

Inventory chooses warehouse and storage location during stock allocation.

Ordering may reference Catalog data by product_id, product_variant_id and sku.

Ordering may reference Inventory reservations by inventory_reservation_id.

Ordering creates Inventory reservations when orders are created.

Ordering releases Inventory reservations when PendingPayment orders are cancelled.

Ordering keeps Inventory reservations allocated when orders are marked as Paid.

Ordering commits Inventory reservations when Paid orders are marked as Shipped.

Payment does not store order details.

Payment references orders by order_id.

Payment calls Ordering when a pending payment succeeds.

Payment does not write OrderingDb directly.

Payment does not write InventoryDb directly.

Fulfillment does not store order details.

Fulfillment references orders by order_id.

Fulfillment calls Ordering when a shipment is shipped.

Fulfillment validates linked orders through Ordering before shipment creation.

Fulfillment does not write OrderingDb directly.

Fulfillment does not write InventoryDb directly.

Fulfillment does not call Inventory directly.

Fulfillment does not commit Inventory reservations directly.

Inventory still allocates stock reservations during order creation, not during cart changes.

Ordering still loads trusted product snapshots from Catalog when creating an order.

About

Distributed commerce backend with Catalog, Inventory, Ordering, Payment, PostgreSQL, Docker Compose, and Angular UI.

Topics

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

 
 
 

Contributors