Các cách thức kiến trúc Monorepo Microservices cho ứng dụng React đa nền tảng
Bằng cách kết hợp sức mạnh đa nền tảng và native của React và React Native cùng với phương pháp cấu trúc dự án Mono-repo, lập trình viên sẽ tiết kiệm được hàng chục giờ cho việc thiết kế những component hay function lặp đi lặp lại.

Bằng cách kết hợp sức mạnh đa nền tảng và native của React và React Native cùng với phương pháp cấu trúc dự án Mono-repo, lập trình viên sẽ tiết kiệm được hàng chục giờ cho việc thiết kế những component hay function lặp đi lặp lại. Theo số liệu đâu đó cho biết thì nếu sử dụng Mono-repo thì có đến 50-70% các component có thể sử dụng lại với một dự án được thiết kết được cấu trúc ngay từ ban đầu và 30-50% cho dự án đang làm dang dở.
Vậy nên trong bài blog hôm nay mình sẽ chia sẻ cách triển khai thiết kế các module dưới dạng một repo duy nhật để dễ dàng chia sẻ tài nguyên và bảo trì dự án.
Mono-repo là gì?
Trong các hệ thống kiểm soát sửa đổi (Revision Control System), monorepo là một chiến lược phát triển phần mềm trong đó mã cho nhiều dự án được lưu trữ trong cùng một kho. Tính đến năm 2017, các hình thức khác nhau của thực hành kỹ thuật phần mềm này đã được hơn hai thập kỷ, nhưng khái niệm chung mới chỉ được đặt tên gần đây. - Theo Wikipedia
Mono trong tiếng Hy Lạp là một, cô đơn, đơn lẻ và repo ở đây là repository (trong tiếng anh là kho chứa). Trường hợp ở đây là các repository trong các hệ thống kiểm soát phiên bản (Version Control System) như Git hay SVN.
Mono-repo là repository đơn lẻ mà trong đó các module con nằm gói gọn trong 1 repo. Ngoài ra, bên cạnh mono-repo thì còn có Multi-repo (nhiều repository) và Monolith (nguyên khối).

Mỗi kiến trúc Microservice trên đều đem lại một lợi ích nhất định. Với Monolith thì đây là kiến trúc căn nguyên và thường được sử dụng để kiến trúc các mô hình đúng nghĩa "nguyên khối" là không có sự phân chia module. Hiểu nôm na là mã nguồn dành cho giao diện người dùng (front-end) và truy vấn, truy cập vào database (back-end) sẽ nằm chung một repository.
Song các kiến trúc như Multi-repo và Mono-repo được tái kiến trúc từ Monolith để cách thức lưu trữ và quản lý mã nguồn trở nên "dễ thở" hơn. Cụ thể hơn về các các thức ấy dễ thở như thế nào thì mình sẽ không bàn luận trong bài này. Tuy nhiên các bạn có thể tham khảo qua bài viết này.

Mono-repo: React Native Web?
React Native là một framework được chống lưng và phát triển bởi facebook để sử dụng phát triển ứng dụng native app cho nền tảng di động và ReactJS là framework cho thiết kế SPA (Single Page Application) trên web. Khi kết hợp hai công nghệ này lại chúng ta cho ra đời React Native Web - công cụ để viết ứng dụng Web bằng các component được cung cấp bởi React. Các "gã khổng lồ công nghệ" như Twitter và Uber cũng dần dần chuyển sang công nghệ này cho các ứng dụng của họ.
Lợi thế khi áp dụng kiến trúc Microservice Mono-repo vào đó là vừa phát triển ứng dụng cho web và ứng dụng cho mobile thông qua một module dùng chung cho cả hai.
Sử dụng Yarn Workspaces để kiến trúc Mono-repo cho React Native Web
Trước khi đi vào các cách thức, chúng ta sẽ cần setup một số thứ trong cây mã nguồn. Dưới đây là hình minh họa cho cách cấu trúc cây mã nguồn.

