Tất tần tật về KIẾN TRÚC LỤC GIÁC trong phát triển ứng dụng

Trong bài viết hôm nay, chúng ta sẽ cùng bàn luận về kiến trúc phát triển ứng dụng khá là phổ biến có tên là Hexagonal Architecture (Kiến trúc lục giác). Vì gần đây khi mình bắt đầu học Golang thì cũng tình cờ biết thêm được về khái niệm này. Sau khi đã thử áp dụng kiến trúc này vào dự án, mình đánh giá đây là mẫu kiến trúc khá hay và đáng học hỏi. Vậy hãy cùng mình tìm hiểu xem "Kiến trúc lục giác" là gì nhé!

Ban đầu khi nhắc tới "kiến trúc lục giác" thì mình nghĩ ngay đến một cô phù thủy áo đỏ của vũ trụ Marvel. Nhưng mà đính chính lại là không phải nha 😁 (Cho những bạn nào không biết thì trong phim WandaVision được trình làng gần đây, Wanda tạo ra một thực tại tên là "Hexagon" hay "The Hex" lấy kiến trúc là hình lục giác). Tôi không nói sợ mấy ông fan Marvel lại nhầm lẫn! Anyway! Just for fun thôi nha 😀

Nếu bạn chưa xem WandaVision thì nên xem ngay kẻo lỡ

Kiến trúc lục giác là gì?

Kiến trúc lục giác (Hexagonal Architecture) hay còn gọi là kiến trúc cổng và bộ điều hợp (ports and adapters) là một mẩu kiến trúc được sử dụng trong thiết kế phần mềm. Mục đích sử dụng kiến trúc lục giác nhằm tạo ra kết nối lỏng lẻo (loose coupling) giữa các thành phần của hệ thống. Kiến trúc lục giác được đề xuất bởi Alistair Cockburn vào năm 2005.

Việc đặt lấy hình lục giác là biểu tượng của kiến trúc không có nghĩa là khi áp dụng kiến trúc này, chúng ta đều phải triển khai hết 6 và chỉ có 6 cổng. Hình lục giác chỉ mang ý nghĩa phác thảo vì giúp cho người triển khai dễ hình dung và dễ vẽ các interface, cổng cũng như thành phần trong kiến trúc.


Thành phần của kiến trúc lục giác

Khi triển khai kiến trúc lúc giác, có các thành phần chính mà các bạn cần quan tâm đến: vòng trong, vòng ngoài, cổng và các bộ điều hợp.

Trong đó, vòng trong (hay còn được gọi là phần lõi) được thiết kế theo kiến trúc phân tầng gồm 2 thành phần chính là lớp Application và lớp Domain. Phần lõi sẽ đóng vai trò điều hành các logic chính của ứng dụng. Đơn giản hơn, các bạn có thể hiểu lớp Domain trong phần lõi sẽ là thành phần chứa logic, trong khi đó lớp Application sẽ giao tiếp với lớp framework thông qua các API được khai báo.

Vòng ngoài chỉ gồm một tầng duy nhất là lớp Framework. Lớp này chứa các dependency bên ngoài như là Database, gRPC, REST API, CLI...

Về phần bộ điều hợp và cổng, có thể hiểu như sau: cổng là các interfacebộ điều hợp là các lớp triển khai các phương thức được khai báo bên trong cổng. Để các lớp có thể giao tiếp được với nhau, ví dụ ứng dụng cần truy vấn với cơ sở dữ liệu, chúng ta cần một bộ điều hợp tương ứng gọi là DatabaseAdapter. Ở phần sau của bài viết mình sẽ đưa ra một ví dụ cụ thể hơn về cách kiến trúc nhé.

Ngoài ra, để quản lý các bộ điều hợp một cách dễ dàng hơn thì ở vòng ngoài sẽ được chia ra làm 2 phần chính là Driving AdapterDriven Adapter.

  • Driving Adapter: Gồm các bộ điều hợp chủ động giúp điều hành ứng dụng. Ví dụ như gRPC, REST, SOAP, CLI...
  • Driven Adapter: Gồm các bộ điều hợp bị động được xử lý bởi ứng dụng. Ví dụ: Database, Message Queue...

