Thiết kế định hướng microservices

Thiết kế định hướng microservices

Steve Jobs tin rằng thiết kế không chỉ là cách mô tả cách một vật gì đó về mặt hình dạng hoặc diễn tả về cảm thấy nó như thế nào mà còn là cách nó hoạt động. Cách một microservice hoạt động bên trong chính nó và tương tác với các microservice khác phụ thuộc nhiều vào thiết kế của nó. Hầu hết các khái niệm kiến trúc và nguyên tắc thiết kế được thảo luận về microservices không chỉ liên quan đến microservices. Nó đã tồn tại một thời gian, ngay cả trong những ngày đầu khi SOA phổ biến. Một số thậm chí còn gọi microservices là SOA được thực hiện hoàn hảo! Vấn đề cơ bản với SOA là mọi người không thiết kế đúng. Họ đã bị cuốn vào sự cường điệu và bỏ lại những nguyên tắc thiết kế quan trọng. Theo thời gian, SOA chỉ trở thành một từ thông dụng, trong khi nhu cầu ban đầu về nó vẫn chưa được giải quyết. Microservices như một khái niệm đã xuất hiện để lấp đầy khoảng trống này. Trừ khi chú ý đến các khái niệm kiến trúc và nguyên tắc thiết kế của microservices, nếu không, sẽ không thể làm microservices!

Sir Charles Antony Richard Hoare là nhà khoa học người Anh đã phát triển thuật toán sắp xếp nhanh (quick-sort). Trong bài phát biểu nhận giải thưởng Turing năm 1980, ông đã đề cập rằng có hai cách để thiết kế phần mềm: một cách là làm cho phần mềm đơn giản đến mức không có thiếu sót, và cách khác là làm cho nó phức tạp đến mức  không có thiếu sót rõ ràng — trong đó phương pháp đầu tiên khó hơn nhiều. Trong thiết kế microservices, cần phải quan tâm về kiến ​​trúc bên trong và bên ngoài của nó. Kiến trúc bên trong xác định cách tự thiết kế một microservice và kiến ​​trúc bên ngoài nói về cách nó giao tiếp với các microservice khác. Trừ khi làm cho cả hai thiết kế trở nên đơn giản và dễ phát triển, nếu không, bạn sẽ dễ mắc lỗi hệ thống và rời xa các mục tiêu thiết kế microservices chính. Cốt lõi của nó trong bất kỳ thiết kế microservices nào thì thời gian phát triển, khả năng mở rộng, cá nhân hóa service phức tạp và khả năng phục hồi là những yếu tố quan trọng. Trừ khi bạn làm cho thiết kế đơn giản, nếu không rất khó để đạt được những kỳ vọng này

Domain Driven Design

Domain Driven Design (DDD) không phải là một khái niệm mới được giới thiệu với microservice mà nó đã xuất hiện từ khá lâu. Trong quyển sách của Eric Evans có tựa đề Domain-Driven Design: Tackling Complexity in the Heart of Software, đã đặt ra thuật ngữ Domain Driven Design. Khi microservices trở thành một mô hình kiến trúc phổ biến, các nhà thiết kế bắt đầu nhận ra khả năng ứng dụng của các khái niệm Domain Driven Design trong việc thiết kế microservices. Thiết kế này đóng một vai trò quan trọng trong việc xác định phạm vi của một microservice

Domain Driven Design là gì? Nó là một khái niệm về mô hình hóa logic nghiệp vụ phức tạp, hoặc xây dựng một sự trừu tượng về logic nghiệp vụ phức tạp. Domain là trung tâm của Domain Driven Design. Tất cả phần mềm được phát triển đều liên quan đến một số hoạt động hoặc sở thích của người dùng. Eric Evans nói rằng lĩnh vực chủ đề mà người dùng áp dụng chương trình là Domain của phần mềm. Một số lĩnh vực liên quan đến nghiệp vụ phát sinh thực tế. Trong kinh doanh bán lẻ, chúng ta người mua, người bán, nhà cung cấp, đối tác và nhiều domain khác. Một số domain là "vô hình". Ví dụ: trong lĩnh vực tiền điện tử, ứng dụng ví Bitcoin xử lý tài sản là domain vô hình. Bất kể nó là gì, domain liên quan đến hoạt động doanh nghiệp, không phải phần mềm. Tất nhiên, phần mềm có thể là một domain khi bạn xây dựng phần mềm để hoạt động trong domain "phần mềm", ví dụ như một chương trình quản lý cấu hình cho tất các phần mềm khác

