# Multi-Tenant Support in the Mall E-Commerce Platform: Architecture and Implementation Options

> Explore multi-tenant support options for the Mall e-commerce platform. Learn about architectural approaches and implementation strategies to enhance scalability and resource utilization.

- Repository: [macro/mall](https://github.com/macrozheng/mall)
- Tags: architecture
- Published: 2026-02-28

---

**The Mall e-commerce platform implements a single-tenant architecture that requires separate deployments for each tenant, with no built-in tenant isolation in the database schema or application code.**

The macrozheng/mall repository is a widely-used open-source e-commerce system built with Spring Boot and MyBatis. While it delivers comprehensive B2C functionality, its approach to multi-tenant support follows an instance-per-tenant model rather than embedding tenant discrimination within the codebase.

## Current Architecture: Single-Tenant Design

The Mall codebase is explicitly designed as a single-tenant application. A comprehensive review of the repository reveals no `Tenant` entities, `tenantId` fields, or `storeId` columns anywhere in the model or service layers.

### Database Schema Structure

In [`document/sql/mall.sql`](https://github.com/macrozheng/mall/blob/main/document/sql/mall.sql), the entire database schema is defined without tenant isolation. Tables such as `pms_product`, `oms_order`, and `ums_admin` contain no tenant identifier columns. This design necessitates that each tenant operates with a dedicated database instance to ensure complete data isolation.

### Service Layer Implementation

Core business services in `mall-admin/src/main/java/com/macro/mall/` operate on entities without tenant scoping. Classes like `OmsOrderService` and `PmsProductService` process requests without distinguishing between different tenants, handling all data as if belonging to a single organization.

### Security Configuration

The `mall-security` module configures Spring Security globally without tenant-aware authentication. Located in `mall-security/src/main/java/com/macro/mall/security/`, this configuration authenticates users against a single user store without implementing tenant-specific permission models or isolated security contexts.

## Instance-Per-Tenant Deployment Strategy

Given the absence of embedded multi-tenant logic, Mall's approach to serving multiple tenants relies on infrastructure isolation rather than application-level discrimination.

### Separate Deployment Instances

To support multiple tenants, operators must spin up separate deployments of the entire application stack. Each instance requires:

- An independent database (or schema copy) populated from [`document/sql/mall.sql`](https://github.com/macrozheng/mall/blob/main/document/sql/mall.sql)
- Dedicated configuration files ([`application.yml`](https://github.com/macrozheng/mall/blob/main/application.yml)) with unique datasources and server ports
- Isolated caching layers and file storage systems

### Configuration Management

The [`application.yml`](https://github.com/macrozheng/mall/blob/main/application.yml) files contain global settings without per-tenant sections. Deployment-time configurations must be duplicated and customized for each tenant instance, managing separate connection pools, Redis databases, and storage endpoints for every deployment.

## Implementing True Multi-Tenant Support

To achieve shared-infrastructure multi-tenancy within a single deployment, significant modifications to the data model, persistence layer, and security context are required.

### Database Schema Modifications

Add a `tenant_id` column to every table storing tenant-specific data. This includes core tables like `pms_product`, `oms_order`, and `ums_member`, establishing a foreign key relationship to a central tenant registry.

### MyBatis Model Extensions

Extend entity classes in the domain model to include tenant identification:

```java
public class PmsProduct {
    private Long id;
    private String name;
    // ... existing fields ...
    
    private Long tenantId; // New field for tenant isolation
    
    // Getter and setter
    public Long getTenantId() { return tenantId; }
    public void setTenantId(Long tenantId) { this.tenantId = tenantId; }
}

```

Apply similar modifications to `OmsOrder`, `PmsBrand`, and other domain entities in the `mall-mbg` module.

### Tenant-Aware SQL Interception

Implement a MyBatis interceptor to automatically append tenant filtering to database operations:

```java
@Component
public class TenantInterceptor implements Interceptor {
    @Override
    public Object intercept(Invocation invocation) throws Throwable {
        MappedStatement ms = (MappedStatement) invocation.getArgs()[0];
        Object parameter = invocation.getArgs()[1];
        BoundSql boundSql = ms.getBoundSql(parameter);
        String sql = boundSql.getSql();

        // Retrieve current tenant from ThreadLocal context
        Long tenantId = TenantContextHolder.getTenantId();
        if (tenantId != null && sql.trim().toUpperCase().startsWith("SELECT")) {
            // Append tenant filtering - production code should use proper SQL parsing
            sql = sql.replaceFirst("(?i)FROM", 
                "FROM (SELECT * FROM (" + sql + ") t WHERE t.tenant_id = " + tenantId + ") sub FROM");
        }

        // SQL replacement logic requires reflection or custom SqlSource implementation
        // ... implementation details ...
        
        return invocation.proceed();
    }
    
    @Override
    public Object plugin(Object target) {
        return Plugin.wrap(target, this);
    }
    
    @Override
    public void setProperties(Properties properties) {}
}

```

Alternatively, integrate MyBatis-Plus's built-in tenant plugin for more robust SQL manipulation without manual string replacement.

### Authentication and Context Management

Modify the authentication flow to establish tenant context after login. Store the current tenant identifier in a `ThreadLocal` variable or Spring `SecurityContext` within a `TenantContextHolder` class, making it available to the MyBatis interceptor for all subsequent database operations.

Update service methods to automatically set the tenant ID when creating new entities, ensuring data integrity across the application layer.

## Summary

- **The Mall platform is single-tenant by design**, with no embedded multi-tenant support in the current codebase according to the source analysis.
- **Database tables lack tenant identifiers** - the schema in [`document/sql/mall.sql`](https://github.com/macrozheng/mall/blob/main/document/sql/mall.sql) contains no `tenant_id` columns.
- **Services operate without tenant scoping** - `OmsOrderService` and `PmsProductService` process all data as belonging to a single organization.
- **Current multi-tenant strategy requires separate deployments** - each tenant needs isolated application instances with dedicated databases and configurations.
- **True multi-tenancy requires custom development** - including schema modifications, MyBatis interceptors, and tenant-aware authentication contexts.

## Frequently Asked Questions

### Does Mall support multi-tenant architecture out of the box?

No, the macrozheng/mall repository is explicitly designed as a single-tenant system. The codebase contains no tenant isolation logic, with all services in `mall-admin` operating on a unified data model without tenant discrimination.

### How can I deploy Mall for multiple clients?

Deploy separate instances of the application for each client, each with its own database initialized from [`document/sql/mall.sql`](https://github.com/macrozheng/mall/blob/main/document/sql/mall.sql). Configure independent [`application.yml`](https://github.com/macrozheng/mall/blob/main/application.yml) files with unique datasources, ports, and storage configurations to ensure complete data isolation between tenants.

### What changes are needed to add tenant isolation to Mall?

You must add `tenant_id` columns to all tenant-specific tables, extend MyBatis entity classes like `PmsProduct` with tenant fields, implement a MyBatis interceptor to automatically filter SQL by tenant, and modify the security layer to maintain tenant context in a `ThreadLocal` holder during request processing.

### Is there a MyBatis plugin available for tenant filtering?

Yes, MyBatis-Plus provides a built-in tenant plugin that can automatically append `WHERE tenant_id = ?` conditions to queries. However, Mall uses standard MyBatis, so you would need to either migrate to MyBatis-Plus or implement a custom `Interceptor` as shown in the code example above to achieve automatic tenant filtering.