Microsystems for mobile app
Hello Boardy!
Microsystems
Với sự phát triển không ngừng của phần cứng, những chiếc điện thoại thông minh ngày càng trở nên mạnh mẽ hơn, không thua kém gì một chiếc máy tính.
Các ứng dụng dành cho loại thiết bị di động này cũng phong phú và lớn hơn, thậm chí đang thay thế chức năng của các ứng dụng desktop. Và khái niệm Siêu ứng dụng (Super app) trở thành một xu hướng khi thiết kế ứng dụng di động cho doanh nghiệp.
Đứng về phía góc độ nhà phát triển, một Super app sẽ cần một Super team, với số lượng hàng chục, hàng trăm developer cùng làm việc. Trong một quy trình phức tạp, áp lực của việc maintain source code là rất lớn. Tất cả những sự rườm rà cuối cùng sẽ đưa team của bạn tới bờ vực của sự làm việc kém hiệu quả. Mỗi dòng code cuối cùng sẽ có giá hàng triệu đô la.
Kiến trúc một ứng dụng như vậy cũng là một thách thức. Nhu cầu thay đổi, mở rộng là liên tục. Đâu là thứ phù hợp nhất cho một siêu ứng dụng? Câu hỏi luôn tạo ra những tranh luận không có hồi kết trên các diễn đàn. Tất nhiên mỗi loại kiến trúc đều có những ưu, nhược điểm đáng cân nhắc.
Để gia tăng hiệu suất làm việc (performance), đội phát triển có thể được chia thành nhiều các squad độc lập. Các squad càng độc lập, ít phụ thuộc vào nhau hiệu suất sẽ càng được cải thiện. Tất nhiên vấn đề thống nhất, tái sử dụng mã nguồn và các yếu tố nền tảng khác (libraries, configurations, resources,…) được coi là điều kiện cần. Các squad hiểu rõ domain của mình và nên có quyền quyết định kiến trúc của đội mình, có thể không giống với các đội khác nhưng phù hợp nhất với nhu cầu và hiểu biết của họ. Điều này sẽ giúp tiết kiệm thời gian và gia tăng tốc độ phát triển của từng đội nói riêng cũng như toàn hệ thống nói chung. Mặt khác việc trao quyền cho mỗi squad sẽ giúp nâng cao tính tự chủ (ownership) và tính sáng tạo trong việc xây dựng sản phẩm, có ý nghĩa lớn đến sự phát triển lâu bền của một tổ chức. Cạnh tranh là động lực để cải tiến và thay đổi.

Những tiêu chí này đặc biệt phù hợp với kiến trúc hệ thống dạng Microsystems hay Microservices. Tuy nhiên việc triển khai Microsystems cho ứng dụng di động là một thách thức khác. Đây là động lực ra đời của Boardy. Boardy sẽ giúp bạn kiến trúc ứng dụng di động của mình tương tự như một hệ thống của hàng trăm, thậm chí hàng nghìn các micro-system, cung cấp bộ giao thức chung phục vụ việc tương tác giữa các micro-system này với nhau, tạo thành các tính năng nghiệp vụ (business feature). Điều này là rất phù hợp với những ứng dụng lớn và có sự tham gia của nhiều squad phát triển cùng lúc. Nhưng cũng tương đối đơn giản ngay cả với ứng dụng và đội nhỏ. Vì vậy nó có thể được triển ngay từ sớm khi mà vấn đề mở rộng (scalability) vẫn còn chưa được tính đến. Hầu hết những vấn đề lớn đều bắt đầu từ những vấn đề nhỏ. Giống như cách Benjamin Franklin đã nói:
“Hãy cẩn thận với những chi phí nhỏ. Một lỗ rò bé có thể làm đắm cả con tàu.”
Xin chào, Boardy!
Cấu trúc ứng dựng trở thành Microsystems nói một cách đơn giản là cách chia nhỏ phạm vi (scope) ứng dụng thành một loạt các ứng dụng nhỏ hơn, mỗi ứng dụng nhỏ này được coi là một micro-system. Việc kết hợp lại của tất cả các ứng dụng kích cỡ siêu nhỏ (micro) này tạo thành một hệ thống (system) hoàn chỉnh.
Cùng xem xét một ứng dụng ví dụ gồm 3 chức năng chính như sau: Login (Đăng nhập), Register (Đăng ký) và Home Page (Trang chính).