Nguyên lý của kiến trúc lúc giác

Nguyên lý 1: Tách biệt User-Side, Business Logic và Server-Side

Nguồn: https://blog.octo.com/en/hexagonal-architecture-three-principles-and-an-implementation-example/

Theo như nguyên tắc thiết kế chia để trị (Separation of Concerns), mỗi thành phần của kiến trúc lục giác sẽ chỉ đảm nhiệm vai trò riêng của các phần ấy. User-side hay các Driving Adapters sẽ giữ vai trò cung cấp công cụ để người dùng tương tác với ứng dụng như là thông qua API. Các API này sẽ trò chuyện với phần Business Logic, nơi sẽ quản lý các logic chính của ứng dụng. Và Server-side hay Driven Adapters, chỉ có nhiệm vụ nằm chờ được sử dụng 😀.

Nguyên lý 2: Dependency vào Business Logic

Nguồn: https://blog.octo.com/en/hexagonal-architecture-three-principles-and-an-implementation-example/

Trong nguyên lý thứ 2 này, nếu bạn biết về Dependency Injection (DI) và Inversion of Control (IoC) thì sẽ khá là dễ hiểu. Các bộ điều hợp ở User-side và Server-side là tùy biến nhưng vẫn phụ thuộc vào Business Logic, tuy nhiên, Business Logic sẽ không có bất kì phụ thuộc nào với vòng ngoài.

Ví dụ như mình đang sử dụng một bộ điều hợp là PostgresDbAdapter nhưng lại muốn chuyển sang sử dụng một database khác là MongoDbAdapter, việc mình cần làm chỉ là gắm vào kiến trúc lục giác một Adapter mới dựa trên các Port đã được định sẵn. Business Logic sẽ không quan tâm điều gì xảy ra bên trong các Adapter mà bạn gắm vào, điều này khá giống với thuộc tính Encapsulation trong lập trình hướng đối tượng.

Nguyên lý 3: Ranh giới được cô lập với Interface

Nguồn: https://blog.octo.com/en/hexagonal-architecture-three-principles-and-an-implementation-example/

Từ đầu bài đến giờ, mình hay nhắc đến các từ như cổng và bộ điều hợp. Tuy nhiên, các từ ấy nhằm ẩn dụ hóa các khái niệm chuyên môn trong kiến trúc lục giác không hơn không kém.

Quan sát hình ở trên thì có thể đưa ra được nhận định rằng: User-side sẽ giao tiếp với Business Logic thông qua một Interface tên là IRequesVerses và Interface này sẽ được khai báo bên trong mã nguồn của lớp business. Trong khi đó, Business Logic sẽ giao tiếp với Server-side thông qua một Interface tên là IObtainPoems và Interface này cũng được khai báo bên trong mã nguồn của lớp business.

Nguồn: https://blog.octo.com/en/hexagonal-architecture-three-principles-and-an-implementation-example/

Các Interface ấy được ẩn dụ hóa là các Port được nhắc đến ở trên. Và lớp triển khai của các Interface sẽ là các Adapter. Đó cũng là nội dung của nguyên lý thứ 3 này.


Kết...

Và đó là những gì mình tìm hiểu về kiến trúc lục giác. Kiến trúc này còn được áp dụng bởi các công ty công nghệ lớn như Netflix. Trong bài viết này, mình chỉ đề cập đến các khái niệm căn bản của kiến trúc lục giác, ngoài ra còn có các mô hình cấp cao được triển khai dựa trên kiến trúc lục giác mà các bạn có thể tìm hiểu thêm. Trong tương lai gần, mình sẽ viết một bài để triển khai kiến trúc này, giúp các bạn có một cái nhìn bao quát hơn nhé.

Góc Của Chung

Góc Của Chung