AlgoDaily

Project: E-commerce Platform

Read at your own pace. Code execution, quizzes, progress tracking, and the AI tutor are available in the interactive view with JavaScript enabled.

Capstone Orientation

Your final project

Now that we've covered Project Setup and Version Control, Design Patterns in Java, REST APIs, Hibernate persistence, a real-time chat project, testing, debugging, and CI/CD, we're ready to assemble those skills into a larger business system: an E-commerce Platform.

This project matters because e-commerce is a realistic architecture exercise:

  • products change over time
  • carts are temporary but important
  • checkout must coordinate several boundaries
  • orders need durable persistence
  • payment and inventory are external-risk areas
  • releases must be tested before they affect customers

Instead of treating this as one giant Controller, we'll build it as a set of clear responsibilities.

Capstone Orientation

Commerce System Shape

The project design

At a high level, the platform has five core modules:

  • Catalog: products, prices, availability display
  • Cart: selected products before purchase
  • Checkout: validation, inventory reservation, payment coordination
  • Order: durable record of what the customer bought
  • Admin/Operations: product updates, order status, release safety

A useful first architecture sketch:

Browser / Mobile Client
        |
        v
REST Controllers
        |
        v
Application Services
        |
        +--> Domain Model
        +--> Repositories / Hibernate
        +--> Payment Gateway Adapter
        +--> Inventory Adapter

This follows the layered source layout from Building a RESTful Web Service, the repository boundary from Database Integration with Hibernate, and the release discipline from Continuous Integration and Deployment.

Commerce System Shape

Domain First

Start with behavior, not tables

Before adding controllers or Hibernate annotations, model the core business behavior in plain Java. This keeps the domain testable and prevents the database schema from becoming the only design artifact.

In the real project, these classes would live under something like:

src/main/java/com/acme/shop/domain

Here is a small standalone version of the cart domain.

```java
import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.List;

public class EcommerceDomainDemo {
    record Product(String sku, String name, BigDecimal price) {}

    record CartLine(Product product, int quantity) {
        BigDecimal lineTotal() {
            return product.price().multiply(BigDecimal.valueOf(quantity));
        }
    }

    static class ShoppingCart {
        private final List<CartLine> lines = new ArrayList<>();

        void add(Product product, int quantity) {
            if (quantity <= 0) {
                throw new IllegalArgumentException("Quantity must be positive");
            }
            lines.add(new CartLine(product, quantity));
        }

        BigDecimal subtotal() {
            return lines.stream()
                    .map(CartLine::lineTotal)
                    .reduce(BigDecimal.ZERO, BigDecimal::add);
        }

        void printSummary() {
            System.out.println("Cart summary");
            for (CartLine line : lines) {
                System.out.println(line.quantity() + " x " + line.product().name()
                        + " = " + line.lineTotal());
            }
            System.out.println("Subtotal = " + subtotal());
        }
    }

    public static void main(String[] args) {
        Product keyboard = new Product("KB-100", "Mechanical Keyboard", new BigDecimal("89.99"));
        Product mouse = new Product("MS-200", "Wireless Mouse", new BigDecimal("29.50"));

        ShoppingCart cart = new ShoppingCart();
        cart.add(keyboard, 1);
        cart.add(mouse, 2);

        cart.printSummary();
    }
}
```

Boundary Check

During checkout, which layer should coordinate stock reservation, payment charging, and order creation?

API Persistence Contract

Define the contract before coding deeply

A practical REST contract for the platform could start with:

GET    /api/products
GET    /api/products/{sku}
POST   /api/cart/items
GET    /api/cart
POST   /api/checkout
GET    /api/orders/{orderId}

Behind those endpoints, keep persistence behind repository interfaces:

ProductRepository
CartRepository
OrderRepository

The controller should not know whether data comes from Hibernate, an in-memory fake, or a test double. That separation is what made testing easier in Testing and Debugging Java Projects.

API Persistence Contract

Checkout Orchestration

The highest-risk use case

Checkout is where several earlier lessons meet:

  • Strategy Pattern: payment can vary by provider
  • Repository Boundary: orders should be persisted behind an interface
  • Transaction Boundary: order creation and inventory changes must be consistent
  • Testing Layers: payment and inventory should be faked in tests

This standalone sample shows the coordination shape without web or database code.

