404 Not Found

404 Not Found


nginx

Project Design — PriceTracker Full-Stack Architecture Blueprint

Project design is like the blueprint before building a building—if you start construction without a blueprint, you might find that the foundation isn't strong enough by the time you reach the third floor, and you'll have no choice but to tear it down and start over. A good blueprint isn't something you draw and then discard; it serves as a guide throughout the entire construction process.

1. What You'll Learn


2. Alice's True Story

(1) Pain Point: The more features are added, the more cluttered it becomes

PriceTracker evolved from a simple API into a service with an ever-increasing number of features, but it lacked a unified design—the boundaries between products and prices were blurred, subscription logic was scattered across 10 endpoints, and the notification system was tightly coupled with business logic. Every time a new feature was added, five files had to be modified, and regression bugs occurred frequently.

(2) Solutions for Domain-Driven Design

Domain-Driven Design (DDD) identifies core domains and boundaries from a business perspective: Product Aggregate, Price Aggregate, User Aggregate, and Subscription Aggregate—each aggregate has clear boundaries and responsibilities, and communication across aggregates occurs via events, eliminating interdependencies.

(3) Revenue

To add new features, you only need to modify the code within the corresponding aggregate; changes no longer affect the entire system. Subscription logic is centralized in the Subscription aggregate, and notifications are triggered via the PriceUpdated event, ensuring complete decoupling.


3. Requirements Analysis

(1) Core Use Cases

Role Use Case Priority
User (Alice) Sign Up/Log In P0
User Check Product Price P0
User Subscription Price Change Notification P1
Administrator Bulk Import Prices P0
Administrator Manage Product CRUD P0
Front End (Bob) Call the API to retrieve data P0
DevOps (Charlie) Monitoring System Health P1
System Automatic Price Scraping P2

(1) ▶ Example: Script for Sorting Use Cases by Priority

PYTHON
# scripts/prioritize_use_cases.py
use_cases = [
    {"role": "User", "action": "register_login", "priority": "P0", "effort": 2},
    {"role": "User", "action": "query_price", "priority": "P0", "effort": 1},
    {"role": "User", "action": "subscribe_alert", "priority": "P1", "effort": 3},
    {"role": "Admin", "action": "bulk_import", "priority": "P0", "effort": 5},
]

# Sort by priority; within the same priority level, sort in ascending order by workload
order = {"P0": 0, "P1": 1, "P2": 2}
sorted_cases = sorted(use_cases, key=lambda x: (order[x["priority"]], x["effort"]))
for uc in sorted_cases:
    print(f"[{uc['priority']}] {uc['role']}: {uc['action']} (effort={uc['effort']}d)")

Output:

TEXT
# Execution Successful

(2) Non-functional Requirements

Requirements Objectives Constraints
QPS Millions (cluster) Nginx LB + K8s elasticity
P99 Latency < 50 ms Redis Cache + Asynchronous DB
Availability 99.9% Multi-replica + Automatic Recovery
Data Volume Millions of products + tens of millions of prices PostgreSQL Partitioning

4. Domain Model Design

(1) ER Diagram

