Skip to main content

Microservices Overview

ShopStack Platform implements a polyglot microservices architecture where two independent services handle different business domains while sharing a common data layer.

Service Responsibilities

Python Service

Domain Focus: Transactional operations
  • User authentication & JWT issuing
  • Product catalog management
  • Order creation & tracking
  • Payment processing & calculations
  • Tax & discount logic

Node.js Service

Domain Focus: User operations & analytics
  • User profile management
  • Product search & discovery
  • Sales reporting
  • Analytics & metrics
  • Health monitoring

Technology Stack

Python Service Stack

Flask 2.3.0+Lightweight WSGI web framework with:
  • Blueprint-based route organization
  • Custom JSON encoder for datetime and Decimal types
  • Application factory pattern for testing
app/__init__.py
from flask import Flask
from flask_sqlalchemy import SQLAlchemy
from flask_jwt_extended import JWTManager
from flask_cors import CORS

db = SQLAlchemy()
jwt = JWTManager()

def create_app(config_name=None):
    app = Flask(__name__)
    app.config.from_object(get_config(config_name))
    
    db.init_app(app)
    jwt.init_app(app)
    CORS(app)
    
    # Register blueprints
    app.register_blueprint(auth_bp, url_prefix="/api/auth")
    app.register_blueprint(products_bp, url_prefix="/api/products")
    app.register_blueprint(orders_bp, url_prefix="/api/orders")
    app.register_blueprint(payments_bp, url_prefix="/api/payments")
    
    return app
Flask-SQLAlchemy 3.1.1Database abstraction with support for:
  • PostgreSQL (production) via psycopg2-binary
  • SQLite (testing) for isolated test runs
  • Automatic table creation
  • Model relationships and lazy loading
Configuration:
  • Development: SQLite in-memory database
  • Production: PostgreSQL with connection pooling
  • Test: SQLite in-memory with fixtures
Flask-JWT-Extended 4.6.0JWT token management with:
  • Access token generation
  • Token verification middleware
  • User identity resolution
  • bcrypt password hashing (4.1.2)
Tokens include user ID and role for authorization checks.

Node.js Service Stack

Express 4.18.2Minimal and flexible Node.js framework with:
  • Route-based organization
  • Middleware chain for auth and validation
  • Global error handling
src/index.js
const express = require("express");
const cors = require("cors");
const { sequelize } = require("./models");

const app = express();

app.use(cors({ origin: "http://localhost:3000", credentials: true }));
app.use(express.json());

// Routes
app.use("/api/auth", authRoutes);
app.use("/api/users", userRoutes);
app.use("/api/products", productRoutes);
app.use("/api/reports", reportRoutes);

// Global error handler
app.use((err, req, res, _next) => {
  console.error("Unhandled error:", err);
  res.status(500).json({
    error: "Internal server error",
    message: process.env.NODE_ENV === "development" ? err.message : undefined
  });
});
Sequelize 6.35.0Promise-based ORM with:
  • Model definition and associations
  • Automatic migrations
  • PostgreSQL support via pg driver
  • SQLite for testing
Model Example:
src/models/index.js
const User = sequelize.define("User", {
  id: { type: DataTypes.INTEGER, primaryKey: true, autoIncrement: true },
  email: { type: DataTypes.STRING, allowNull: false, unique: true },
  passwordHash: { type: DataTypes.STRING, allowNull: false },
  name: { type: DataTypes.STRING, allowNull: false },
  role: { type: DataTypes.STRING, defaultValue: "customer" }
});
jsonwebtoken 9.0.2JWT implementation with:
  • Token signing and verification
  • Expiration handling
  • bcryptjs for password hashing
  • express-validator for input validation
Supports the same JWT format as Python service for potential token sharing.

Database Schema

Both services share a unified PostgreSQL database with the following core tables:

Users Table

CREATE TABLE users (
    id SERIAL PRIMARY KEY,
    email VARCHAR(255) NOT NULL UNIQUE,
    password_hash VARCHAR(255) NOT NULL,
    name VARCHAR(255) NOT NULL,
    role VARCHAR(50) DEFAULT 'customer',
    profile JSON,  -- Node.js only
    created_at TIMESTAMP DEFAULT NOW(),
    updated_at TIMESTAMP DEFAULT NOW()
);

CREATE INDEX idx_users_email ON users(email);
The profile field is a JSON column used by the Node.js service for storing extended user metadata.

Products Table

