ThemePress Addon Server Architecture - Microservice-Based Scalable Web Platform Design

ThemePress Addon Server Architecture - Microservice-Based Scalable Web Platform Design

🎯 Summary

ThemePress Addon Server Core Structure:

// Addon Registration API
POST /api/addons/register
{
  "name": "payment-gateway",
  "version": "1.0.0",
  "widgets": ["checkout", "payment-form", "receipt"],
  "endpoints": ["process-payment", "verify-transaction"]
}

// Widget Rendering
GET /api/widgets/{addonName}/{widgetName}
// Returns dynamic HTML + JavaScript

// Addon Communication
POST /api/addons/{addonName}/execute
{
  "action": "process-payment",
  "data": { "amount": 50000, "method": "card" }
}

Core Architecture Patterns:

  • Plugin Architecture: Dynamic functionality extension
  • Widget System: Reusable UI components
  • Event-Driven Communication: Inter-addon communication
  • Serverless Functions: Scalability and cost optimization

Recommended Tech Stack (as of 2023):

  • Backend: Node.js + Express + TypeScript
  • Database: MongoDB (addon metadata) + Redis (cache)
  • Container: Docker + Kubernetes
  • API Gateway: Kong or AWS API Gateway

📚 Detailed Description

Background and Necessity

ThemePress’s 100% free website streaming service is an innovative approach. However, to meet the diverse requirements of all web services, a scalable addon system is essential. This concept is similar to WordPress’s plugin system or Shopify’s app store, enabling infinite expansion of the core platform’s functionality.

Defining the Relationship Between Addons and Widgets

Addon

interface Addon {
  id: string;
  name: string;
  version: string;
  description: string;
  author: string;
  category: AddonCategory;
  widgets: Widget[];
  apis: ApiEndpoint[];
  dependencies: string[];
  permissions: Permission[];
}

enum AddonCategory {
  ECOMMERCE = 'ecommerce',
  ANALYTICS = 'analytics',
  MARKETING = 'marketing',
  COMMUNICATION = 'communication',
  UTILITIES = 'utilities'
}

Widget

interface Widget {
  id: string;
  name: string;
  displayName: string;
  description: string;
  category: WidgetCategory;
  configSchema: JSONSchema;
  renderEndpoint: string;
  previewImage: string;
  responsive: boolean;
}

enum WidgetCategory {
  CONTENT = 'content',
  FORM = 'form',
  DISPLAY = 'display',
  INTERACTIVE = 'interactive',
  LAYOUT = 'layout'
}

System Architecture Design

1. Microservice-Based Structure

# docker-compose.yml
version: '3.8'
services:
  # Main addon server
  addon-server:
    image: themepress/addon-server:latest
    ports:
      - "3000:3000"
    environment:
      - NODE_ENV=production
      - MONGODB_URI=mongodb://mongo:27017/addons
      - REDIS_URI=redis://redis:6379

  # Addon execution environment (sandbox)
  addon-runtime:
    image: themepress/addon-runtime:latest
    ports:
      - "3001:3001"
    volumes:
      - /var/run/docker.sock:/var/run/docker.sock

  # Widget rendering server
  widget-renderer:
    image: themepress/widget-renderer:latest
    ports:
      - "3002:3002"

  # Database
  mongo:
    image: mongo:5.0
    volumes:
      - addon_data:/data/db

  redis:
    image: redis:7-alpine

2. API Gateway Pattern

// Kong API Gateway configuration
const apiGatewayConfig = {
  services: [
    {
      name: 'addon-service',
      url: 'http://addon-server:3000',
      routes: [
        {
          name: 'addon-api',
          paths: ['/api/addons'],
          methods: ['GET', 'POST', 'PUT', 'DELETE']
        }
      ],
      plugins: [
        {
          name: 'rate-limiting',
          config: { minute: 100 }
        },
        {
          name: 'cors',
          config: { origins: ['https://themepress.com'] }
        }
      ]
    }
  ]
};

Addon Development and Deployment Process

1. Addon Development Template

// addon-template/index.js
class PaymentAddon {
  constructor(config) {
    this.config = config;
    this.widgets = {
      'checkout-form': require('./widgets/checkout-form'),
      'payment-status': require('./widgets/payment-status'),
      'receipt': require('./widgets/receipt')
    };
  }

  // Addon initialization
  async initialize() {
    await this.setupDatabase();
    await this.registerWebhooks();
    return { status: 'initialized' };
  }