Chúng ta đơn giản chia nó thành 3 ứng dụng con (micro): Login, Register và Home tương ứng.
Các ứng dụng micro sẽ tập trung xử lý nghiệp vụ trọng phạm vi mà nó đảm nhiệm không bao gồm các nhiệm vụ khác yêu cầu tài nguyên không sẵn có. Một nguyên tắc luôn nhớ là các micro-system nên độc lập và không tương tác trực tiếp với nhau.
Trong thí dụ trên ứng dụng micro Đăng nhập có chứa nút để mở tính năng đăng ký nhưng ứng dụng Đăng nhập không trực tiếp xử lý việc này mà ủy thác (delegate) ra bên ngoài phương thức cần mở một ứng dụng khác gọi là ứng dụng micro Đăng ký. Việc này sẽ được thực hiện khi các ứng dụng micro này tích hợp cùng nhau.
Mỗi ứng dụng micro như trên trong Boardy sẽ tương đương với một Board
.
Mỗi Board
sẽ có nhiệm vụ như một AppDelegate
của ứng dụng micro kia (Boardy gọi là các Component). Board
cung cấp một phương thức activate
, là entry point để bắt đầu thực thi tính năng của Component. Nhiệm vụ của nó là khởi tạo và trình bày hoặc kích hoạt tính năng nghiệp vụ. Board
chứa liên kết với một root object, thường là UIWindow
hoặc UIViewController
, nơi nó được cài đặt vào. Có nghĩa Board
sẽ cần được install vào đâu đó trước khi activate
, nếu không nó sẽ raise một assertion.
func installIntoRoot(_ rootObject: AnyObject)
- BoardID
BoardID
định danh của một Board
trong hệ thống. Boardy sẽ sử dụng định danh này cho toàn bộ quá trình giao tiếp giữa các micro system (Board
).
extension BoardID {
static let login: BoardID = "com.ex.login"
static let register: BoardID = "com.ex.register"
static let home: BoardID = "com.ex.home"
}
- BoardFlow
Sau khi đã chia ứng dụng thành các micro-system (từ nay sẽ gọi là các Board
), tiếp theo cần tích hợp các Board
này lại, đó là lúc Motherboard
xuất hiện. Lấy cảm hứng từ bo mạch chủ của máy tính, nơi kết nối tất cả các linh kiện máy tính khác. Motherboard
trong Boardy có nhiệm vụ tích hợp các Board
với nhau tạo thành các luồng nghiệp vụ. Tại đây, các Board
giao tiếp và tương tác thông qua BoardFlow
.