Trong suốt series này, lần lượt sẽ sử dụng nhiều ví dụ để giải thích rõ hơn về những khái niệm này. Giả sử có một doanh nghiệp bán lẻ đang xây dựng một ứng dụng thương mại điện tử. Nhà bán lẻ có bốn bộ phận chính: quản lý hàng tồn kho và đơn đặt hàng, quản lý khách hàng, giao hàng, thanh toán và tài chính. Mỗi bộ phận có thể có nhiều bộ phận. Bộ phận xử lý đơn hàng của bộ phận quản lý hàng tồn kho và đơn hàng, xác nhận đơn hàng, khóa các mặt hàng trong kho, sau đó chuyển quyền kiểm soát cho bộ phận xuất hóa đơn và tài chính để xử lý việc thanh toán. Sau khi thanh toán được xử lý thành công, bộ phận giao hàng sẽ chuẩn bị đơn hàng để giao. Bộ phận quản lý khách hàng nắm quyền quản lý tất cả dữ liệu cá nhân của khách hàng và tất cả các tương tác với khách hàng. Xem hình bên dưới

Một nguyên tắc quan trọng đằng sau Domain Driven Design là nguyên tắc chia để trị. Retail domain là domain cốt lõi trong ví dụ. Mỗi bộ phận có thể được coi như một domain con. Việc xác định nghiệp vụ kinh doanh cốt lõi và các nghiệp vụ liên quan là cực kỳ quan trọng. Điều này giúp xây dựng một ứng dụng thương mại điện tử cho nhà bán lẻ theo các nguyên tắc kiến trúc  microservices. Một trong những thách thức chính mà nhiều “kiến trúc sư” phải đối mặt trong việc xây dựng kiến trúc microservices là đưa ra mức độ chi tiết phù hợp cho từng service. Domain Driven Design sẽ giúp ích ở đây. Như tên của nó, theo thiết kế domain, domain là vua!

Bounded Context

Như chúng ta đã thảo luận, một trong những phần thách thức lớn nhất của thiết kế microservices là mở rộng phạm vi một microservice. Đây là nơi mà SOA và các triển khai của nó đã xác định phạm vi không tốt. Trong SOA, trong khi thiết kế, chúng ta xem xét toàn bộ doanh nghiệp. Hầu như việc thiết kết ít quan tâm về các lĩnh vực kinh doanh riêng lẻ, mà là toàn bộ doanh nghiệp. Ở SOA quản lý hàng tồn kho và đơn đặt hàng, thanh toán và tài chính, giao hàng và quản lý khách hàng không được tách thành các domain độc lập riêng biệt — mà thường xem xét ở góc độ một hệ thống hoàn chỉnh như một ứng dụng thương mại điện tử của doanh nghiệp

Hình dưới minh họa kiến trúc phân lớp của một ứng dụng thương mại điện tử theo kiến trúc SOA. Nếu bạn là một lâp trình viên theo hướng SOA, bạn sẽ trông khá quen thuộc. Những gì chúng ta có ở đây là một ứng dụng nguyên khối. Mặc dù lớp service cung cấp một số chức năng như các service riêng lẻ nhưng không có chức năng nào được tách rời khỏi nhau. Việc xác định phạm vi service không được thực hiện dựa trên lĩnh vực kinh doanh hay các vấn đề nghiệp vụ tương ứng. Ví dụ: service Order Processing cũng có thể giải quyết việc lập hóa đơn và giao hàng. Trong bài trước, chúng ta đã thảo luận về những khiếm khuyết của một kiến trúc nguyên khối như vậy.