```java
import java.math.BigDecimal;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

public class CheckoutOrchestratorDemo {
    record CartLine(String sku, int quantity, BigDecimal price) {}
    record Order(String id, BigDecimal total, String status) {}

    interface PaymentGateway {
        boolean charge(BigDecimal amount);
    }

    static class ApprovingPaymentGateway implements PaymentGateway {
        public boolean charge(BigDecimal amount) {
            System.out.println("Payment charged: " + amount);
            return true;
        }
    }

    static class InventoryGateway {
        private final Map<String, Integer> stock = new HashMap<>();

        InventoryGateway() {
            stock.put("KB-100", 3);
            stock.put("MS-200", 1);
        }

        boolean reserve(List<CartLine> cart) {
            for (CartLine line : cart) {
                int available = stock.getOrDefault(line.sku(), 0);
                if (available < line.quantity()) {
                    System.out.println("Insufficient stock for " + line.sku());
                    return false;
                }
            }
            for (CartLine line : cart) {
                stock.put(line.sku(), stock.get(line.sku()) - line.quantity());
            }
            System.out.println("Inventory reserved");
            return true;
        }
    }

    static class CheckoutService {
        private final InventoryGateway inventory;
        private final PaymentGateway paymentGateway;

        CheckoutService(InventoryGateway inventory, PaymentGateway paymentGateway) {
            this.inventory = inventory;
            this.paymentGateway = paymentGateway;
        }

        Order checkout(List<CartLine> cart) {
            if (!inventory.reserve(cart)) {
                throw new IllegalStateException("Checkout failed: stock unavailable");
            }

            BigDecimal total = cart.stream()
                    .map(line -> line.price().multiply(BigDecimal.valueOf(line.quantity())))
                    .reduce(BigDecimal.ZERO, BigDecimal::add);

            if (!paymentGateway.charge(total)) {
                throw new IllegalStateException("Checkout failed: payment declined");
            }

            Order order = new Order("ORD-1001", total, "CONFIRMED");
            System.out.println("Order created: " + order);
            return order;
        }
    }

    public static void main(String[] args) {
        CheckoutService service = new CheckoutService(
                new InventoryGateway(),
                new ApprovingPaymentGateway()
        );

        List<CartLine> cart = List.of(
                new CartLine("KB-100", 1, new BigDecimal("89.99")),
                new CartLine("MS-200", 1, new BigDecimal("29.50"))
        );

        service.checkout(cart);
    }
}
```

Payment Responsibility

True or false: a payment strategy should directly update order database rows because it knows whether the charge succeeded.

Release Ready

Treat the project like production software

For this capstone, your release checklist should connect directly to prior lessons:

  • Project Setup and Version Control: meaningful commits, clear README, reproducible setup
  • Testing and Debugging Java Projects: unit tests for cart and checkout, integration tests for repositories
  • Continuous Integration and Deployment: build, test, package, and deploy through a repeatable workflow
  • Database Integration with Hibernate: migrations or schema setup documented clearly

A real e-commerce app fails not only when code crashes, but when releases are unclear, tests are unreliable, or operational behavior is invisible.

Release Ready

Layer Recall

The class that coordinates cart validation, inventory reservation, payment charging, and order creation should live in the ____ layer.

Course Wrap

Your final implementation path

A realistic source layout for the capstone:

src/main/java/com/acme/shop
  catalog/
    Product.java
    ProductRepository.java
    ProductController.java
  cart/
    ShoppingCart.java
    CartController.java
  checkout/
    CheckoutService.java
    PaymentGateway.java
    InventoryGateway.java
  order/
    Order.java
    OrderRepository.java
    OrderController.java

Before calling the project complete, run a small readiness check. In a real repository, these checks would be enforced by CI, but the idea is the same: shipping is a discipline, not a button.

```java
import java.util.List;

public class ReleaseReadinessCheck {
    record Check(String name, boolean passed) {}

    public static void main(String[] args) {
        List<Check> checks = List.of(
                new Check("Domain model has cart and order behavior", true),
                new Check("Checkout service has unit tests", true),
                new Check("Repositories are behind interfaces", true),
                new Check("Payment gateway is replaceable", true),
                new Check("CI pipeline runs build and tests", true)
        );

        boolean ready = true;
        for (Check check : checks) {
            String status = check.passed() ? "PASS" : "FAIL";
            System.out.println(status + " - " + check.name());
            ready = ready && check.passed();
        }

        System.out.println(ready
                ? "E-commerce platform is ready for a demo release."
                : "Fix failing checks before release.");
    }
}
```
Open interactive view →