feat: Implement order service (cart + checkout) - #5
Merged
Conversation
- Domain: Cart, CartItem, Order, OrderItem entities with GORM tags and ToResponse helpers - Domain interfaces: CartRepository, OrderRepository, CartService, OrderService, CartCache, OrderCache, EventPublisher, ProductClient - DB migrations: 001_create_carts.up.sql, 002_create_orders.up.sql with indexes and CHECK constraints - Repository: CartRepository and OrderRepository backed by GORM; CreateOrder uses explicit transaction to insert order then items atomically Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…dler layers - Cache: CartCache (24h TTL) and OrderCache (1h detail / 5min list TTL) backed by Redis; InvalidateOrderList scans by user prefix - Events: Kafka publisher for order.created, order.updated, order.cancelled topics - Client: HTTP ProductClient fetches product snapshot from product-service; returns ErrProductInactive if product is disabled - Service: CartService auto-creates cart on first access, merges quantities on duplicate add, enforces item ownership on update/remove; OrderService builds order from cart snapshots in a single transaction, clears cart after checkout, publishes events asynchronously - Handler + Route: 8 endpoints (GET/POST/PUT/DELETE cart, GET/POST/GET/PUT orders); user identity read from X-User-ID header injected by gateway Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
… docker-compose wiring - cmd: config (PORT/DATABASE_URL/REDIS_URL/KAFKA_BROKERS/PRODUCT_SERVICE_URL), dotenv loader, GORM+Redis infrastructure setup, Kafka publisher bootstrap with ensureTopics, dependency wiring in Run(), graceful shutdown (DB/Redis/Kafka) - main.go: single-line entry point calling cmd.Run() - Dockerfile: multi-stage build (golang:1.25-alpine → alpine:3.18), port 8083 - .env / .env.example: local dev defaults - docker-compose: added REDIS_URL, KAFKA_BROKERS, PRODUCT_SERVICE_URL to order-service environment Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
… networking In Docker Compose, service-to-service communication uses the container's internal port (5432 for PostgreSQL), not the host-side mapped port. The ports directive (e.g. "5433:5432") only affects host access — all services in the same network must connect on 5432. Affected services: product-service, order-service, payment-service, inventory-service Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
PRODUCT_SERVICE_URLenv var to order-service; provision Kafka topics for order eventsWhat's included
Domain layer (
internal/domain/)Cart,CartItem,Order,OrderItementities with GORM tags andToResponse()helpersOrderStatustype withCancellable()helper (blocks cancel on shipped/delivered)CartRepository,OrderRepository,CartService,OrderService,CartCache,OrderCache,EventPublisher,ProductClientProductSnapshotstruct — price and name are snapshotted at cart-add time so historical orders are unaffected by product editsDB migrations (
db/)001_create_carts.up.sql—carts(UNIQUE on user_id) +cart_itemswith CHECK constraint on quantity002_create_orders.up.sql—orderswith CHECK constraint on status enum +order_itemsRepository layer (
internal/repository/)CartRepository— full CRUD for carts and items;ClearCartdeletes all items by cart_idOrderRepository—CreateOrderuses an explicit transaction (inserts order then sets FK on each item);UpdateOrderStatusdoes a targeted column update then re-fetchesCache layer (
internal/cache/)CartCache— keycart:<userID>, 24 h TTLOrderCache— order detailorder:<id>(1 h TTL), order listorders:user:<uid>:page:<n>:limit:<l>(5 min TTL);InvalidateOrderListscans by user prefixEvents (
internal/events/)order.created,order.updated,order.cancelled; all publishes are fire-and-forget goroutines so Kafka unavailability never blocks HTTP responsesProduct HTTP client (
internal/client/)GET {PRODUCT_SERVICE_URL}/products/{id}with 5 s timeoutErrProductInactiveimmediately ifis_active == false— prevents snapshotting a dead product into the cartService layer (
internal/service/)CartService— auto-creates cart on first access;AddItemmerges quantity if product already in cart and refreshes price snapshot;UpdateItem/RemoveItemverify item ownership (item.CartID == cart.ID) before touching anythingOrderService—CreateOrderbuilds order items from cart snapshots in one transaction, clears cart after checkout;CancelOrderchecksStatus.Cancellable()before proceedingHandler + Route layers (
internal/handler/,internal/route/)X-User-IDheader injected by the gateway proxyBootstrap (
cmd/,main.go,Dockerfile)ensureTopics, full dependency wiring, graceful shutdowndocker-compose fix
products-db,orders-db,payments-db,inventory-servicewere all using the host-mapped port in theirDATABASE_URL— inside Docker containers communicate on the container port (5432), not the host port. Fixed across all four affected services.Test plan
go build ./...passes inservices/order-service/go vet ./...passes with zero warningsdocker compose up orders-db redis kafka order-servicestarts and/healthreturns 200POST /api/cart/itemswith a valid product UUID adds item and returns cart with correct price snapshotPOST /api/orderswith shipping details creates order, clears cart, returnspendingstatusPUT /api/orders/:id/cancelon a pending order returnscancelled; on a shipped order returns 409order.createdreceives a message after checkout🤖 Generated with Claude Code