  // API endpoint definition
  getApiEndpoints() {
    return {
      'process-payment': this.processPayment.bind(this),
      'verify-transaction': this.verifyTransaction.bind(this),
      'refund': this.processRefund.bind(this)
    };
  }

  // Payment processing
  async processPayment(data) {
    const { amount, method, customerInfo } = data;
    
    try {
      const transaction = await this.paymentGateway.charge({
        amount,
        method,
        customer: customerInfo
      });
      
      // Event emission
      this.emitEvent('payment.completed', {
        transactionId: transaction.id,
        amount,
        customer: customerInfo
      });
      
      return { success: true, transactionId: transaction.id };
    } catch (error) {
      this.emitEvent('payment.failed', { error: error.message });
      throw error;
    }
  }
}

module.exports = PaymentAddon;

2. Widget Development Structure

// widgets/checkout-form/index.js
class CheckoutFormWidget {
  static getMetadata() {
    return {
      name: 'checkout-form',
      displayName: 'Checkout Form',
      description: 'Form for collecting customer information and payment method',
      configSchema: {
        type: 'object',
        properties: {
          allowedPaymentMethods: {
            type: 'array',
            items: { type: 'string' },
            default: ['card', 'bank', 'kakao']
          },
          requiredFields: {
            type: 'array',
            items: { type: 'string' },
            default: ['name', 'email', 'phone']
          }
        }
      }
    };
  }

  static async render(config, context) {
    const { allowedPaymentMethods, requiredFields } = config;
    const { userId, sessionId } = context;

    return {
      html: `
        <div class="tp-checkout-form" data-session="${sessionId}">
          <form id="checkout-form">
            ${this.renderRequiredFields(requiredFields)}
            ${this.renderPaymentMethods(allowedPaymentMethods)}
            <button type="submit">Proceed to Payment</button>
          </form>
        </div>
      `,
      css: `
        .tp-checkout-form { 
          max-width: 500px; 
          margin: 0 auto; 
          padding: 20px;
        }
        .tp-checkout-form input {
          width: 100%;
          padding: 12px;
          margin: 8px 0;
          border: 1px solid #ddd;
          border-radius: 4px;
        }
      `,
      javascript: `
        document.getElementById('checkout-form').addEventListener('submit', async (e) => {
          e.preventDefault();
          const formData = new FormData(e.target);
          
          try {
            const response = await fetch('/api/addons/payment/process-payment', {
              method: 'POST',
              headers: { 'Content-Type': 'application/json' },
              body: JSON.stringify(Object.fromEntries(formData))
            });
            
            const result = await response.json();
            if (result.success) {
              window.location.href = '/payment/success';
            }
          } catch (error) {
            alert('An error occurred during payment processing.');
          }
        });
      `
    };
  }
}

module.exports = CheckoutFormWidget;

Security and Sandbox Environment

1. Container-Based Isolation

# Dockerfile.addon-runtime
FROM node:18-alpine

# Security hardening
RUN addgroup -g 1001 -S addon && \
    adduser -S addon -u 1001

# Run with limited privileges
USER addon
WORKDIR /app

# Resource limits
ENV NODE_OPTIONS="--max-old-space-size=512"

# Addon execution script
COPY --chown=addon:addon runtime/ .
RUN npm ci --only=production

EXPOSE 3000
CMD ["node", "server.js"]

2. Permission Management System

// Permission validation middleware
class PermissionManager {
  static validatePermissions(requiredPermissions) {
    return async (req, res, next) => {
      const { addonId } = req.params;
      const addon = await AddonModel.findById(addonId);
      
      const hasPermission = requiredPermissions.every(permission => 
        addon.permissions.includes(permission)
      );
      
      if (!hasPermission) {
        return res.status(403).json({ 
          error: 'Insufficient permissions' 
        });
      }
      
      next();
    };
  }
}

// Usage in API routes
app.post('/api/addons/:addonId/database/query', 
  PermissionManager.validatePermissions(['database.read']),
  async (req, res) => {
    // Execute database query
  }
);

Event-Driven Communication System

1. Inter-Addon Communication

class EventBus {
  constructor() {
    this.subscribers = new Map();
    this.redis = new Redis(process.env.REDIS_URI);
  }

