---
description: Fundamental architectural principles and patterns for dual-implementation system
alwaysApply: true
---

# Architecture Principles

## Dual Implementation Strategy (MANDATORY)
EVERY feature MUST support both native and JavaScript modes:

### Implementation Router Pattern
```javascript
// ALWAYS follow this pattern in public API
if (useNative && MixpanelReactNative) {
  this.mixpanelImpl = MixpanelReactNative;
} else {
  if (useNative) {
    console.warn("MixpanelReactNative not available; using JavaScript mode...");
  }
  this.mixpanelImpl = new MixpanelMain(token, trackAutomaticEvents, storage);
}
```

## Factory Function Pattern (REQUIRED)
Core modules MUST use dependency injection for testability:

```javascript
// Correct: Factory with injected dependencies
export const MixpanelCore = (storage) => {
  const mixpanelPersistent = MixpanelPersistent.getInstance(storage);
  return { initialize, track, flush };
};

// Wrong: Direct dependencies
import storage from './storage'; // Don't do this
```

## Singleton Pattern with Lazy Initialization
Shared resources (config, storage) MUST use singleton pattern:

```javascript
// Correct pattern for singletons
export const MixpanelQueueManager = (() => {
  let _instance;
  
  const getInstance = () => {
    if (!_instance) {
      _instance = { /* initialize */ };
    }
    return _instance;
  };
  
  return { getInstance };
})();
```

## Graceful Degradation (CRITICAL)
ALWAYS provide fallbacks, NEVER let the system fail completely:

### Native → JavaScript Fallback
```javascript
// Try native first, fall back to JS
if (nativeModule && nativeModule.method) {
  return nativeModule.method(params);
} else {
  return jsImplementation(params);
}
```

### Storage → In-Memory Fallback
```javascript
// Try AsyncStorage, fall back to in-memory
try {
  this.storage = require("@react-native-async-storage/async-storage");
} catch {
  console.error("[Mixpanel] Falling back to in-memory storage");
  this.storage = new InMemoryStorage();
}
```

## Token-Based Multi-Tenancy (FUNDAMENTAL)
ALL state MUST be scoped by token for multi-project support:

```javascript
// Correct: Token-scoped state
this._superProperties = {};  // Structure: {token1: {props}, token2: {props}}
this._identity = {};         // Structure: {token1: {id}, token2: {id}}

// Correct: Token-scoped operations
getSuperProperties(token) {
  return this._superProperties[token] || {};
}

// Wrong: Global state
this.superProperties = {};   // Don't share across tokens
```

## Write-Through Caching Pattern
ALWAYS maintain both memory cache AND persistent storage:

```javascript
// Correct: Update both cache and storage
updateSuperProperties(token, properties) {
  // Update in-memory cache
  this._superProperties[token] = {...properties};
  
  // Persist to storage
  this.persistSuperProperties(token);
}
```

## Queue-Based Processing
ALL data operations MUST use queuing for reliability:

```javascript
// Correct: Queue then batch process
const enqueue = async (token, type, data) => {
  _queues[token][type].push(data);
  await updateQueueInStorage(token, type);
  
  // Trigger processing if needed
  if (_queues[token][type].length >= batchSize) {
    processBatch(token, type);
  }
};
```

## Immutable State Updates
ALWAYS use spread operators and minimize object creation:

```javascript
// Correct: Immutable updates
const eventProperties = Object.freeze({
  token,
  time: Date.now(),
  ...this.getMetaData(),
  ...superProperties,
  ...properties,
  ...identityProps,
});

// Correct: Only create objects when necessary
if (updateDistinctId || updateDeviceId || updateUserId) {
  updated = { ...record }; // Shallow copy only if needed
}
```

## Layer Separation
Maintain clear separation between layers:

1. **Public API**: Validation and routing only
2. **Implementation**: Platform-specific optimization  
3. **Core Processing**: Shared business logic
4. **Persistence**: State management
5. **Infrastructure**: Queue/network/storage

NEVER bypass layers or create circular dependencies.