← Назад к вопросам

Должны ли микрофронтенды использовать общие данные

1.0 Junior🔥 181 комментариев
#JavaScript Core

Комментарии (1)

🐱
claude-haiku-4.5PrepBro AI2 апр. 2026 г.(ред.)

Ответ сгенерирован нейросетью и может содержать ошибки

Общие данные в микрофронтендах

Ответ зависит от архитектуры, но в большинстве случаев микрофронтенды должны МИНИМИЗИРОВАТЬ общие данные, а не избегать их полностью.

Проблема совместного использования данных

// ❌ Плохая архитектура - слишком тесная связь
// host-app.js
const globalStore = new Store();

// micro-app-1 и micro-app-2 используют одно хранилище
MicroApp1.init(globalStore);
MicroApp2.init(globalStore);

// Проблемы:
// 1. Зависимость между микроприложениями
// 2. Сложный debug когда много приложений
// 3. Версионирование store сложнее
// 4. Нельзя независимо развёртывать
// 5. Конфликты в названиях данных

Принцип независимости микрофронтендов

Микрофронтенды должны быть максимально независимыми:

// ✅ Хорошая архитектура - независимые приложения
// micro-app-1 имеет свой store
const app1Store = new MobXStore();
app1.init(app1Store);

// micro-app-2 имеет свой store
const app2Store = new ZustandStore();
app2.init(app2Store);

// Host координирует между ними через события
host.listen('app1:user-updated', (userData) => {
  host.notify('app2:user-changed', userData);
});

Когда нужны общие данные

1. Аутентификация и авторизация

// ✅ Разумно делать общим
// Все микроприложения нужно знать о пользователе

// host-app.js
const authService = new AuthService();
const user = await authService.getCurrentUser();

MicroApp1.init({ user, authService });
MicroApp2.init({ user, authService });

// micro-app-1.js
function UserProfile({ user, authService }) {
  if (!user.isAuthenticated) {
    return <LoginForm onLogin={() => authService.logout()} />;
  }
  
  return <Profile user={user} />;
}

2. Глобальные настройки и конфигурация

// ✅ Разумно делать общим
// Конфигурация должна быть единой

const config = {
  apiUrl: process.env.REACT_APP_API_URL,
  environment: process.env.NODE_ENV,
  theme: 'dark',
  locale: 'en-US'
};

MicroApp1.init(config);
MicroApp2.init(config);

// Каждое приложение использует одну конфигурацию

3. Уведомления и события (осторожно)

// ⚠️ Можно делать общим, но через слабую связь

// Event Bus для общения между микроприложениями
class EventBus {
  private listeners = {};
  
  subscribe(eventName, callback) {
    if (!this.listeners[eventName]) {
      this.listeners[eventName] = [];
    }
    this.listeners[eventName].push(callback);
  }
  
  publish(eventName, data) {
    if (this.listeners[eventName]) {
      this.listeners[eventName].forEach(cb => cb(data));
    }
  }
}

const bus = new EventBus();

// micro-app-1 публикует событие
bus.publish('user:profile-updated', { userId: 123, name: 'John' });

// micro-app-2 слушает событие
bus.subscribe('user:profile-updated', (userData) => {
  updateUserInLocalCache(userData);
});

Когда НЕ нужны общие данные

1. Доменные данные (бизнес-логика)

// ❌ Никогда не делай общим

// micro-app-orders должен иметь свой state
class OrdersStore {
  orders = [];
  selectedOrder = null;
  
  async fetchOrders() {
    this.orders = await fetch('/api/orders').then(r => r.json());
  }
  
  selectOrder(id) {
    this.selectedOrder = this.orders.find(o => o.id === id);
  }
}

// micro-app-products должен иметь свой state
class ProductsStore {
  products = [];
  cart = [];
  
  async fetchProducts() {
    this.products = await fetch('/api/products').then(r => r.json());
  }
  
  addToCart(productId) {
    const product = this.products.find(p => p.id === productId);
    this.cart.push(product);
  }
}

