+3

Đơn giản hóa Micro Frontend bằng Nx Workspaces.

Hello mọi người, mình trở lại rồi đây 😂 😂

Dạo gần đây, chủ đề micro frontend thường xuyên xuất hiện ở các buổi interview cũng như các diễn đàn công nghệ. Chính vì thế, hôm nay mình sẽ giúp các bạn newbie có cái nhìn về nó và có thể setup một project micro-frontend một cách nhanh nhất có thể. Okay vào bài thôi nhé

1. Câu chuyện

Trước khi bắt đầu, tôi sẽ kể cho bạn một câu chuyện. Trong một buổi chiều thứ Sáu nọ,

Tui: "H này, mày có thấy dự án này càng ngày càng phình to không? Mỗi lần thêm một tính năng mới là lại sợ đụng vào code của team khác. Xong lại phải ngồi fix conflict cả ngày. Mệt muốn chết!"

H: "Chuẩn đấy! Tao cũng thấy vậy. Hôm trước tao đang làm cái module thanh toán, tự nhiên team UI đẩy code mới lên, xong cái là cả ứng dụng lỗi tè le. Tao ngồi debug cả buổi mới biết là do xung đột CSS. Mệt mỏi thật sự!"

Tui: "Ừ, rồi còn mỗi lần build cả cái ứng dụng to đùng này, máy tao chạy như sắp nổ tung luôn. Mà mỗi lần deploy là phải đợi cả tiếng đồng hồ. Chán không chịu nổi!"

H: "Tao nghe nói bên team kia họ đang dùng cái gì đó gọi là Micro Frontend, nghe bảo là chia nhỏ ứng dụng ra thành từng phần độc lập, mỗi team phụ trách một phần. Như vậy sẽ đỡ xung đột hơn, mà còn deploy riêng được nữa."

Tui: "Nghe hay đấy! Nhưng mà làm sao quản lý được mấy cái phần độc lập đó? Rồi làm sao để chúng nó chạy chung với nhau mà không đá nhau?"

H: "Tao tìm hiểu rồi, hình như có mấy công cụ hỗ trợ việc này. Ví dụ như Webpack Module Federation giúp chia sẻ code giữa các Micro Frontend, hay single-spa để quản lý vòng đời của từng ứng dụng con. Nhưng mà tao thấy cái Nx Workspaces này cũng hay lắm, nó không chỉ quản lý Micro Frontend mà còn hỗ trợ cả monorepo, chia sẻ thư viện chung, rồi còn tự động hóa cả testing và deployment nữa."

Tui: "Ồ, thế sao không dùng luôn Webpack Federation hoặc single-spa? Nghe cũng hot mà?"

H: "Tao cũng nghĩ vậy, nhưng mà Nx Workspaces có cái hay là nó tích hợp sẵn nhiều công cụ, từ code sharing, dependency graph, đến cả CI/CD. Mà nó cũng hỗ trợ cả Webpack Federation và single-spa luôn đấy. Kiểu như một công cụ toàn diện, không phải lo lắng về việc phải tự config từng thứ một."

Tui: "À, thế thì tiện đấy! Nhưng mà dùng Nx Workspaces có khó không? Tao sợ học cái mới lại mất thời gian lắm."

H: "Cũng không quá khó đâu. Nx có documentation khá chi tiết, lại còn có cả generator để tạo project nhanh chóng. Chúng mình có thể bắt đầu từ từ, thử nghiệm với một vài module trước, rồi dần dần áp dụng cho cả dự án."

Tui: "Nghe có vẻ khả thi đấy! Mày có tài liệu gì không? Chúng mình thử nghiên cứu rồi đề xuất với sếp xem sao. Chứ cứ tiếp tục thế này, sớm muộn gì cũng kiệt sức mà nghỉ việc quá!"

H: "Chuẩn! Để tao gửi mày vài link tài liệu. Cuối tuần này rảnh thì nghiên cứu chung nhé. Biết đâu lại tìm được cách cứu cả team khỏi địa ngục code!"

Tui: "Deal! Nhưng giờ đi uống trà sữa đã, code từ sáng đến giờ căng thẳng quá rồi!"

H: "Chuẩn không cần chỉnh!"

Câu chuyện giữa tui và bạn tui không chỉ là một cuộc trò chuyện vui vẻ mà còn phản ánh những thách thức thực tế khi phát triển ứng dụng lớn. Micro Frontend là một giải pháp tuyệt vời, nhưng việc lựa chọn công cụ phù hợp để quản lý chúng lại là một câu hỏi lớn. Trong bài viết này, chúng ta sẽ cùng tìm hiểu tại sao Nx Workspaces lại là lựa chọn tối ưu để quản lý Micro Frontend một cách hiệu quả. Hãy cùng khám phá!


2. Lí do chọn Nx Workspaces cho Micro Frontend

