Khi phát triển ứng dụng đa nền tảng (Web với Next.js và Mobile với Expo), việc lặp lại logic nghiệp vụ là nguyên nhân chính gây ra bug và tăng chi phí bảo trì. Giải pháp phổ biến là tách các hàm thuần túy, custom hook và kiểu TypeScript vào một package chung (shared) nằm trong monorepo. Dưới đây là quy trình thực tế đã áp dụng thành công trong nhiều dự án. 1. Cấu trúc monorepo - Sử dụng Nx hoặc Turborepo để quản lý workspace. - Tạo thư mục packages/shared chứa: types/, utils/, hooks/, services/. - Cấu hình tsconfig.base.json với path alias "@shared/*" để import gọn gàng từ cả hai app. 2. Tách logic thuần túy - Các hàm tính toán, validation, định dạng dữ liệu không phụ thuộc vào UI đặt ở utils/. - Ví dụ: formatCurrency, calculateDiscount, parseDateRange. - Viết unit test cho từng hàm bằng Vitest; chạy test trong CI để đảm bảo không bị phá vỡ khi refactor. 3. Custom hook dùng chung - Hook như useAuth, useCart, usePagination đóng gói state, side‑effect và gọi API. - Hook chỉ phụ thuộc vào các service trong shared/services (ví dụ apiClient được cấu hình riêng cho web và native). - Trong app web: import { useAuth } from '@shared/hooks'; - Trong app mobile: cùng import, không cần viết lại. 4. Quản lý phụ thuộc nền tảng - Tạo interface PlatformApiClient với phương thức get, post… - Cung cấp implementation riêng: webApiClient (dùng fetch/axios) và nativeApiClient (dùng expo‑fetch). - Đăng ký implementation qua React Context hoặc dependency injection khi bootstrap app. 5. Lợi ích thực tế - Giảm 40‑50% dòng code trùng lặp. - Bug sửa ở shared tự động phản ánh cả hai nền tảng. - Dễ dàng onboarding thành viên mới: chỉ cần hiểu shared API. Mẹo áp dụng ngay: bắt đầu với một hook nhỏ (ví dụ useDebounce) và một utility (formatPhone). Mở rộng dần khi team quen với quy trình. Đảm bảo có script lint và type‑check chạy trên toàn bộ workspace trước khi merge.