Designing a Scalable Shopping Cart Service with Go and Redis: Architecture & Patterns

This article explains how to design a robust e‑commerce shopping‑cart service by separating business, system, and technical architectures, applying DDD, strategy and chain‑of‑responsibility patterns, and choosing Redis data structures for high‑performance storage.

Java High-Performance Architecture
Java High-Performance Architecture
Java High-Performance Architecture
Designing a Scalable Shopping Cart Service with Go and Redis: Architecture & Patterns

skr shop is a group of developers who, frustrated by project pain and proud of their own designs, decided to create an e‑commerce design handbook that focuses on design rather than coding.

Project address: https://github.com/skr-shop/manuals

The previous article described the general requirements of a shopping cart; this one focuses on the architectural design (business + system architecture).

Explanation

Architecture design can be divided into three layers:

Business architecture

System architecture

Technical architecture

When we receive a shopping‑cart requirement, we might choose Golang for implementation and Redis for storage – that describes the technical architecture. Organising code layers, design standards and dependent system planning constitutes the system architecture.

Business architecture is essentially a textual description of the system architecture. It starts with communication with stakeholders to establish a unified understanding, define terminology, and model entities such as cart, user, product, and order.

Common methods for business‑architecture analysis include Domain‑Driven Design (DDD), entity‑relationship diagrams, and UML. Regardless of the method, a design is always better than none, and the chosen method should suit the project.

In the business‑architecture analysis we use DDD concepts; the guiding principle remains "the suitable one is the best".

Business Architecture

Based on the previous requirement analysis, we know what the shopping cart should do. Below is a typical user journey.

During this process the user uses the cart as a stable container while products flow through it. The cart is the stable point; products are the changing data.

Products can be added from various sources and have different lifecycles, but the overall flow is stable: a product must exist in the cart before checkout can create an order.

The product lifecycle in the cart is illustrated below:

Corresponding operations for each stage are shown here:

The three stages above correspond to DDD entities that together form the cart domain; detailed discussion will be in a future article.

Pre‑Add

Pre‑add validation checks the product to be added from multiple dimensions: source verification, purchase qualification, and product attribute validation (e.g., stock, price, limits).

Because validation rules can vary greatly, we need an extensible implementation.

Two implementation options were considered:

Strategy pattern + Facade pattern: different strategies for different sources, wrapped by facades.

Chain‑of‑Responsibility pattern with the ability to skip nodes (e.g., flash‑sale skips stock check).

The chain‑of‑responsibility approach was chosen. Core code:

// Each validation logic must implement this interface
type Handler interface {
    Skipped(in interface{}) bool // decide whether to skip
    HandleRequest(in interface{}) error // perform validation
}

// Chain node
type RequestChain struct {
    Handler
    Next *RequestChain
}

// Set next handler
func (h *RequestChain) SetNextHandler(in *RequestChain) *RequestChain {
    h.Next = in
    return in
}

For more design‑pattern examples see: https://github.com/TIGERB/easy-tips/tree/master/go/src/patterns

Cart Operations

After pre‑add, the cart itself has several stable operations.

Add to Cart

Pre‑add validation already performed product retrieval, so the add operation can reuse that data without another DB call.

The add operation merges the new item into the existing cart, handling quantity increments, parent‑child relationships, and activity rules.

Note: Adding may happen from a list or detail page, not directly modifying the cart quantity.

After merging, the cart is checked against marketing activities and written back to storage.

Merge Cart

When a guest logs in, the guest cart must be merged with the user cart, reusing much of the add‑logic.

Cart List

Two list APIs are provided: a lightweight version for UI widgets and a full version for detailed views. Each read must verify data consistency; however, to improve performance, some checks can be deferred to the list operation.

Checkout

Checkout consists of a detail page and order submission. The checkout page adds address selection and final price calculation. Immediate‑purchase also uses the checkout API but internally adds the item to the cart first.

Order creation must lock the cart (e.g., Redis distributed lock) to prevent concurrent modifications. After locking, two approaches are possible:

Write directly to the database for low‑throughput scenarios.

Write a snapshot to a message queue for asynchronous processing in high‑throughput scenarios.

System Architecture

The system architecture maps the business architecture to code modules and defines Redis data structures.

Code Structure

The project follows a Go‑style layout:

├── addproducts.go
├── cartlist.go
├── mergecart.go
├── entity
│   ├── cart
│   │   ├── add.go
│   │   ├── cart.go
│   │   └── list.go
│   ├── order
│   │   ├── checkout.go
│   │   ├── order.go
│   │   └── submit.go
│   └── precart
├── event
│   └── sendorder.go
├── facade
│   ├── activity.go
│   └── product.go
└── repo

Four top‑level packages:

entity : domain entities (cart, order, precart) where most operations reside.

event : handles events such as sending order data to MQ.

facade : wraps external services (product, activity) to isolate third‑party changes.

repo : persistence layer (model) for all database interactions.

These divisions keep concerns separate, improve cohesion, and make the codebase easier to maintain.

Redis Storage

We store cart data in Redis using a single String value per cart (key format uid:cart_type). The value is a JSON array of ShoppingData structs, providing O(1) read/write and avoiding the overhead of hash or set structures.

// Shopping cart data
type ShoppingData struct {
    Item       []*Item `json:"item"`
    UpdateTime int64   `json:"update_time"`
    Version    int32   `json:"version"`
}

// Single item
type Item struct {
    ItemId       string   `json:"item_id"`
    ParentItemId string   `json:"parent_item_id,omitempty"`
    OrderId      string   `json:"order_id,omitempty"`
    Sku          int64    `json:"sku"`
    Spu          int64    `json:"spu"`
    Channel      string   `json:"channel"`
    Num          int32    `json:"num"`
    Status       int32    `json:"status"`
    TTL          int32    `json:"ttl"`
    SalePrice    float64  `json:"sale_price"`
    SpecialPrice float64  `json:"special_price,omitempty"`
    PostFree     bool     `json:"post_free,omitempty"`
    Activities   []*ItemActivity `json:"activities,omitempty"`
    AddTime      int64    `json:"add_time"`
    UpdateTime   int64    `json:"update_time"`
}

type ItemActivity struct {
    ActID    string `json:"act_id"`
    ActType  string `json:"act_type"`
    ActTitle string `json:"act_title"`
}

The ItemId uniquely identifies a product in the cart; ParentItemId represents parent‑child relationships; OrderId links the item to an order for scenarios like pre‑orders.

Using a string avoids large‑key problems by limiting the number of items per cart and simplifies serialization.

Summary

The shopping‑cart design is now complete; order‑table design will be covered in a separate article. Readers should be able to adapt the presented architecture and patterns to their own e‑commerce services.

Original Source

Signed-in readers can open the original source through BestHub's protected redirect.

Sign in to view source
Republication Notice

This article has been distilled and summarized from source material, then republished for learning and reference. If you believe it infringes your rights, please contactadmin@besthub.devand we will review it promptly.

Design PatternsBackend ArchitectureredisGoDomain-Driven DesignShopping Cart
Java High-Performance Architecture
Written by

Java High-Performance Architecture

Sharing Java development articles and resources, including SSM architecture and the Spring ecosystem (Spring Boot, Spring Cloud, MyBatis, Dubbo, Docker), Zookeeper, Redis, architecture design, microservices, message queues, Git, etc.

0 followers
Reader feedback

How this landed with the community

Sign in to like

Rate this article

Was this worth your time?

Sign in to rate
Discussion

0 Comments

Thoughtful readers leave field notes, pushback, and hard-won operational detail here.