Tích hợp lúc thực thi có thể được thực hiện bằng hai kỹ thuật chính là “hợp thành phía máy khách” (client-side composition) và “hợp thành phía máy chủ” (server-side composition).
2.3.2.1. Hợp thành phía máy khách
Kỹ thuật hợp thành phía máy khách được dùng để tích hợp các micro-frontends ở phía máy khách. Theo hướng này, thành phần container sẽ được xây dựng và triển khai
13 https://webpack.js.org/ 14 https://www.npmjs.com/
32
tách biệt. Mỗi một micro-frontend được kết xuất ra thành một gói riêng và container sẽ lấy từng micro-frontend để chèn vào chương trình tùy theo yêu cầu. Có ba kỹ thuật chính có thể được áp dụng là dùng iframe, dùng JavaScript và sử dụng các thành phần web [22].
Sử dụng iframe
Iframe là một thành phần của HTML, được dùng để nhúng một tài liệu HTML này vào bên trong một tài liệu HTML khác. Mã nguồn trong hình 2.9 minh họa việc sử dụng iframe để tích hợp các micro-frontends.
<html> <head>
<title>Online Store</title> </head>
<body>
<h1>Welcome to Online Store Web App!</h1>
<iframe id="store-container"></iframe>
<script type="text/javascript">
const microFrontendsRouting = { '/': 'https://onlinestore.com/index.html', '/order-confirm': 'https://order-confirm.onlinestore.com/index.html', '/customer-info': 'https://customer-info.onlinestore.com/index.html', '/order-summary': 'https://order-summary.onlinestore.com/index.html' };
const iframe = document.getElementById('store-container'); iframe.src = microFrontendsByRoute[window.location.pathname]; </script>
</body> </html>
Hình 2.9. Sử dụng iframe để tích hợp micro-frontends
Việc dùng iframe được xem là kỹ thuật đơn giản để tích hợp các thành phần khác nhau vào một trang web, và nó tạo ra một sự cô lập giữa các ứng dụng con, ít phụ thuộc lẫn nhau. Tuy nhiên, nhược điểm của kỹ thuật này là không thể thực hiện việc chia sẻ các thư viện chung giữa các iframe, và việc giao tiếp nội bộ giữa các ứng dụng con trong từng iframe kém đi sự linh hoạt. Đặc biệt, khi cấu trúc ứng dụng phức tạp (có nhiều mức phân cấp cha con khác nhau) thì việc tương tác, điều hướng cũng như sử dụng các liên kết bên trong ứng dụng trở nên khá khó khăn.
Sử dụng JavaScript
Một kỹ thuật khác linh hoạt hơn là sử dụng ngôn ngữ JavaScript để tích hợp các thành phần. Mỗi một module con sẽ được xây dựng thành một gói riêng rẽ (thuật ngữ
33
gọi là bundle). Mỗi micro-frontend sẽ được nạp vào trang web khi cần thiết bằng cách sử dụng cặp thẻ <script/>, và khi được nạp vào trang nó sẽ xuất ra một hàm toàn cục hay còn được gọi là một điểm đầu vào (thuật ngữ gọi là “entry-point”). Thành phần container có trách nhiệm quyết định xem micro-frontend nào sẽ được thiết lập và thực hiện lời gọi tới các hàm liên quan để thông báo cho micro-frontend về vị trí cũng như thời điểm kết xuất. Ví dụ minh họa cho kỹ thuật này được mô tả trong đoạn mã nguồn ở hình 2.10.
<html> <head>
<title>Online Store</title> </head>
<body>
<h1>Welcome to Online Store Web App!</h1> <!--Import the separate bundles-->
<script src="https://order-confirm.onlinestore.com/bundle.js"></script> <script src="https://customer-info.onlinestore.com/bundle.js"></script> <script src="https://order-summary.onlinestore.com/bundle.js"></script> <div id="container-root"></div>
<script type="text/javascript">
// These global functions or entry points are attached to window by the above sc ripts
const microFrontendsRouting = {
'/order-confirm': window.renderOrderConfirm, '/customer-info': window.renderCustomerInfo, '/order-summary': window.renderOrderSummary, };
const renderFunction = microFrontendsRouting[window.location.pathname]; // After determine the entry-point function, we now invoke it,
// giving it the ID of the element where it should render itself
renderFunction('container-root'); </script>
</body> </html>
Hình 2.10. Tích hợp micro-frontends bằng JavaScript
Khác với cơ chế tích hợp lúc xây dựng, từng gói bundle.js được triển khai độc lập, và so với kỹ thuật dùng iframe thì kỹ thuật này đem lại cho chúng ta một sự linh hoạt trong việc tích hợp các module với nhau. Người lập trình có thể mở rộng mã nguồn theo nhiều hướng khác nhau, ví dụ như chỉ nạp các gói bundle.js khi cần, có thể truyền thêm dữ liệu và xác định thời điểm cũng như vị trí cụ thể từng micro-frontend được kết xuất
34
và hiển thị. Sự khả chuyển trong việc cá nhân hóa mã nguồn cùng với khả năng triển khai độc lập làm cho kỹ thuật này trở thành một phương pháp tiếp cận phổ biến và rộng rãi.
Sử dụng các thành phần web
Trong công nghệ làm web hiện đại, việc sử dụng các thành phần web (thuật ngữ gọi là “web component”) để đóng gói các chức năng phía giao diện đã trở thành phổ biến. Thành phần web ở đây có thể hiểu là các API ở mức độ cấp thấp mà phía trình duyệt hỗ trợ để giúp lập trình viên có thể mở rộng, tùy biến nhằm tạo ra các thành phần mới tùy theo nhu cầu sử dụng. Để làm được điều này, các thành phần web đều phải được quy định sử dụng một chuẩn chung bằng cách dùng HTML và các API của DOM cũng như kết hợp nhiều kỹ thuật khác nhau [23].
Các thư viện và framework phát triển web hiện nay như Angular, ReactJS hay VueJS đều cung cấp một kiến trúc thành phần để lập trình viên có thể tạo ra các thành phần giao diện được đóng gói, có tính mở rộng và tái sử dụng được. Sử dụng các thành phần web của các framework sẵn có giúp nhà phát triển nhanh chóng và dễ dàng trong việc xây dựng ứng dụng. Tuy nhiên, một số trường hợp có thể gây trở ngại, ví dụ chúng ta không thể sử dụng một số thành phần của Angular bên trong ứng dụng viết bằng ReactJS và ngược lại nếu như các phiên bản của framework chưa được hỗ trợ từ phía nhà phát triển. Để giải quyết các khó khăn này, lập trình viên cần phải tự tạo ra các thành phần web dựa trên các chuẩn hỗ trợ sẵn có. Nói một cách khác, người lập trình phải tự định nghĩa, xây dựng và tạo ra các thành phần web phục vụ cho nhu cầu của mình. Tiếp cận theo hướng này mang lại sự linh hoạt, khả chuyển rất cao, tuy nhiên nó đòi hỏi người lập trình cần có trình độ vững vàng cũng như sự am hiểu sâu rộng về các mảng kỹ thuật liên quan thì mới có thể tùy biến được việc tạo ra các thành phần web theo nhu cầu để giải quyết những hạn chế còn tồn tại trong các framework.
2.3.2.2. Hợp thành phía máy chủ
Kỹ thuật hợp thành phía máy chủ quản lý điều phối các ứng dụng ở phía máy chủ trong quá trình kết xuất để hiển thị ra các trang HTML. Theo hướng tiếp cận này, việc quyết định nạp micro-frontend nào sẽ được thực hiện ở tầng máy chủ. Phía máy chủ quyết định việc điều hướng các yêu cầu từ phía máy khách thông qua các URL [24].
Về mặt kỹ thuật, có nhiều nền tảng đã được xây dựng sẵn khả năng tích hợp với các cơ chế đi kèm để phục vụ phương pháp tích hợp này. Ví dụ bên dưới minh họa việc tích hợp sử dụng Nginx15 kết hợp với cơ chế SSI16, một tính năng được hỗ trợ bởi hầu hết các máy chủ web. SSI cho phép người dùng đặt các chỉ thị bằng các thẻ trong một tệp
15 http://nginx.org/en/
35
HTML mẫu, khi yêu cầu được gửi tới máy chủ web, phía máy chủ sẽ phân tích các chỉ thị này và dựa vào đó để kết xuất và gửi trả lại nội dung dữ liệu cho phía máy khách.
Ban đầu, chúng ta cần tạo một trang HTML, đặt tên là index.html (xem hình 2.11). Trang này đóng vai trò là thành phần khung của ứng dụng, nó sẽ dùng các chỉ thị được hỗ trợ bởi SSI để xác định thành phần nội dung được hiển thị sau khi có kết quả xử lý từ phía máy chủ web.
<html lang="en" dir="ltr"> <head>
<meta charset="utf-8">
<title>Online Store Web App</title> </head> <body> <h1>Online Store</h1> <!--# include file="$COMPONENT.html" --> </body> </html> Hình 2.11. Tệp tin index.html
Bên phía máy chủ Nginx, các thông số cấu hình cho thành phần $COMPONENT ở trên được thực hiện trong tệp server.config:
server { listen 8080; server_name localhost; root /usr/share/nginx/html; index index.html; ssi on; # Redirect / to /onlinestore
rewrite ^/$ http://localhost:8080/onlinestore redirect;
# Decide which HTML fragment to insert based on the URL location /order-confirm {
set $PAGE 'order-confirm'; }
location /customer-info { set $PAGE 'customer-info'; }
location /order-summary { set $PAGE 'order-summary' }
# All locations should render through index.html error_page 404 /index.html;
}
Hình 2.12. Quản lý cấu hình trong tệp server.config
Việc xử lý theo cơ chế này có nhược điểm là nội dung trang web sẽ được kết xuất mỗi khi người dùng gửi yêu cầu và nó làm mất đi những lợi điểm mà kiểu ứng dụng SPA mang lại.
36