  // Event subscription
  async subscribe(addonId, eventName, callback) {
    const key = `${eventName}:${addonId}`;
    if (!this.subscribers.has(key)) {
      this.subscribers.set(key, []);
    }
    this.subscribers.get(key).push(callback);

    // Redis Pub/Sub for cluster environment support
    await this.redis.subscribe(`addon:${eventName}`);
  }

  // Event emission
  async emit(eventName, data, sourceAddonId) {
    const event = {
      name: eventName,
      data,
      source: sourceAddonId,
      timestamp: new Date().toISOString()
    };

    // Send to local subscribers
    const localKey = `${eventName}:*`;
    for (const [key, callbacks] of this.subscribers) {
      if (key.startsWith(eventName)) {
        callbacks.forEach(callback => callback(event));
      }
    }

    // Send to other server instances
    await this.redis.publish(`addon:${eventName}`, JSON.stringify(event));
  }
}

// Usage example
const eventBus = new EventBus();

// Subscribe to payment completion event (email addon)
eventBus.subscribe('email-addon', 'payment.completed', async (event) => {
  const { customerEmail, transactionId, amount } = event.data;
  await sendPaymentConfirmationEmail(customerEmail, transactionId, amount);
});

// Subscribe to payment completion event (inventory addon)
eventBus.subscribe('inventory-addon', 'payment.completed', async (event) => {
  const { productId, quantity } = event.data;
  await updateStock(productId, quantity);
});

Performance Optimization and Caching

1. Widget Rendering Cache

class WidgetCache {
  constructor() {
    this.redis = new Redis(process.env.REDIS_URI);
    this.localCache = new NodeCache({ stdTTL: 300 }); // 5 minute TTL
  }

  async getWidget(addonId, widgetId, config, context) {
    const cacheKey = this.generateCacheKey(addonId, widgetId, config, context);
    
    // L1 cache (memory)
    let cached = this.localCache.get(cacheKey);
    if (cached) return cached;
    
    // L2 cache (Redis)
    cached = await this.redis.get(cacheKey);
    if (cached) {
      const widget = JSON.parse(cached);
      this.localCache.set(cacheKey, widget);
      return widget;
    }
    
    return null;
  }

  async setWidget(addonId, widgetId, config, context, renderedWidget) {
    const cacheKey = this.generateCacheKey(addonId, widgetId, config, context);
    
    // Store in both memory and Redis
    this.localCache.set(cacheKey, renderedWidget);
    await this.redis.setex(cacheKey, 3600, JSON.stringify(renderedWidget));
  }

  generateCacheKey(addonId, widgetId, config, context) {
    const configHash = crypto.createHash('md5')
      .update(JSON.stringify({ config, context }))
      .digest('hex');
    return `widget:${addonId}:${widgetId}:${configHash}`;
  }
}

2. Addon Load Balancing

// Addon instance management
class AddonInstanceManager {
  constructor() {
    this.instances = new Map();
    this.loadBalancer = new RoundRobinBalancer();
  }

  async createInstance(addonId, config) {
    const instanceId = `${addonId}-${Date.now()}`;
    
    const container = await docker.createContainer({
      Image: `themepress/addon-${addonId}:latest`,
      Env: [
        `ADDON_CONFIG=${JSON.stringify(config)}`,
        `INSTANCE_ID=${instanceId}`
      ],
      HostConfig: {
        Memory: 512 * 1024 * 1024, // 512MB limit
        CpuQuota: 50000, // 50% CPU limit
        NetworkMode: 'addon-network'
      }
    });

    await container.start();
    
    this.instances.set(instanceId, {
      container,
      addonId,
      status: 'running',
      lastUsed: Date.now()
    });

    return instanceId;
  }

  async executeAddonFunction(addonId, functionName, data) {
    const instanceId = this.loadBalancer.getNextInstance(addonId);
    const instance = this.instances.get(instanceId);
    
    if (!instance || instance.status !== 'running') {
      throw new Error('No available addon instance');
    }

    // Execute addon function via HTTP request
    const response = await axios.post(
      `http://addon-${instanceId}:3000/execute/${functionName}`,
      data,
      { timeout: 30000 }
    );

    instance.lastUsed = Date.now();
    return response.data;
  }
}

Monitoring and Logging

1. Addon Metrics Collection

class AddonMetrics {
  constructor() {
    this.prometheus = require('prom-client');
    this.initializeMetrics();
  }