Mình sẽ khởi tạo một thư mục con bên trong thư mục gốc tên là package. Folder package nàu sẽ chứa tắt cả các module con của dự án. Trong đó các module bao gồm:
- Mobile: Phiên bản của dự án cho nền tảng di động được viết bằng React Native. Để khởi tạo, chúng ta sẽ dùng
expo-cli
qua câu lệnhexpo init <Tên của ứng dụng>
nếu các bạn sử dụng Expo . Nhưng mình khuyên trong trường hợp này thì không nền dùng third-party service như Expo mà hãy khơi tạo quareact-native-cli
bằng commandnpx react-native init MyApp --template react-native-template-typescript
(Do mình sử dụng Typescript nên mới có thêm phần--template react-native-template-typescript
).

- Server: Đây là module sẽ chứa tất cả các logic backend như gọi API, truy cập database.
- Web: Module này sẽ mang vai trò như tên của nó là dùng cho tạo ứng dụng trên web sử dụng React Native Web. Trong trường hợp này thì chúng ta vẫn sẽ khởi tạo dự án React web như bình thường bằng
npx create-react-app --template typescript
(phần--template typescript
là không bắt buộc). Sau khi đã khởi tạo xong ứng dụng React Web thì chúng ta cần phải setup để biến React Web thành React Native Web (mình sẽ viết một bài riêng về cách setup này nhé).

- Shared: Đây là module cốt lỗi của một monorepo khi các logic trùng lặp giữa hai nền tảng web và di động được chia sẻ qua module này.
Sau khi đã kiến trúc cây thư mục như trên thì chúng ta sẽ đến với các cách thức biến dự án này theo mô hình mono-repo microservices nhé.
Yarn Workspaces
Workspaces là một cách tiếp cận mới được cung cấp trong phiên bản Yarn 1.0 để set up các kiến trúc gói (package architecture). Chỉ với yarn install
là có thể setup nhiều package cùng một lúc.
Sau khi đã có cây mã nguồn như trên thì chúng ta sẽ vào file package.json ở bên ngoài thư mục gốc

Chúng ta sẽ thêm các dòng lệnh để khởi tạo yarn workspaces là
{
// Thông báo cho package server biết đây là package private nên sẽ không bị public ra ngoài
"private": true,
// Chỉ định rằng tất cả các module con trong folder package đều nằm trong workspace
"workspaces":[
"package/*"
],
// Tên của package được đóng gói
"name": "monorepo",
// Phiên bản của package
"version": "1.0.0"
}
Sau đó thì chúng ta sẽ truy cập vào các module con và setup symlink đến package mẹ. Lần lượt chúng ta sẽ thêm các dòng lệnh sau vào package.json của 4 module con.

Lưu ý là cách đặt tên cũng ảnh hưởng đến symlink giữ module con và package gốc. @<tên của package gốc>/<tên của module con>
.
Tới đây thì việc setup yarn workspace cũng xong rồi, giờ chỉ có install và sử dụng thôi.
Để thử nghiệm, mình tạo một file index.ts trong module shared
const s: string = "Hello World";
export default s;
Trong package.json của module web, mình sẽ thêm vào phần dependencies tên của module con chung workspace

Sau đó thì chỉ cần yarn install
là chúng ta có thể sử dụng được.
Truy cập vào file index.ts trong ứng dụng React Web và mình gọi đến biến của module shared.

Chạy thử ứng dụng React web và cho ra kết quả là

Vậy là đã hoàn tất kiến trúc monorepo. Lợi thế của kiến trúc microservice này là sự tái sử dụng và chia sẻ logic giữa các module với nhau. Tuy nhiên vẫn còn có những điểm bất cập trong kiến trúc này khác với kiến trúc multi-repo đó là vì mỗi module tương thích với những phiên bản khác nhau nên sẽ dễ dàng sinh ra các xung đột giữa các tài nguyên. Điều này dẫn đến việc tốn thời gian trong việc bảo trì và update các phiên bản tài nguyên.
Một số cách thức khác...
Lerna
Nx: Extensible Dev Tools for Monorepos
