Webshop: Serverless E-Commerce on Localstack
Overview
We built a B2C e-commerce platform with two user roles: buyers who browse, search, cart and order, and sellers who register a shop, list products, manage orders and generate invoices. The constraint we set ourselves was to build it the way it would run on AWS, but to make it runnable on any laptop. That meant a microservice-oriented backend on AWS Lambda + API Gateway + DynamoDB, all developed locally against Localstack and brought up with a single docker compose up. Before writing any code we went through a formal envisioning exercise: five stakeholder personas, MoSCoW-prioritised requirements, and a risk assessment that flagged "trouble setting up AWS" as our top risk; that risk ended up shaping every technical decision we made.
System Architecture
Each microservice followed a shared template: config files (db_schema.json, request_models.json, resources_to_create.json) plus a shared deploy.py that provisioned everything, so adding a new service was mostly a matter of writing the config and the Lambda handlers. We solved two infrastructure pain points once: apig-id-share publishes the dynamic API Gateway URL to the frontend (Localstack can't pin a static URL without paid features), and options_lambda_function provides a single shared CORS-preflight handler reused by every route.
Final Result

The deployed stack runs end-to-end from a single docker compose -f docker-compose_final_submission.yml up. The frontend at localhost:3000 lets a buyer browse a product catalogue, add items to a cart, check out, and review orders; a seller can register a shop, list products, manage orders and pull statistics. Quality gates ran on every push to main: Qodana for static analysis, pytest and Jest in GitHub Actions for tests, and Docker images pushed automatically to a public Docker Hub registry tagged final-submission for the grader.
Problem We Struggled With
We hit moto3's lack of support for AWS_PROXY integration on API Gateway, which meant we could not write proper end-to-end integration tests against a mocked gateway and had to test one layer below it, a limitation we documented in the README rather than papered over.