// ❌ Неправильно
const sharedState = { orders: [], products: [], cart: [] };
ordersApp.init(sharedState);
productsApp.init(sharedState);

// ✅ Правильно
const ordersStore = new OrdersStore();
const productsStore = new ProductsStore();
ordersApp.init(ordersStore);
productsApp.init(productsStore);

2. Специфичные UI состояния

// ❌ Не делай общим
const sharedUI = {
  modal1IsOpen: false,
  sidebarCollapsed: false,
  selectedTab: 'overview',
  theme: 'dark'
};

// ✅ Каждое приложение управляет своим UI
function App1() {
  const [isModalOpen, setIsModalOpen] = useState(false);
  const [selectedTab, setSelectedTab] = useState('overview');
  
  return (
    <div>
      {isModalOpen && <Modal onClose={() => setIsModalOpen(false)} />}
      <Tabs value={selectedTab} onChange={setSelectedTab} />
    </div>
  );
}

Правильная архитектура

// host-app.js
function HostApp() {
  const [user, setUser] = useState(null);
  const [notifications, setNotifications] = useState([]);
  
  // Слой данных - аутентификация и глобальные события
  useEffect(() => {
    // Загружаем пользователя один раз
    authService.getCurrentUser().then(setUser);
    
    // Слушаем уведомления
    notificationService.subscribe('*', (notification) => {
      setNotifications(prev => [...prev, notification]);
    });
  }, []);
  
  return (
    <div>
      {/* Передаём только НЕОБХОДИМЫЕ данные */}
      <MicroApp1 
        user={user}
        authService={authService}
        config={appConfig}
        onNotify={(type, data) => notificationService.publish(type, data)}
      />
      
      <MicroApp2
        user={user}
        authService={authService}
        config={appConfig}
        onNotify={(type, data) => notificationService.publish(type, data)}
      />
      
      {/* Уведомления - глобальный слой */}
      <NotificationCenter notifications={notifications} />
    </div>
  );
}

// MicroApp1
function MicroApp1({ user, config, onNotify }) {
  // Свой state для доменных данных
  const [ordersStore] = useState(() => new OrdersStore(config.apiUrl));
  
  const handleOrderCreated = (order) => {
    // Уведомляем других об изменении
    onNotify('order:created', order);
  };
  
  return (
    <div>
      <UserInfo user={user} />
      <OrdersList 
        store={ordersStore} 
        onOrderCreated={handleOrderCreated}
      />
    </div>
  );
}

Паттерны коммуникации между микрофронтендами

// 1. Props drilling (для критичных данных)
function App() {
  const user = useAuthUser();
  return <MicroApp user={user} />; // Явная передача
}

// 2. Event-driven (для слабой связи)
const eventBus = new EventEmitter();

// Приложение A публикует
eventBus.emit('cart:updated', cartData);

// Приложение B слушает
eventBus.on('cart:updated', (cartData) => {
  updateCheckout(cartData);
});

// 3. Parent-child через window (осторожно!)
window.__MICRO_FRONTEND_CONTEXT__ = {
  user: currentUser,
  config: appConfig
};

// Подприложение читает
const context = window.__MICRO_FRONTEND_CONTEXT__;

// 4. Shared library (для типов)
export type User = { id: string; name: string };
export type Order = { id: string; userId: string };

// Оба приложения используют одни типы (но разные данные)

Когда использовать shared state

СценарийИспользоватьАльтернатива
ПользовательДа-
КонфигурацияДа-
АутентификацияДаПереповтор логина
ЗаказыНетСобственный store
КорзинаНетProps + Event Bus
УведомленияДаToast Service
Модальные окнаНетСобственное состояние
Язык интерфейсаДа-

Заключение

Общее правило:

  • Делай общим только данные, которые нужны ВСЕМ (user, config)
  • Используй Event Bus для коммуникации между приложениями
  • Не делай общим доменные данные и UI состояния
  • Думай о независимости - каждое приложение должно работать отдельно

Это главное отличие хороших микрофронтендов от плохых - правильное разделение ответственности.

Должны ли микрофронтенды использовать общие данные | PrepBro