- Đăng ký flow
Đăng ký các flow trên Motherboard
để ghép nối luồng xử lý của các Board
với nhau.
Sử dụng phương thức registerFlow(_:)
để đăng ký. Boardy hỗ trợ một số phương thức tiện ích đăng ký BoardFlow
dễ dàng hơn. Thực tế thường dùng những phương thức này hơn là việc tự tạo BoardFlow
.
/// Guaranteed Flow ensures data must match with Output type if not handler will fatal in debug and will be skipped in release mode. func registerGuaranteedFlow(...)/// Chain Flow handles step by step of chain of handlers until a handler in chain is executed. Eventually handler is mandatory to register this flow. func registerChainFlow(...)
Với ChainFlow, cần gọi phương thức
.eventuallyHandle(...)
ở cuối để hoàn thành việc đăng ký.
// Đăng ký flow login
motherboard.registerGuaranteedFlow(matchedIdentifiers: .login, target: self, uniqueOutputType: String.self) { target, output in
target.continueBoard(.home(user: output))
}
Đoạn code trên có nghĩa, sau khi login xong, tiếp tục kích hoạt
HomeBoard
với input là thông tinuser
sau khi login. Sau đóHomeBoard
sẽ mở màn hình chính của ứng dụng.
GuaranteedFlow
sẽ đảm bảo output củaLoginBoard
phải đúng với kiểu OutputTypeđược xác định ở đấy làString
. Trong trường hợp dữ liệu gửi từLoginBoard
không đúng kiểuString
, một assertion sẽ được raise.
- Gửi thông điệp từ một Board
Để có thể thoàn thành flow login bên trên, LoginBoard
cần gửi thông điệp đã đăng nhập thành công (đính kèm user info) tới Motherboard
.
Sử dụng phương thức sendToMotherboard(data:)
:
// LoginBoard
func loggedInSuccessfully(with user: String) {
sendToMotherboard(data: user)
}
Boardy hỗ trợ bổ sung các phương thức cho các mục đích sử dụng khác:
/// Activate một Board khác trong cùng Motherboard
func nextToBoard(...)
Flow bên trên có thể được làm lại một cách ngắn gọn như sau:
// LoginBoard
func loggedInSuccessfully(with user: String) {
nextToBoard(.home(user: output))
}
Và không cần đăng ký flow login trong Motherboard nữa.
nextToBoard
là flow đã được xử lý sẵn bên trong Boardy rồi.
/// Tương tác với một Board khác trong cùng Motherboard. Chúng ta sẽ tìm hiểu về luồng tương tác này sau.
func interactWithOtherBoard(...)/// Hoàn thành chức năng một Board và xoá nó khỏi Motherboard.
func complete()
- ContinuousBoard
Một Board
chứa bên trong nó là một motherboard
con - đại diện cho một tập các microsystems con được đóng gói.
Để tạo mới một Board, đơn giản kế thừa Board
hoặc ContinuousBoard
, conform ActivatableBoard
protocol và thực thi phương thức activate(withOption:)
.
Trên thực tế, chúng ta thường xuyên sử dụng GuaranteedBoard
thay vì ActivatableBoard
. GuaranteedBoard
yêu cầu xác định chính xác kiểu dữ liệu đầu vào InputType
được phép chấp nhận của Board. Nếu input được gửi vào không đúng với InputType
, debugger sẽ raise assertion crash. Trường hợp không quan tâm kiêu dữ liệu đầu vào, có thể đặt typealias InputType = Any
hoặc typealias InputType = Void
.
Xem đầy đủ các ví dụ mẫu tại đây
Xây dựng ứng dụng di động của bạn theo hướng Microsystems, có thể giúp đội của bạn tăng cường khả năng tích hợp độc lập và triển khai phân tán, từ đó nâng cao hiệu quả làm việc của từng squad và của toàn đội. Nó cũng đặc biệt phù hợp với môi trường Agile có yêu cầu thay đổi và phát hành sản phẩm diễn ra thường xuyên, liên tục.
Với việc thiết kế các micro-system đủ nhỏ và độc lập, kiến trúc Component của một micro-system vì thế trở nên linh hoạt, chỉ cần làm sao đủ rõ ràng, mạch lạc, dễ hiểu là được. Bạn có thể lựa chọn bất kỳ kiến trúc nào phù hợp với squad của mình và nhu cầu của sản phẩm. Tôi nghĩ Apple MVC là đủ, như thế tốc độ phát triển là tốt nhất.
Nếu bạn hứng thú với Boardy, chúng ta sẽ cùng nhau tìm hiểu thêm trong các phần tiếp theo.
Cảm ơn bạn đã đọc, chúc bạn có được một vài điều thú vị.