100%
erDiagram
    User ||--o{ Subscription : has
    User ||--o{ Product : creates
    User ||--o{ Alert : sets
    Product ||--o{ Price : has
    Product ||--o{ Alert : watched_by
    Subscription ||--o{ Feature : includes
    
    User {
        int id PK
        string email UK
        string hashed_password
        string role
        datetime created_at
    }
    
    Product {
        int id PK
        string name
        string category
        float base_price
        string sku UK
        int user_id FK
        datetime created_at
    }
    
    Price {
        int id PK
        int product_id FK
        float price
        string currency
        string source
        datetime recorded_at
    }
    
    Subscription {
        int id PK
        int user_id FK
        string plan
        datetime starts_at
        datetime expires_at
    }
    
    Alert {
        int id PK
        int user_id FK
        int product_id FK
        float target_price
        string status
    }

(2) Aggregation Root Identification

Aggregate Root Contains Entities Boundary Rules
User User, Subscription, Alert The user owns subscriptions and alerts
Product Product, Price Product has price history
PriceImport Batch Information, Import Status Bulk import is a separate transaction

(1) ▶ Example: Domain Event Definitions

PYTHON
# domain/events.py
from dataclasses import dataclass, field
from datetime import datetime
from enum import Enum

class EventType(Enum):
    PRICE_CHANGED = "price_changed"
    ALERT_TRIGGERED = "alert_triggered"
    USER_SUBSCRIBED = "user_subscribed"

@dataclass
class DomainEvent:
    event_type: EventType
    aggregate_id: int
    occurred_at: datetime = field(default_factory=datetime.utcnow)
    payload: dict = field(default_factory=dict)

# Usage: Price Change Events
price_event = DomainEvent(
    event_type=EventType.PRICE_CHANGED,
    aggregate_id=42,
    payload={"old_price": 299.9, "new_price": 249.9, "currency": "CNY"},
)

Output:

TEXT
# Execution Successful

5. Technology Selection Decisions

(1) Model Comparison

Need Option A Option B Decision Reason
Web Frameworks FastAPI Flask/Django FastAPI Asynchronous + Auto-documentation + Type Checking
Database PostgreSQL MySQL/MongoDB PostgreSQL JSONB + Asynchronous Driver Mature
Caching Redis Memcached Redis Rich data structures + persistence
Task Queue Celery+Redis RQ/Dramatiq Celery Mature ecosystem + robust monitoring
Real-time Communication WebSocket SSE WebSocket Bidirectional + Low Latency
Container Docker Bare Metal/VM Docker Consistency + Orchestration

(2) Overview of the System Architecture

100%
flowchart TD
    Client[Client / Bob Frontend] --> Nginx[Nginx Load Balancer]
    Nginx --> API1[FastAPI Pod 1]
    Nginx --> API2[FastAPI Pod 2]
    Nginx --> APIN[FastAPI Pod N]
    
    API1 --> PG_Master[(PostgreSQL Master)]
    API2 --> PG_Master
    APIN --> PG_Master
    
    PG_Master --> PG_Replica[(PostgreSQL Replica)]
    
    API1 --> Redis[(Redis Cluster)]
    API2 --> Redis
    APIN --> Redis
    
    Redis --> CW1[Celery Worker 1]
    Redis --> CW2[Celery Worker 2]
    
    CW1 --> PG_Master
    CW2 --> PG_Master
    
    API1 --> WS[WebSocket Hub]
    API2 --> WS
    
    Prometheus[Prometheus] --> API1
    Grafana[Grafana] --> Prometheus

6. API Versioning Strategy

(1) URL Version vs. Header Version

Dimension URL Version /api/v1/ Header Version Accept: v=1
Visibility High (URL is explicit) Low (hidden in the header)
Routing Simple (prefix-based) Complex (requires middleware parsing)
Cache Independent URL Cache Requires Vary Header
Swagger Automatic grouping Requires manual configuration
Recommended ✅ How to Use PriceTracker Suitable for Internal APIs

(1) ▶ Example: URL Versioning Routing Structure

PYTHON
from fastapi import APIRouter

# V1 routes
v1_router = APIRouter(prefix="/api/v1", tags=["v1"])
v1_products = APIRouter(prefix="/products", tags=["products"])
v1_prices = APIRouter(prefix="/prices", tags=["prices"])
v1_auth = APIRouter(prefix="/auth", tags=["auth"])

# V2 routes (future)
v2_router = APIRouter(prefix="/api/v2", tags=["v2"])

# Mount routers
app.include_router(v1_auth)
app.include_router(v1_products, dependencies=[Depends(get_current_user)])
app.include_router(v1_prices, dependencies=[Depends(get_current_user)])
app.include_router(v1_router)

Output:

TEXT
# Execution Successful

❓ FAQ

Q What is the purpose of an aggregate root in DDD?
A An aggregate root defines transaction boundaries and ensures data consistency. Since Product is the aggregate root, Price can only be modified through Product and cannot be added or deleted independently, thereby ensuring data consistency.
Q How do you implement read-write separation in PostgreSQL?
A Use the primary database for writes and the secondary database for reads. Configure two engines in SQLAlchemy (write_engine and read_engine), and use the read_engine to connect to the secondary database for read operations. You'll need to account for replication latency.
Q Why choose Redis over Memcached?
A Redis supports persistence, multiple data structures (Hash/Set/ZSet), and Pub/Sub, while Memcached only supports simple key-value pairs. Redis offers a broader range of features.
Q When are API versions updated?
A Versions are only updated when there are breaking changes (such as removing fields or changing the response structure). Adding new fields or endpoints does not constitute a breaking change and does not require a new version.
Q How are domain events implemented?
A A simple approach uses Celery tasks (such as publish_price_updated_event.delay(product_id, new_price)), while a more complex approach uses message queues (RabbitMQ/Kafka).
Q How detailed should an architectural diagram be?
A For the team, it just needs to be "sufficient." Over-engineering wastes time, while under-engineering leads to confusion. PriceTracker strikes the right balance with its three-tier architecture and aggregate roots.

📖 Summary


📝 Exercises

  1. Basic Question (Difficulty: ⭐): Draw a system architecture diagram for PriceTracker (including FastAPI, PostgreSQL, Redis, Celery, and Nginx), and label the responsibilities of each component and the data flow. Hint: Mermaid flowchart
  2. Advanced Problem (Difficulty ⭐⭐): Design an ER diagram for the PriceTracker domain model, identify three aggregate roots (User, Product, PriceImport), and define the entities included in each aggregate and their boundary rules. Hint: Mermaid erDiagram + aggregate root table
  3. Challenge (Difficulty: ⭐⭐⭐): Complete the full architectural blueprint for PriceTracker—including the requirements document (core use cases + non-functional requirements), a technology comparison table, code for the API versioning routing structure, and a deployment topology diagram (including master-slave databases, caching tiers, and a Celery cluster). Hint: Incorporate all content covered in this lesson.

---|

Web-Tutorial.com

Web-Tutorial Tech Team

A team of developers maintaining programming tutorials. Each tutorial is written and reviewed by developers with expertise in that field. We work to keep our content accurate and reliable — if you spot an issue, please let us know.

100%

🙏 帮我们做得更好

我们是刚上线的编程教程站,几个人的小团队,精力有限。页面虽经检查,难免还有疏漏——链接失效、排版错乱、内容有误、语言生硬……

如果您发现了,麻烦告诉我们,我们会在收到反馈后第一时间进行修复,再次感谢您的光临 🙏