CREATE TABLE products (
    id SERIAL PRIMARY KEY,
    name VARCHAR(255) NOT NULL,
    description TEXT,
    price NUMERIC(10, 2) NOT NULL,
    stock INTEGER DEFAULT 0,
    category VARCHAR(100),
    created_at TIMESTAMP DEFAULT NOW(),
    updated_at TIMESTAMP DEFAULT NOW()
);

CREATE INDEX idx_products_category ON products(category);

Orders Table

CREATE TABLE orders (
    id SERIAL PRIMARY KEY,
    user_id INTEGER NOT NULL REFERENCES users(id),
    status VARCHAR(50) DEFAULT 'pending',
    subtotal NUMERIC(10, 2) NOT NULL,
    tax NUMERIC(10, 2) DEFAULT 0,
    total NUMERIC(10, 2) NOT NULL,
    created_at TIMESTAMP DEFAULT NOW(),
    updated_at TIMESTAMP DEFAULT NOW()
);

CREATE INDEX idx_orders_user_id ON orders(user_id);
CREATE INDEX idx_orders_status ON orders(status);

Order Items Table

CREATE TABLE order_items (
    id SERIAL PRIMARY KEY,
    order_id INTEGER NOT NULL REFERENCES orders(id),
    product_id INTEGER NOT NULL REFERENCES products(id),
    quantity INTEGER NOT NULL,
    price_at_time NUMERIC(10, 2) NOT NULL,
    created_at TIMESTAMP DEFAULT NOW()
);

CREATE INDEX idx_order_items_order_id ON order_items(order_id);

Service Communication

Shared Database Pattern

Both services communicate primarily through the shared database:
1

Write Operations

Each service writes to tables within its domain:
  • Python service creates/updates: products, orders, order_items
  • Node.js service creates/updates: users, user profiles
2

Read Operations

Both services can read from all tables:
  • Python service reads user data for authentication
  • Node.js service reads products and orders for reporting
3

Consistency

Database transactions ensure consistency:
  • Order creation with items is atomic
  • User registration with profile is transactional
While both services can access all tables, domain boundaries should be respected. Avoid having both services write to the same table to prevent conflicts.

Redis for Cross-Service Communication

Redis is used for:

Session Storage

JWT tokens and session data with TTL expiration

Caching

Frequently accessed data like product catalogs

Pub/Sub

Event notifications between services (future enhancement)

Rate Limiting

API rate limiting and throttling
Redis Configuration:
docker-compose.yml
redis:
  image: redis:7-alpine
  ports:
    - "6379:6379"
  healthcheck:
    test: ["CMD", "redis-cli", "ping"]
    interval: 5s

API Endpoints Overview

Python Service Endpoints (Port 5000)

MethodEndpointAuthDescription
POST/api/auth/registerNoRegister new user
POST/api/auth/loginNoLogin and get JWT
GET/api/auth/meJWTGet current user
MethodEndpointAuthDescription
GET/api/products/NoList products (paginated)
GET/api/products/<id>NoGet product by ID
GET/api/products/search?q=NoSearch products
POST/api/products/JWTCreate product
MethodEndpointAuthDescription
GET/api/orders/JWTList user’s orders
GET/api/orders/<id>JWTGet order by ID
POST/api/orders/JWTCreate new order
MethodEndpointAuthDescription
POST/api/payments/calculateJWTCalculate total with tax/discount
POST/api/payments/checkoutJWTProcess payment for order

Node.js Service Endpoints (Port 3000)

MethodEndpointAuthDescription
POST/api/auth/registerNoRegister new user
POST/api/auth/loginNoLogin and get JWT
MethodEndpointAuthDescription
GET/api/users/:idJWTGet user by ID
GET/api/users/me/profileJWTGet current user profile
PUT/api/users/me/profileJWTUpdate user profile
MethodEndpointAuthDescription
GET/api/productsNoList products (paginated)
GET/api/products/search?q=NoSearch products
GET/api/products/:idNoGet product by ID
POST/api/productsJWTCreate product
MethodEndpointAuthDescription
GET/api/reports/salesJWTGenerate sales report
MethodEndpointAuthDescription
GET/api/healthNoService health check

Deployment Architecture

Docker Compose Setup

The platform uses Docker Compose for orchestration with health checks and dependency management:
docker-compose.yml
version: "3.8"