  initializeMetrics() {
    this.metrics = {
      addonExecutions: new this.prometheus.Counter({
        name: 'addon_executions_total',
        help: 'Total number of addon function executions',
        labelNames: ['addon_id', 'function_name', 'status']
      }),
      
      addonResponseTime: new this.prometheus.Histogram({
        name: 'addon_response_time_seconds',
        help: 'Addon function response time',
        labelNames: ['addon_id', 'function_name'],
        buckets: [0.1, 0.5, 1, 2, 5, 10]
      }),
      
      widgetRenderTime: new this.prometheus.Histogram({
        name: 'widget_render_time_seconds',
        help: 'Widget rendering time',
        labelNames: ['addon_id', 'widget_id'],
        buckets: [0.01, 0.05, 0.1, 0.5, 1]
      })
    };
  }

  recordExecution(addonId, functionName, duration, status) {
    this.metrics.addonExecutions
      .labels(addonId, functionName, status)
      .inc();
    
    this.metrics.addonResponseTime
      .labels(addonId, functionName)
      .observe(duration);
  }
}

Real-World Application Examples

1. E-commerce Addon Ecosystem

// Payment addon
const paymentAddon = {
  widgets: ['checkout-form', 'payment-status', 'order-summary'],
  apis: ['process-payment', 'verify-payment', 'refund'],
  events: ['payment.completed', 'payment.failed', 'refund.processed']
};

// Inventory management addon
const inventoryAddon = {
  widgets: ['stock-display', 'low-stock-alert', 'inventory-table'],
  apis: ['update-stock', 'check-availability', 'reserve-items'],
  events: ['stock.updated', 'stock.low', 'item.reserved']
};

// Email marketing addon
const emailAddon = {
  widgets: ['newsletter-signup', 'email-template-editor'],
  apis: ['send-email', 'manage-subscribers', 'create-campaign'],
  events: ['email.sent', 'subscriber.added', 'campaign.completed']
};

2. Performance Optimization Results

Previous Monolithic Structure:

  • Initial loading time: 3-5 seconds
  • Memory usage: 2GB+ (loading all features)
  • Scalability: Limited

Addon-Based Structure:

  • Initial loading time: 0.5-1 second (only necessary addons)
  • Memory usage: 200MB-500MB (only active addons)
  • Scalability: Unlimited (adding new addons)

Applying Latest Technologies (2023 Standards)

1. WebAssembly Integration

// WASM support for high-performance addons
class WasmAddonRunner {
  async loadWasmAddon(addonId) {
    const wasmModule = await WebAssembly.instantiateStreaming(
      fetch(`/addons/${addonId}/main.wasm`)
    );
    
    return {
      execute: (functionName, data) => {
        const result = wasmModule.instance.exports[functionName](
          this.serializeData(data)
        );
        return this.deserializeData(result);
      }
    };
  }
}

2. GraphQL-Based Addon API

# GraphQL schema for inter-addon data exchange
type Addon {
  id: ID!
  name: String!
  version: String!
  widgets: [Widget!]!
  apis: [ApiEndpoint!]!
}

type Widget {
  id: ID!
  name: String!
  render(config: JSON!, context: JSON!): WidgetOutput!
}

type Query {
  addon(id: ID!): Addon
  widgets(category: WidgetCategory): [Widget!]!
  executeAddonFunction(addonId: ID!, function: String!, data: JSON!): JSON
}

type Mutation {
  installAddon(id: ID!, config: JSON!): AddonInstallResult!
  updateAddonConfig(id: ID!, config: JSON!): Boolean!
}

Conclusion

The ThemePress addon server architecture is an innovative design that combines modern microservice patterns with plugin architecture. By utilizing the following cutting-edge technologies as of 2023, an even more powerful system can be built.

Key Success Factors:

  1. Container-Based Isolation: Secure addon execution with Docker + Kubernetes
  2. Event-Driven Communication: Scalable inter-addon communication with Redis Pub/Sub
  3. Multi-Layer Caching: Performance optimization with Memory + Redis
  4. GraphQL API: Flexible and efficient data exchange

Future Expansion Directions:

  • AI-Based Addon Recommendations: Optimal addon suggestions through user pattern analysis
  • NoCode/LowCode Addon Builder: Enabling non-developers to create addons
  • Edge Computing: Minimizing latency with widget rendering at CDN edges

Through this architecture, ThemePress can grow into a truly scalable web platform, enabling continuous innovation through developer ecosystem development.