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. 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).

Sơ đồ biểu hiện sự khác nhau của các phương pháp cấu trúc phần mềm (Monolith, Multi-repo, Mono-repo)

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) 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-repoMono-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 Vs. Multi-repo Vs. Hybrid: What’s the Right Approach? | Hacker Noon
I still remember my first day at Outbrain. As part of the Bootcamp (training program), we were required to clone the code from a repository called the trunk (one monolithic repo that contained all our codebase). It took at least half a day to clone and build the whole source code. Over the next year…

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 Uber cũng dần dần chuyển sang công nghệ này cho các ứng dụng của họ.

necolas/react-native-web
React Native for Web. Contribute to necolas/react-native-web development by creating an account on GitHub.

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.

Cây thư mục

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ệnh expo 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 qua react-native-cli bằng command npx 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).
Using TypeScript · React Native
TypeScript is a language which extends JavaScript by adding type definitions, much like Flow. While React Native is built in Flow, it supports both TypeScript and Flow by default.
  • 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é).
Adding TypeScript | Create React App
Note: this feature is available with react-scripts@2.1.0 and higher.
  • 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

Bên trong file package.json 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.

Bên trong package.json của các 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.

Bên trong file src/index.ts của module web

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

lerna/lerna
:dragon: A tool for managing JavaScript projects with multiple packages. - lerna/lerna

Nx: Extensible Dev Tools for Monorepos

Nx: Extensible Dev Tools for Monorepos
Nx is a suite of powerful, extensible dev tools to help you architect, test, and build at any scale — integrating seamlessly with modern technologies and libraries while providing a robust CLI, caching, dependency management, and more.
Góc Của Chung

Góc Của Chung