services:
  postgres:
    image: postgres:15-alpine
    environment:
      POSTGRES_USER: appuser
      POSTGRES_PASSWORD: apppassword
      POSTGRES_DB: ecommerce
    ports:
      - "5432:5432"
    healthcheck:
      test: ["CMD-SHELL", "pg_isready -U appuser -d ecommerce"]
      interval: 5s

  redis:
    image: redis:7-alpine
    ports:
      - "6379:6379"
    healthcheck:
      test: ["CMD", "redis-cli", "ping"]
      interval: 5s

  python-service:
    build: ./python-service
    ports:
      - "5000:5000"
    environment:
      - DATABASE_HOST=postgres
      - REDIS_URL=redis://redis:6379/0
      - FLASK_ENV=staging
    depends_on:
      postgres:
        condition: service_healthy
      redis:
        condition: service_healthy

  node-service:
    build: ./node-service
    ports:
      - "3000:3000"
    environment:
      - DB_HOST=postgres
      - REDIS_URL=redis://redis:6379/1
      - NODE_ENV=production
    depends_on:
      postgres:
        condition: service_healthy
      redis:
        condition: service_healthy
Services use different Redis database indices (0 and 1) to isolate their cached data.

Testing Architecture

Both services use in-memory SQLite databases for testing to ensure:

Isolation

Each test run has a fresh database with no shared state

Speed

In-memory databases are significantly faster than PostgreSQL

Portability

No external dependencies required to run tests

Fixtures

Test data is loaded from fixtures in conftest.py/test setup
Python Test Configuration:
tests/conftest.py
@pytest.fixture
def app():
    app = create_app('testing')
    with app.app_context():
        db.create_all()
        yield app
        db.session.remove()
        db.drop_all()
Node.js Test Configuration:
jest.config.js
module.exports = {
  testEnvironment: 'node',
  coveragePathIgnorePatterns: ['/node_modules/'],
  testTimeout: 10000
};

Scalability Considerations

Both services are stateless and can be scaled horizontally:
  • Deploy multiple instances behind a load balancer
  • Use Redis for shared session state
  • Database connection pooling prevents resource exhaustion
  • JWT tokens eliminate session affinity requirements
  • Indexes on frequently queried columns (email, category, user_id)
  • Connection pooling with configurable pool size
  • Read replicas for report generation (Node.js service)
  • Prepared statements prevent SQL injection and improve performance
  • Redis caching for product catalogs and user sessions
  • TTL-based cache invalidation
  • Cache-aside pattern for frequently accessed data
  • Separate Redis databases per service for isolation
  • Health check endpoints for container orchestration
  • Database connection health verification
  • Error logging with context for debugging
  • Request/response logging in development mode

Security Architecture

Authentication

  • bcrypt password hashing with salt rounds
  • JWT tokens with expiration
  • Secure secret key storage via environment variables

Authorization

  • Role-based access control (customer, admin)
  • JWT middleware validates tokens on protected routes
  • User-scoped operations (users can only access their orders)

Input Validation

  • express-validator for Node.js request validation
  • Marshmallow schemas for Python data validation
  • SQL injection prevention via ORM parameterization

CORS

  • Configurable CORS policies
  • Credential support for authenticated requests
  • Origin whitelisting in production

Repository Structure

├── docker-compose.yml              # Container orchestration
├── python-service/
│   ├── app/
│   │   ├── __init__.py             # App factory with extensions
│   │   ├── config.py               # Environment-based configuration
│   │   ├── models/                 # SQLAlchemy models
│   │   │   ├── user.py
│   │   │   ├── product.py
│   │   │   └── order.py
│   │   ├── routes/                 # Blueprint route handlers
│   │   │   ├── auth.py
│   │   │   ├── products.py
│   │   │   ├── orders.py
│   │   │   └── payments.py
│   │   └── services/               # Business logic
│   ├── tests/                      # Pytest test suite
│   ├── requirements.txt
│   └── run.py
├── node-service/
│   ├── src/
│   │   ├── index.js                # Express app setup
│   │   ├── config.js               # Configuration
│   │   ├── models/                 # Sequelize models
│   │   │   └── index.js
│   │   ├── routes/                 # Route handlers
│   │   │   ├── auth.js
│   │   │   ├── users.js
│   │   │   ├── products.js
│   │   │   └── reports.js
│   │   ├── middleware/             # Auth & validation
│   │   └── services/               # Business logic
│   ├── tests/                      # Jest test suite
│   └── package.json
└── incidents/                      # Incident tickets (JSON)
The incidents/ directory contains structured JSON tickets for issue tracking and resolution workflows.

Build docs developers (and LLMs) love