Hãy mở rộng ví dụ trước với các bounded context. Ở đó, chúng ta đã xác định bốn lĩnh vực: quản lý hàng tồn kho và đơn đặt hàng, lập hóa đơn và tài chính, giao hàng và quản lý khách hàng. Mỗi microservice được thiết kế sẽ gắn liền với một trong các domain đó. Mặc dù ở đây là mối quan hệ 1-1 giữa một microservice với một domain, nhưng giờ đây ta biết rằng một domain có thể có nhiều bounded context, do đó có nhiều hơn một microservices. Ví dụ: nếu bạn sử dụng domain quản lý hàng tồn kho và đơn đặt hàng, chúng ta có microservice Xử lý đơn hàng, nhưng chúng ta cũng có thể có nhiều microservice khác, dựa trên các bounded context khác nhau (ví dụ: microservice invetory). Để làm được điều đó, chúng ta cần xem xét kỹ hơn các chức năng chính được cung cấp trong domain quản lý hàng tồn kho và đơn đặt hàng, đồng thời xác định các bounded context tương ứng

Bộ phận quản lý hàng tồn kho và đơn hàng của một doanh nghiệp đảm nhận việc quản lý kho hàng và đảm bảo đáp ứng nhu cầu của khách hàng về lượng hàng tồn kho hiện có. Họ cũng cần biết khi nào nên đặt hàng thêm từ các nhà cung cấp để tối ưu hóa việc bán hàng cũng như tối ưu về quản lý tồn kho và lưu trữ. Bất cứ khi nhận được một đơn đặt hàng mới, hệ thống phải cập nhật hàng tồn kho và khóa các mặt hàng tương ứng để giao hàng. Sau khi thanh toán được thực hiện và được xác nhận bởi bộ phận lập hóa đơn, bộ phận giao hàng phải xác định vị trí của mặt hàng trong kho của mình và chuẩn bị sẵn sàng để nhận và giao hàng. Đồng thời, bất cứ khi nào số lượng hiện có của một mặt hàng trong cửa hàng đạt đến một giá trị ngưỡng nào đó, bộ phận quản lý hàng tồn kho và đơn hàng nên liên hệ với nhà cung cấp để lấy thêm và khi đã nhận được, nên cập nhật số lượng tồn kho.

Một trong những điểm nổi bật chính của Domain Driven Design là sự hợp tác giữa các domain expert và developer. Trừ khi bạn có hiểu biết đúng đắn về cách hoạt động của bộ phận quản lý hàng tồn kho trong một doanh nghiệp, nếu không bạn sẽ không bao giờ xác định được các bounded context tương ứng. Với hiểu biết hạn chế của về quản lý hàng tồn kho, dựa trên những gì đã thảo luận trước đó, với domain tương ứng ta có thể xác định ba bounded context như sau:

  • Xử lý đơn đặt hàng: Bounded context này đóng gói chức năng liên quan đến xử lý đơn đặt hàng, bao gồm khóa các mặt hàng theo đơn đặt hàng trong kho, ghi lại đơn đặt hàng với khách hàng, v.v.
  • Tồn kho: Bản thân việc quản lý tồn kho có thể được coi là một bounded context. Điều này sẽ giúp cập nhật các kho hàng khi nhận được các mặt hàng từ nhà cung cấp và xuất kho để giao hàng.
  • Quản lý nhà cung cấp: Bounded context này bao hàm chức năng liên quan đến việc quản lý nhà cung cấp. Khi xuất một mặt hàng để giao hàng, ban quản lý nhà cung cấp sẽ kiểm tra xem nó có đủ hàng trong kho hay không, và nếu không, họ sẽ đặt hàng cho các nhà cung cấp tương ứng.