Nx Workspaces là lựa chọn tối ưu để quản lý Micro Frontend nhờ các ưu điểm sau:

  • Hỗ trợ đa công nghệ: Làm việc với nhiều framework như React, Angular, Vue.js trong cùng một workspace.

  • Mô hình Monorepo: Quản lý nhiều dự án trong một kho lưu trữ, dễ dàng chia sẻ code và quản lý dependency.

  • Phân tách rõ ràng: Tách biệt các Micro Frontend thành các ứng dụng/thư viện độc lập, giúp phát triển và bảo trì dễ dàng.

  • Tự động hóa: Tự động sinh code, chạy test, build và deploy, tích hợp CI/CD.

  • Phân tích và tối ưu hóa: Công cụ phân tích dependency và tối ưu hóa quá trình build.

  • Tích hợp tốt: Hỗ trợ các công cụ phổ biến như Webpack, Jest, Cypress, Git, và GitHub Actions.

  • Cộng đồng mạnh: Tài liệu phong phú và cộng đồng hỗ trợ lớn.

Nhờ những tính năng này, Nx giúp quản lý Micro Frontend hiệu quả, tăng năng suất và giảm độ phức tạp trong phát triển dự án.

3. Cài đặt workspaces

  • Node (20.19.0)
  • npm (10.8.2)
sudo npm i -g nx

nx --version
  • Tạo workspace bằng yarn
yarn create nx-workspace micro-frontend-nx
# micro-frontend-nx: tên workspace


✔ Which stack do you want to use? · none
✔ Would you like to use Prettier for code formatting? · Yess
✔ Which CI provider would you like to use? · skip
✔ Would you like remote caching to make your build faster? · skip

4. Cài đặt micro app

  • Host app: quản lí toàn bộ các micro app. Cũng là nơi xử lý authentication, routing, ... cho toàn bộ app.
  • Micro app (shop, cart): là các component hoặc page được tích hợp vào host app hoặc micro app khác.
cd micro-frontend-nx
yarn nx add @nx/react
nx g @nx/react:host apps/host --remotes=shop,cart

✔ Which stylesheet format would you like to use? · styled-components
✔ Which E2E test runner would you like to use? · none
✔ Which bundler do you want to use to build the application? · rspack

Sau này, nếu chúng ta muốn thêm các micro app khác thì:

nx g @nx/react:remote apps/about --host=host

4.1 Chạy ứng dụng

 nx run @micro-frontend-nx/host:serve:development --dev-remotes=shop,cart

Bạn sẽ thấy giao diện như này Screenshot 2025-03-23 at 11.38.43.png

4.2 Update Micro-app (by Page)

  • Xóa hết các nx-welcome.tsx trong ứng dụng để tránh conflict css.
  • host/src/app/app.tsx
import * as React from 'react';
import { Link, Route, Routes } from 'react-router-dom';

const Shop = React.lazy(() => import('shop/Module'));
const Cart = React.lazy(() => import('cart/Module'));

export function App() {
  return (
    <React.Suspense fallback={null}>
      <ul>
        <li>
          <Link to="/">Home</Link>
        </li>
        <li>
          <Link to="/shop">Shop</Link>
        </li>
        <li>
          <Link to="/cart">Cart</Link>
        </li>
      </ul>
      <Routes>
        <Route path="/" element={<p>Host app</p>} />
        <Route path="/shop" element={<Shop />} />
        <Route path="/cart" element={<Cart />} />
      </Routes>
    </React.Suspense>
  );
}

export default App;

  • cart/src/app/app.tsx
import styled from 'styled-components';

const StyledApp = styled.h5`
  // Your style here
  color: red;
`;

export function App() {
  return (
    <StyledApp>
      <h5>Welcome cart</h5>
    </StyledApp>
  );
}

export default App;

  • Tui đã xây dựng mfe với mỗi micro-app là một page. Bạn hãy thử làm với shop nhé.
  • Test thử với Nx graph

Screenshot 2025-03-23 at 11.53.54.png

4.3 Update Micro-app (by Feature)

Phần trên, tôi đã giới thiệu cho các bạn về việc chia micro-app theo page. Nhưng trên thực tế, có các team chỉ implement các component cho từng page riêng lẻ như payment, search, ...

Chính vì thế, tôi sẽ giả định có 1 team sẽ ownership tính năng search cho ứng dụng (seach product, search cart, search history,...) . Để dễ hơn cho demo, chúng ta sẽ thao tác search cart.

Đầu tiên, hãy cài đặt micro-app search nhé

nx g @nx/react:remote apps/search --host=host

✔ Which stylesheet format would you like to use? · scss
✔ Which E2E test runner would you like to use? · none
✔ Which bundler do you want to use to build the application? · rspack