Hình dưới minh họa nhiều microservice trong domain quản lý đơn đặt hàng và tồn kho, đại diện cho từng bounded context. Ở đây, các ranh giới service được căn chỉnh theo các bounded context của domain tương ứng. Việc giao tiếp giữa các bounded context chỉ xảy ra thông qua message truyền tới các interface được xác định rõ. Như trong hình, trước tiên, microservice Xử lý Đơn hàng gửi thông tin đến microservice hàng tồn kho để khóa các mặt hàng trong đơn hàng và sau đó kích hoạt sự kiện ORDER_PROCESSING_COMPLETED. Service thanh toán lắng nghe sự kiện ORDER_PROCESSING_COMPLETED thực hiện xử lý thanh toán và sau đó kích hoạt sự kiện PAYMENT_PROCESSING_COMPLETED. Service Quản lý nhà cung cấp lắng nghe sự kiện PAYMENT_PROCESSING_COMPLETED sẽ kiểm tra xem số lượng mặt hàng trong kho có vượt quá ngưỡng tối thiểu hay không và nếu không sẽ thông báo cho nhà cung cấp. Service Giao hàng lắng nghe cùng một sự kiện sẽ thực hiện các hoạt động của nó để tìm kiếm các mặt hàng (có thể là gửi hướng dẫn đến các bot nhà kho) và sau đó nhóm các mặt hàng lại với nhau để đóng gói đơn hàng và sẵn sàng giao hàng. Sau khi hoàn tất, service Phân phối sẽ kích hoạt sự kiện ORDER_DISPATCHED, sự kiện này sẽ thông báo cho service Xử lý đơn hàng và sẽ cập nhật trạng thái đơn đặt hàng.

Một thiết kế tốt sẽ mở rộng một microservice tới một bounded context duy nhất. Bất kỳ microservice nào mở rộng trên nhiều bounded context đều đi chệch mục tiêu ban đầu. Khi chúng ta có một microservice, việc đóng gói logic nghiệp vụ đằng sau một interface được xác định rõ ràng và đại diện cho một bounded context, điều này giúp cho việc các yêu cầu thay đổi mới sẽ không có hoặc ít tác động đến toàn bộ hệ thống.

Như đã thảo luận, giao tiếp giữa các microservices có thể xảy ra thông qua các sự kiện. Theo thiết kế Domain Driven Design, những sự kiện này được gọi là domain event. Các domain event được kích hoạt do sự thay đổi trạng thái trong các bounded context. Sau đó, bounded context khác có thể phản hồi các sự kiện này theo cách kết hợp. Các sự kiện có bounded context kích hoạt sự kiện không cần phải lo lắng về các hành vi sẽ diễn ra do kết quả của các sự kiện đó và đồng thời các bounded context, nơi xử lý các sự kiện đó, không cần phải lo lắng về nguồn gốc của các sự kiện. Domain event có thể được sử dụng giữa các bounded context trong một domain hoặc giữa các domain.

Context Map

Bounded context giúp đóng gói logic nghiệp vụ trong một ranh giới service và giúp xác định service interface. Khi số lượng bouded context trong một doanh nghiệp ngày càng tăng, việc tìm ra kết nối giữa các service với nhau có thể trở thành một cơn ác mộng. Context map giúp hình dung mối quan hệ giữa các bounded context. Luật Conway đã thảo luận trước đó là một lý do khác tại sao nên xây dựng context map. Theo Luật Conway, bất kỳ tổ chức nào thiết kế hệ thống sẽ tạo ra một thiết kế có cấu trúc là bản sao cấu trúc truyền thống của tổ chức. Nói cách khác, chúng ta sẽ có các nhóm khác nhau làm việc trên các bounded context khác nhau. Điều này có một trở ngại là giao tiếp trong một nhóm sẽ rất tốt, nhưng giữa các nhóm thường không tốt. Khi thiếu thông tin liên lạc giữa các nhóm, các quyết định thiết kế được đưa ra trên các bounded context tương ứng sẽ không được thông báo cho các bên khác một cách chính xác. Có một context map giúp mỗi nhóm theo dõi những thay đổi xảy ra trên các bounded context mà họ phụ thuộc

Vaughn Vernon, trong cuốn sách Implementing Domain-Driven Design, trình bày nhiều cách tiếp cận để thể hiện context map. Cách dễ dàng nhất là đưa ra một sơ đồ để hiển thị ánh xạ giữa hai hoặc nhiều bounded context hiện có, như trong hình dưới. Ngoài ra, hãy nhớ rằng mỗi bounded context trong hình là một microservice tương ứng. Một đường thẳng giữa hai bounded context với hai ký tự đánh dấu ở mỗi đầu, hoặc U hoặc D, để hiển thị mối quan hệ giữa các bounded context tương ứng. U là upstream, trong khi D là downstream

Trong mối quan hệ giữa bounded context Xử lý đơn đặt hàng và bounded context Thanh toán, bounded context Xử lý đơn hàng là context upstream, trong khi Thanh toán là bounded downstream. Bounded context upstream có nhiều quyền kiểm soát hơn bounded context dowstream. Nói cách khác, bounded context upstream xác định mô hình domain được truyền giữa hai context. Bounded context downstream cần được biết rõ về bất kỳ thay đổi nào xảy ra đối với bounded context upstream. Hình trên cho thấy cách các thông điệp được chuyển một cách chính xác giữa hai bounded context. Không có kết nối trực tiếp. Giao tiếp giữa bounded context Xử lý đơn hàng và bounded context thanh toán xảy ra thông qua sự kiện. Upstream bounded context hoặc bounded context Xử lý đơn đặt hàng xác định cấu trúc của sự kiện và bất kỳ bounded context downstream nào quan tâm đến sự kiện đó đều phải tuân thủ theo

Mối quan hệ giữa bounded context Thanh toán và bounded context Quản lý nhà cung cấp cũng giống như mối quan hệ giữa bounded context Xử lý đơn hàng và bounded context thanh toán. Ở đó, Thanh toán là bounded context upstream trong khi Quản lý nhà cung cấp là bounded context downstream. Giao tiếp giữa hai bounded context này xảy ra thông qua event, như trong hình trên. Giao tiếp giữa bounded context Xử lý đơn hàng và bounded context tồn kho là đồng bộ. Tồn kho là bounded context upstream trong khi Xử lý đơn hàng là bounded context downstream. Nói cách khác, "cấu trúc nội dung giao tiếp" giữa bounded context Xử lý đơn hàng và bounded context quản lý tồn kho được xác định bởi bounded context tồn kho.

Hãy ngừng lại và nghiên cứu sâu hơn một chút về bounded context tồn kho và Xử lý đơn đặt hàng. bounded context có mô hình domain riêng của nó, được xác định là kết quả của một quá trình làm việc dài do các domain expert và developer thực hiện. Nhớ lại rằng cùng một đối tượng domain có thể nằm trong các bounded context khác nhau với các định nghĩa khác nhau. Ví dụ: đơn hàng trong bounded context Xử lý đơn hàng có các thuộc tính như id đơn hàng, id khách hàng, mục hàng, địa chỉ giao hàng và tùy chọn thanh toán, trong khi thực thể đơn hàng trong bounded context tồn kho có các thuộc tính như id đơn hàng và mục hàng . Mặc dù tham chiếu đến khách hàng, địa chỉ giao hàng và tùy chọn thanh toán được yêu cầu bởi service Xử lý đơn đặt hàng để duy trì lịch sử của tất cả các đơn đặt hàng đối với khách hàng, không có đơn đặt hàng nào trong số đó là cần thiết đối với bounded context tồn kho. Mỗi bounded context phải biết cách quản lý các tình huống như vậy, để tránh bất kỳ xung đột nào trong các domain của chúng. Trong phần sau, chúng ta thảo luận về một số design pattern (mẫu thiết kế) cần tuân theo để duy trì mối quan hệ giữa nhiều ngữ cảnh bị ràng buộc.

Tóm tắt

Trong phần này chúng ta đã tìm hiểu các khái niệm ban đầu trong thiết kế định hướng microservice. Chúc bạn đọc vui vẻ


Bài viết thuộc các danh mục

Bài viết được gắn thẻ



BÌNH LUẬN (0)

Hãy là người đầu tiên để lại bình luận cho bài viết !!

Hãy đăng nhập để tham gia bình luận. Nếu bạn chưa có tài khoản hãy đăng ký để tham gia bình luận với mình


Bài viết liên quan

Các tình huống của kiến trúc microservice

Kiến trúc phần mềm doanh nghiệp luôn phát triển với các phong cách kiến trúc mới do sự thay đổi mô hình trong bối cảnh của một thời đại công nghệ và với mong muốn tìm ra những cách tốt hơn để xây dựng các ứng dụng một cách nhanh chóng với độ ổn định cao và đáng tin cậy

Copyright © 2022. Bảo lưu tất cả quyền