Tuy nhiên, bởi vì chúng ta sử dụng Nx 20 nên sẽ có lỗi do nên của project không hợp lệ (https://github.com/nrwl/nx/issues/28558)

 NX   Invalid remote name: @micro-frontend-nx/search. Remote project names must:

- Start with a letter, dollar sign ($) or underscore (_)
- Followed by any valid character (letters, digits, underscores, or dollar signs)
The regular expression used is ^[a-zA-Z_$][a-zA-Z_$0-9]*$.
Pass --verbose to see the stacktrace.

Solution: Cần update name của micro-app này

nx g @nx/react:remote apps/search --host=host --name=search

✔ Which stylesheet format would you like to use? · scss
✔ Which E2E test runner would you like to use? · none
✔ Which bundler do you want to use to build the application? · rspack

 NX   Cannot find configuration for 'host'

Ẹt, error tiếp nè. Tiếp tục update hostname để fix thử xem.

   nx g @nx/react:remote apps/search --host=@micro-frontend-nx/host --name=search   

Okay, cuối cùng cũng thành công rồi. Bắt tay vào implement search app nào.

Import search micro-app to cart micro-app

  • Đầu tiên, cần remove <Search /> không cần thiết ở Host App

  • Kế đó, ta sẽ tạo tính năng cho micro seach như sau:

>> search/src/app/app.tsx

import styles from './app.module.scss';
export function App() {
  return (
    <div>
      <h5>Search module</h5>
    </div>
  );
}

export default App;


>> search/src/app/app.module.scss
h5 {
  color: green;
}

Lúc này, bạn mở VScode và config của search app đã được tự động thêm vào workspaces. Tuy nhiên, khi run app sẽ xuất hiện một vài lỗi do chưa setup typescript for search app.

src/app/app.tsx(8,34): error TS6059: File '/Users/albert.huynh/Desktop/learning/micro-frontend-nx/apps/search/src/remote-entry.ts' is not under 'rootDir' '/Users/albert.huynh/Desktop/learning/micro-frontend-nx/apps/cart/src'. 'rootDir' is expected to contain all source files.
src/app/app.tsx(8,34): error TS6307: File '/Users/albert.huynh/Desktop/learning/micro-frontend-nx/apps/search/src/remote-entry.ts' is not listed within the file list of project '/Users/albert.huynh/Desktop/learning/micro-frontend-nx/apps/cart/tsconfig.app.json'. Projects must list all files or use an 'include' pattern.
../search/src/remote-entry.ts(1,25): error TS6059: File '/Users/albert.huynh/Desktop/learning/micro-frontend-nx/apps/search/src/app/app.tsx' is not under 'rootDir' '/Users/albert.huynh/Desktop/learning/micro-frontend-nx/apps/cart/src'. 'rootDir' is expected to contain all source files.
../search/src/remote-entry.ts(1,25): error TS6307: File '/Users/albert.huynh/Desktop/learning/micro-frontend-nx/apps/search/src/app/app.tsx' is not listed within the file list of project '/Users/albert.huynh/Desktop/learning/micro-frontend-nx/apps/cart/tsconfig.app.json'. Projects must list all files or use an 'include' pattern.
  • cart/tsconfig.json
{
  ...
  "references": [
    {
      "path": "./tsconfig.app.json"
    },
    {
      "path": "./tsconfig.spec.json"
    },
    {
      "path": "../search"
    }
  ],
}

  • cart/tsconfig.app.json
{
  ...
  "references": [
    {
      "path": "../search/tsconfig.app.json"
    }
  ]
}

  • cart/module-federation.config.ts
import { ModuleFederationConfig } from '@nx/module-federation';

const config: ModuleFederationConfig = {
  name: 'cart',
  exposes: {
    './Module': './src/remote-entry.ts',
  },
  remotes: ['search'],
};

/**
 * Nx requires a default export of the config to allow correct resolution of the module federation graph.
 **/
export default config;

Kết quả thu được:

Screenshot 2025-03-23 at 13.18.24.png

Ở đây, tôi sẽ xem dependency graph như thế nào? Các bạn có thể thấy host app sẽ phụ thuộc cart app và shop app. Trong cart app sẽ phụ thuộc vào search app. Kkkk, đúng yêu cầu đặt ra lúc đầu rồi. Screenshot 2025-03-23 at 13.19.37.png

5. Tổng kết

Cuối cùng thì hôm nay chúng ta đã có thể setup 1 project micro frontend cơ bản rồi. Ở bài sau, tôi sẽ mang đến cho các bạn các chủ đề như Routing, Authentication, Communication,...

Hãy lan tỏa nó bằng cách like & share bài nhé các bạn. Cảm ơn các bạn nhiều.

Github: https://github.com/thoaihuynh2509/Micro-Frontend-Nx/

Linkedln: https://www.linkedin.com/in/thoai-huynh-5035a015b/


All rights reserved

Viblo
Hãy đăng ký một tài khoản Viblo để nhận được nhiều bài viết thú vị hơn.
Đăng kí