Trang chủ » Triển khai GraphQL server với các thành phần trong PHP

Triển khai GraphQL server với các thành phần trong PHP

by KDATA

Một GraphQL server có thể không cần phải sử dụng đồ thị để đại diện cho dữ liệu. Thay vào đó, bằng cách chuyển đổi truy vấn GraphQL thành một cấu trúc phân cấp các thành phần và xử lý dữ liệu theo từng kiểu riêng biệt, server này vẫn có thể trả về phản hồi chính xác và hiệu quả.

Triển khai GraphQL server với các thành phần trong PHP

Trong bài viết này, chúng ta sẽ khám phá cách một GraphQL server có thể giải quyết các truy vấn một cách hiệu quả thông qua kiến trúc của PoP, giúp giảm độ phức tạp trong việc tải và trả về dữ liệu.

1. Thành phần là gì?

Cấu trúc của mỗi trang web có thể được biểu diễn bằng các thành phần. Một thành phần đơn giản là một tập hợp các mảnh mã (như HTML, JavaScript và CSS) được gộp lại để tạo thành một thực thể độc lập, có thể bao bọc các thành phần khác để tạo thành các cấu trúc phức tạp hơn, và chính nó cũng có thể được bao bọc bởi các thành phần khác.

Sale cuối năm

Mỗi thành phần có một mục đích, có thể là một thứ rất cơ bản, như một liên kết hoặc một nút bấm, cho đến một thứ rất tinh vi, như một bộ chuyển hướng (carousel) hoặc một trình tải ảnh kéo và thả.

Xây dựng một trang web thông qua các thành phần giống như chơi với LEGO. Ví dụ, trong trang web từ hình ảnh dưới đây, các thành phần đơn giản (liên kết, nút bấm, ảnh đại diện) được kết hợp để tạo ra các cấu trúc phức tạp hơn (tiện ích, phần, thanh bên, menu), cho đến khi ta có được trang web:

Thành phần là gì?

Trang web là một thành phần bao bọc các thành phần bao bọc các thành phần, như thể hiện qua các ô vuông.

Các thành phần có thể được triển khai cho cả phía client (như các thư viện JS Vue và React, hoặc các thư viện thành phần CSS như Bootstrap và Material-UI) và cho cả phía server, trong bất kỳ ngôn ngữ nào.

2. PoP hoạt động như thế nào?

PoP mô tả một kiến trúc dựa trên mô hình thành phần phía server, và triển khai nó trong PHP thông qua thư viện component-model.

Trong các phần dưới đây, các thuật ngữ “thành phần” và “module” được sử dụng thay thế cho nhau.

Cấu trúc thành phần

Mối quan hệ của tất cả các module bao bọc lẫn nhau, từ module cấp cao nhất cho đến cấp thấp nhất, được gọi là cấu trúc thành phần. Mối quan hệ này có thể được biểu diễn thông qua một mảng liên kết (mảng với key => giá trị) ở phía server, trong đó mỗi module xác định tên của nó dưới thuộc tính key và các module con của nó dưới thuộc tính “modules”.

Dữ liệu trong mảng PHP có thể được sử dụng trực tiếp ở phía client, được mã hóa dưới dạng đối tượng JSON.

Cấu trúc thành phần trông như sau:

$componentHierarchy = [
'module-level0' => [
"modules" => [
'module-level1' => [
"modules" => [
'module-level11' => [
"modules" => [...],
],
'module-level12' => [
"modules" => [
'module-level121' => [
"modules" => [...],
],
],
],
],
],
'module-level2' => [
"modules" => [
'module-level21' => [
"modules" => [...],
],
],
],
],
],
];

Mối quan hệ giữa các module được xác định theo cách từ trên xuống: một module bao bọc các module khác và biết chúng là ai, nhưng nó không biết, và không quan tâm, ai bao bọc nó.

Ví dụ, trong cấu trúc thành phần trên, module ‘module-level1’ biết rằng nó bao bọc các module ‘module-level11’ và ‘module-level12’, và do đó, nó cũng biết rằng nó bao bọc ‘module-level121’; nhưng module ‘module-level11’ không quan tâm ai bao bọc nó, vì vậy nó không biết về ‘module-level1’.

Khi có cấu trúc thành phần, chúng ta thêm vào thông tin thực tế yêu cầu cho mỗi module, được phân loại thành cài đặt (như giá trị cấu hình và các thuộc tính khác) và dữ liệu (như ID của các đối tượng cơ sở dữ liệu được truy vấn và các thuộc tính khác), và được đặt dưới các mục “modulesettings” và “moduledata” tương ứng.

Dữ liệu của cấu trúc thành phần

Dữ liệu của đối tượng cơ sở dữ liệu sau đó được thêm vào cấu trúc thành phần. Thông tin này không được đặt dưới mỗi module, mà dưới một phần chung gọi là “databases”, để tránh trùng lặp thông tin khi có hai hoặc nhiều module khác nhau truy vấn các đối tượng giống nhau từ cơ sở dữ liệu.

Thêm vào đó, thư viện biểu diễn dữ liệu đối tượng cơ sở dữ liệu theo cách quan hệ, để tránh trùng lặp thông tin khi hai hoặc nhiều đối tượng cơ sở dữ liệu khác nhau có liên quan đến một đối tượng chung (chẳng hạn như hai bài viết có cùng tác giả).

Nói cách khác, dữ liệu đối tượng cơ sở dữ liệu được chuẩn hóa. Cấu trúc này là một từ điển, tổ chức theo loại đối tượng đầu tiên và ID đối tượng thứ hai, từ đó chúng ta có thể lấy các thuộc tính của đối tượng:

$componentHierarchyData = [
...
"databases" => [
"dbobject_type" => [
"dbobject_id" => [
"property" => ...,
...
],
...
],
...
]
];

Ví dụ, đối tượng dưới đây chứa một cấu trúc thành phần với hai module, “page” => “post-feed”, trong đó module “post-feed” truy vấn các bài đăng blog. Xin lưu ý các điểm sau:

  • Mỗi module biết đối tượng mà nó truy vấn từ thuộc tính “dbobjectids” (ID 4 và 9 cho các bài viết blog).
  • Mỗi module biết loại đối tượng cho các đối tượng mà nó truy vấn từ thuộc tính “dbkeys” (dữ liệu của mỗi bài viết được tìm thấy dưới “posts”, và dữ liệu tác giả bài viết, tương ứng với tác giả có ID được đưa ra dưới thuộc tính “author” của bài viết, được tìm thấy dưới “users”).
  • Vì dữ liệu đối tượng cơ sở dữ liệu có quan hệ, thuộc tính “author” chứa ID của đối tượng tác giả thay vì in trực tiếp dữ liệu tác giả.
$componentHierarchyData = [
"moduledata" => [
'page' => [
"modules" => [
'post-feed' => [
"dbobjectids" => [4, 9]
]
]
]
],
"modulesettings" => [
'page' => [
"modules" => [
'post-feed' => [
"dbkeys" => [
'id' => "posts",
'author' => "users"
]
]
]
]
],
"databases" => [
'posts' => [
4 => [
'title' => "Hello World!",
'author' => 7
],
9 => [
'title' => "Everything fine?",
'author' => 7
]
],
'users' => [
7 => [
'name' => "Leo"
]
]
]
];

Tải dữ liệu

Khi một module hiển thị một thuộc tính từ một đối tượng cơ sở dữ liệu, module đó có thể không biết, hoặc không quan tâm, đối tượng đó là gì; tất cả những gì nó quan tâm là định nghĩa những thuộc tính nào từ đối tượng tải về là cần thiết.

Ví dụ, hãy tưởng tượng dưới đây: một module tải một đối tượng từ cơ sở dữ liệu (trong trường hợp này, là một bài viết duy nhất), và sau đó các module con của nó sẽ hiển thị các thuộc tính nhất định từ đối tượng đó, như “title” và “content”:

Tải dữ liệu

Trong khi một số module tải đối tượng cơ sở dữ liệu, các module khác sẽ tải các thuộc tính.

Do đó, trong suốt cấu trúc thành phần, các module “dataloading” sẽ chịu trách nhiệm tải các đối tượng được yêu cầu (module tải bài viết duy nhất trong trường hợp này), và các module con của nó sẽ định nghĩa những thuộc tính nào từ đối tượng cơ sở dữ liệu là cần thiết (“title” và “content” trong trường hợp này).

Việc lấy tất cả các thuộc tính cần thiết cho đối tượng cơ sở dữ liệu có thể được thực hiện bằng cách duyệt qua cấu trúc thành phần: bắt đầu từ module tải dữ liệu, PoP sẽ lặp qua tất cả các module con cho đến khi gặp một module tải dữ liệu mới, hoặc cho đến khi hết cây; tại mỗi cấp, nó thu thập tất cả các thuộc tính yêu cầu, và sau đó kết hợp tất cả các thuộc tính lại và truy vấn chúng từ cơ sở dữ liệu, tất cả chỉ một lần.

Vì dữ liệu đối tượng cơ sở dữ liệu được truy vấn theo quan hệ, ta cũng có thể áp dụng chiến lược này giữa các mối quan hệ giữa các đối tượng cơ sở dữ liệu với nhau.

Hãy tưởng tượng hình ảnh dưới đây: Bắt đầu từ loại đối tượng “post”, và di chuyển xuống cấu trúc thành phần, chúng ta sẽ cần chuyển đối tượng cơ sở dữ liệu thành “user” và “comment”, tương ứng với tác giả của bài viết và các bình luận của bài viết đó, và sau đó, với mỗi bình luận, ta phải chuyển đối tượng thành “user” đại diện cho tác giả của mỗi bình luận.

Sau khi chuyển sang một miền mới, từ cấp đó trở đi, tất cả các thuộc tính yêu cầu sẽ phải thuộc về miền mới: Thuộc tính “name” được lấy từ đối tượng “user” đại diện cho tác giả bài viết, “content” từ đối tượng “comment” đại diện cho các bình luận của bài viết, và sau đó “name” từ đối tượng “user” đại diện cho tác giả của mỗi bình luận.

Tải dữ liệu

Chuyển đổi giữa các miền cơ sở dữ liệu

Duyệt qua cấu trúc thành phần, PoP biết khi nào chuyển miền và sẽ truy vấn dữ liệu đối tượng theo quan hệ một cách hợp lý.

3. Cách các thành phần được định nghĩa trong PoP

Các thuộc tính của module (giá trị cấu hình, dữ liệu cơ sở dữ liệu cần lấy, v.v.) và các module con được định nghĩa thông qua các đối tượng ModuleProcessor, từng module một, và PoP tạo ra cấu trúc phân cấp của các thành phần từ tất cả các ModuleProcessor xử lý tất cả các module liên quan.

Tương tự như một ứng dụng React (nơi chúng ta phải chỉ ra component nào được render trên <div id="root"></div>), mô hình thành phần trong PoP phải có một module nhập.

Bắt đầu từ module này, PoP sẽ duyệt qua tất cả các module trong cấu trúc phân cấp của thành phần, lấy các thuộc tính cho mỗi module từ ModuleProcessor tương ứng và tạo ra một mảng kết hợp lồng nhau với tất cả các thuộc tính cho tất cả các module.

Khi một thành phần định nghĩa một thành phần con, nó tham chiếu đến nó thông qua một mảng gồm 2 phần:

  1. Lớp PHP
  2. Tên thành phần

Điều này là cần thiết vì các thành phần thường chia sẻ các thuộc tính. Ví dụ, các thành phần POST_THUMBNAIL_LARGEPOST_THUMBNAIL_SMALL sẽ chia sẻ hầu hết các thuộc tính, ngoại trừ kích thước của thumbnail. Do đó, việc nhóm tất cả các thành phần tương tự dưới một lớp PHP và sử dụng các câu lệnh switch để xác định module yêu cầu và trả về thuộc tính tương ứng là hợp lý.

Một ModuleProcessor cho các thành phần widget bài viết để đặt trên các trang khác nhau trông như thế này:

class PostWidgetModuleProcessor extends AbstractModuleProcessor {

const POST_WIDGET_HOMEPAGE = 'post-widget-homepage';
const POST_WIDGET_AUTHORPAGE = 'post-widget-authorpage';

function getSubmodulesToProcess() {
return [
self::POST_WIDGET_HOMEPAGE,
self::POST_WIDGET_AUTHORPAGE,
];
}

function getSubmodules($module): array
{
$ret = [];

switch ($module[1]) { 
case self::POST_WIDGET_HOMEPAGE:
case self::POST_WIDGET_AUTHORPAGE:
$ret[] = [
UserLayoutModuleProcessor::class,
UserLayoutModuleProcessor::POST_THUMB
];
$ret[] = [
UserLayoutModuleProcessor::class,
UserLayoutModuleProcessor::POST_TITLE
];
break;
}
switch ($module[1]) { 
case self::POST_WIDGET_HOMEPAGE:
$ret[] = [
UserLayoutModuleProcessor::class,
UserLayoutModuleProcessor::POST_DATE
];
break;
}

return $ret;
}

function getImmutableConfiguration($module, &$props)
{
$ret = [];

switch ($module[1]) {
case self::POST_WIDGET_HOMEPAGE: 
$ret['description'] = __('Latest posts', 'my-domain');
$ret['showmore'] = $this->getProp($module, $props, 'showmore');
$ret['class'] = $this->getProp($module, $props, 'class');
break;

case self::POST_WIDGET_AUTHORPAGE: 
$ret['description'] = __('Latest posts by the author', 'my-domain');
$ret['showmore'] = false;
$ret['class'] = 'text-center';
break;
}

return $ret;
}

function initModelProps($module, &$props)
{
switch ($module[1]) {
case self::POST_WIDGET_HOMEPAGE:
$this->setProp($module, $props, 'showmore', false);
$this->appendProp($module, $props, 'class', 'text-center');
break;
}

parent::initModelProps($module, $props);
}
// ...
}

Việc tạo ra các thành phần tái sử dụng được thực hiện bằng cách xây dựng các lớp ModuleProcessor trừu tượng định nghĩa các hàm placeholder mà các lớp kế thừa phải thực hiện:

abstract class PostWidgetLayoutAbstractModuleProcessor extends AbstractModuleProcessor
{
function getSubmodules($module): array
{ 
$ret = [
$this->getContentModule($module),
];

if ($thumbnail_module = $this->getThumbnailModule($module))
{
$ret[] = $thumbnail_module;
}

if ($aftercontent_modules = $this->getAfterContentModules($module))
{
$ret = array_merge(
$ret,
$aftercontent_modules
);
}

return $ret;
}

abstract protected function getContentModule($module): array;

protected function getThumbnailModule($module): ?array
{
// Giá trị mặc định (có thể ghi đè)
return [self::class, self::THUMBNAIL_LAYOUT];
}

protected function getAfterContentModules($module): array
{
return [];
}

function getImmutableConfiguration($module, &$props): array
{
return [
'description' => $this->getDescription(),
];
}

protected function getDescription($module): string
{
return '';
}
}

Các lớp ModuleProcessor tùy chỉnh sau đó có thể kế thừa lớp trừu tượng này và định nghĩa các thuộc tính của riêng chúng:

class PostLayoutModuleProcessor extends AbstractPostLayoutModuleProcessor {

const POST_CONTENT = 'post-content'
const POST_EXCERPT = 'post-excerpt'
const POST_THUMBNAIL_LARGE = 'post-thumbnail-large'
const POST_THUMBNAIL_MEDIUM = 'post-thumbnail-medium'
const POST_SHARE = 'post-share'

function getSubmodulesToProcess() {
return [
self::POST_CONTENT,
self::POST_EXCERPT,
self::POST_THUMBNAIL_LARGE,
self::POST_THUMBNAIL_MEDIUM,
self::POST_SHARE,
];
}

}

class PostWidgetLayoutModuleProcessor extends AbstractPostWidgetLayoutModuleProcessor
{
protected function getContentModule($module): ?array
{
switch ($module[1])
{
case self::POST_WIDGET_HOMEPAGE_LARGE:
return [
PostLayoutModuleProcessor::class,
PostLayoutModuleProcessor::POST_CONTENT
];

case self::POST_WIDGET_HOMEPAGE_MEDIUM:
case self::POST_WIDGET_HOMEPAGE_SMALL:
return [
PostLayoutModuleProcessor::class,
PostLayoutModuleProcessor::POST_EXCERPT
];
}

return parent::getContentModule($module);
}

protected function getThumbnailModule($module): ?array
{
switch ($module[1])
{
case self::POST_WIDGET_HOMEPAGE_LARGE:
return [
PostLayoutModuleProcessor::class,
PostLayoutModuleProcessor::POST_THUMBNAIL_LARGE
];

case self::POST_WIDGET_HOMEPAGE_MEDIUM:
return [
PostLayoutModuleProcessor::class,
PostLayoutModuleProcessor::POST_THUMBNAIL_MEDIUM
];
}

return parent::getThumbnailModule($module);
}

protected function getAfterContentModules($module): array
{
$ret = [];

switch ($module[1])
{
case self::POST_WIDGET_HOMEPAGE_LARGE:
$ret[] = [
PostLayoutModuleProcessor::class,
PostLayoutModuleProcessor::POST_SHARE
];
break;
}

return $ret;
}

protected function getDescription($module): string
{
return __('These are my blog posts', 'my-domain');
}
}

4. Cách các thành phần phù hợp tự nhiên với GraphQL Server

Mô hình thành phần có thể ánh xạ trực tiếp một truy vấn GraphQL theo dạng cây, khiến nó trở thành một kiến trúc lý tưởng để triển khai GraphQL server.

GraphQL by PoP đã triển khai các lớp ModuleProcessor cần thiết để chuyển đổi một truy vấn GraphQL thành cấu trúc phân cấp của các thành phần tương ứng và giải quyết nó thông qua công cụ dataloading của PoP.

Đây là lý do tại sao và cách giải pháp này hoạt động.

Ánh xạ các thành phần phía client sang các truy vấn GraphQL Server

Truy vấn GraphQL có thể được đại diện bằng cấu trúc phân cấp của PoP, trong đó mỗi kiểu đối tượng đại diện cho một thành phần và mỗi trường quan hệ từ kiểu đối tượng này tới kiểu đối tượng khác đại diện cho một thành phần bao bọc một thành phần khác.

Hãy xem xét ví dụ sau. Giả sử chúng ta muốn xây dựng một widget “Đạo diễn nổi bật”:

Ánh xạ các thành phần phía client sang các truy vấn GraphQL

Sử dụng Vue hoặc React (hoặc bất kỳ thư viện component-based nào khác), chúng ta sẽ đầu tiên xác định các thành phần. Trong trường hợp này, chúng ta sẽ có một thành phần ngoài <FeaturedDirector> (màu đỏ), bao bọc một thành phần <Film> (màu xanh dương), mà chính nó bao bọc một thành phần <Actor> (màu xanh lá):

Ánh xạ các thành phần phía client sang các truy vấn GraphQL

Xác định các thuộc tính dữ liệu cho mỗi thành phần

Và chúng ta xây dựng truy vấn GraphQL để lấy dữ liệu yêu cầu:

query {
featuredDirector {
name
country
avatar
films {
title
thumbnail
actors {
name
avatar
}
}
}
}

Như có thể nhận thấy, có một mối quan hệ trực tiếp giữa hình dạng của cấu trúc phân cấp thành phần và một truy vấn GraphQL. Thực tế, một truy vấn GraphQL có thể được coi là đại diện của một cấu trúc phân cấp thành phần.

Giải quyết truy vấn GraphQL sử dụng các thành phần phía server

Vì một truy vấn GraphQL có hình dạng giống như một cấu trúc phân cấp của các thành phần, PoP chuyển đổi truy vấn thành cấu trúc phân cấp thành phần tương đương, giải quyết truy vấn này bằng cách sử dụng phương pháp của mình để lấy dữ liệu cho các thành phần, và cuối cùng tái tạo hình dạng của truy vấn để gửi dữ liệu trong phản hồi.

Hãy xem cách điều này hoạt động.

Để xử lý dữ liệu, PoP chuyển đổi các kiểu GraphQL thành các thành phần: <FeaturedDirector> => Director, <Film> => Film, <Actor> => Actor, và bằng cách sử dụng thứ tự mà chúng xuất hiện trong truy vấn, PoP tạo ra một cấu trúc phân cấp thành phần ảo với các phần tử tương tự: thành phần gốc Director, bao bọc thành phần Film, bao bọc thành phần Actor.

Từ giờ, việc nói về các kiểu GraphQL hay các thành phần của PoP sẽ không còn sự khác biệt.

Để tải dữ liệu của chúng, PoP xử lý chúng theo “các vòng lặp”, lấy dữ liệu đối tượng cho mỗi kiểu trong mỗi vòng lặp riêng biệt như sau:

Giải quyết truy vấn GraphQL sử dụng các thành phần phía server

Công cụ tải dữ liệu của PoP thực hiện thuật toán giả sau để tải dữ liệu:

Chuẩn bị:

Có một hàng đợi trống để lưu trữ danh sách các ID của các đối tượng cần lấy từ cơ sở dữ liệu, được tổ chức theo kiểu (mỗi mục sẽ là: [kiểu => danh sách ID]).

Lấy ID của đối tượng đạo diễn nổi bật, và đặt nó vào hàng đợi dưới kiểu Director.

Lặp lại cho đến khi không còn mục nào trong hàng đợi:

  • Lấy mục đầu tiên từ hàng đợi: kiểu và danh sách ID (ví dụ: Director và [2]), và loại bỏ mục này khỏi hàng đợi.
  • Thực hiện một truy vấn duy nhất với cơ sở dữ liệu để lấy tất cả các đối tượng cho kiểu đó với những ID đó.
  • Nếu kiểu có các trường quan hệ (ví dụ: kiểu Director có trường quan hệ films kiểu Film), thì thu thập tất cả các ID từ các trường này từ tất cả các đối tượng đã được lấy trong vòng lặp hiện tại (ví dụ: tất cả các ID trong trường films từ tất cả các đối tượng kiểu Director), và đưa các ID này vào hàng đợi dưới kiểu tương ứng (ví dụ: các ID [3, 8] dưới kiểu Film).

Cuối cùng, sau khi các vòng lặp hoàn tất, chúng ta sẽ có tất cả dữ liệu đối tượng cho tất cả các kiểu, như sau:

Giải quyết truy vấn GraphQL sử dụng các thành phần phía server

Xin lưu ý cách tất cả các ID cho một kiểu được thu thập, cho đến khi kiểu đó được xử lý trong hàng đợi. Nếu, ví dụ, chúng ta thêm một trường quan hệ preferredActors vào kiểu Director, thì các ID này sẽ được thêm vào hàng đợi dưới kiểu Actor, và chúng sẽ được xử lý cùng với các ID từ trường actors của kiểu Film:

Giải quyết truy vấn GraphQL sử dụng các thành phần phía server

Tuy nhiên, nếu một kiểu đã được xử lý và sau đó chúng ta cần tải thêm dữ liệu từ kiểu đó, thì đó là một vòng lặp mới cho kiểu đó. Ví dụ, việc thêm một trường quan hệ preferredDirector vào kiểu Author sẽ khiến kiểu Director được thêm vào hàng đợi một lần nữa:

Giải quyết truy vấn GraphQL sử dụng các thành phần phía server

Hãy chú ý rằng chúng ta có thể sử dụng cơ chế caching: vào vòng lặp thứ hai cho kiểu Director, đối tượng có ID 2 sẽ không bị truy vấn lại, vì nó đã được lấy trong vòng lặp đầu tiên, do đó có thể lấy từ bộ nhớ đệm.

Bây giờ chúng ta đã tải hết tất cả dữ liệu đối tượng, chúng ta cần tạo lại dữ liệu thành phản hồi như mong đợi, mô phỏng truy vấn GraphQL. Dữ liệu hiện tại đã được tổ chức như trong một cơ sở dữ liệu quan hệ:

Bảng cho kiểu Director:

IDtênquốc giaavatarfilms
2George LucasUSAgeorge-lucas.jpg[3, 8]

Bảng cho kiểu Film:

IDtiêu đềthumbnailactors
3The Phantom Menaceepisode-1.jpg[4, 6]
8Attack of the Clonesepisode-2.jpg[6, 7]

Bảng cho kiểu Actor:

IDtênavatar
4Ewan McGregormcgregor.jpg
6Nathalie Portmanportman.jpg
7Hayden Christensenchristensen.jpg

Tại thời điểm này, PoP đã có tất cả dữ liệu được tổ chức dưới dạng các bảng, và biết cách mỗi kiểu liên kết với nhau (tức là Director tham chiếu Film qua trường films, Film tham chiếu Actor qua trường actors).

Sau đó, bằng cách lặp qua cấu trúc phân cấp của các thành phần từ gốc, điều hướng các mối quan hệ và lấy các đối tượng tương ứng từ các bảng quan hệ, PoP sẽ tạo ra cấu trúc cây từ truy vấn GraphQL:

Triển khai GraphQL server với các thành phần trong PHP

Phản hồi dạng cây

Cuối cùng, khi in dữ liệu vào đầu ra, PoP sẽ tạo ra phản hồi với hình dạng giống như truy vấn GraphQL:

{
"data": {
"featuredDirector": {
"name": "George Lucas",
"country": "USA",
"avatar": "george-lucas.jpg",
"films": [
{
"title": "Star Wars: Episode I",
"thumbnail": "episode-1.jpg",
"actors": [
{
"name": "Ewan McGregor",
"avatar": "mcgregor.jpg"
},
{
"name": "Natalie Portman",
"avatar": "portman.jpg"
}
]
},
{
"title": "Star Wars: Episode II",
"thumbnail": "episode-2.jpg",
"actors": [
{
"name": "Natalie Portman",
"avatar": "portman.jpg"
},
{
"name": "Hayden Christensen",
"avatar": "christensen.jpg"
}
]
}
]
}
}
}

5. Phân tích hiệu suất khi sử dụng các thành phần để giải quyết truy vấn GraphQL Server

Hãy phân tích ký hiệu O lớn của thuật toán tải dữ liệu để hiểu cách số lượng truy vấn thực hiện với cơ sở dữ liệu sẽ tăng lên như thế nào khi số lượng đầu vào tăng, nhằm đảm bảo rằng giải pháp này có hiệu suất cao.

Công cụ tải dữ liệu của PoP tải dữ liệu theo các vòng lặp tương ứng với mỗi kiểu. Khi bắt đầu một vòng lặp, nó sẽ đã có danh sách tất cả các ID của tất cả các đối tượng cần tải, vì vậy nó có thể thực hiện một truy vấn duy nhất để lấy tất cả dữ liệu cho các đối tượng tương ứng.

Vì vậy, số lượng truy vấn đến cơ sở dữ liệu sẽ tăng theo cấp số nhân với số lượng kiểu tham gia trong truy vấn. Nói cách khác, độ phức tạp thời gian là O(n), trong đó n là số lượng kiểu trong truy vấn (tuy nhiên, nếu một kiểu được lặp lại nhiều lần, thì kiểu đó phải được tính nhiều lần vào n).

Giải pháp này rất hiệu quả, chắc chắn là hiệu quả hơn so với độ phức tạp theo cấp số nhân dự kiến khi xử lý đồ thị, hoặc độ phức tạp theo logarithm khi xử lý cây.

Kết luận về triển khai GraphQL server

Một GraphQL server không cần phải sử dụng đồ thị để đại diện cho dữ liệu. Trong bài viết này, chúng ta đã khám phá kiến trúc được mô tả bởi PoP, và được triển khai bởi GraphQL của PoP, dựa trên các thành phần và tải dữ liệu theo các vòng lặp theo kiểu.F

Thông qua phương pháp này, máy chủ có thể giải quyết các truy vấn GraphQL với độ phức tạp thời gian tuyến tính, đây là kết quả tốt hơn so với độ phức tạp theo cấp số nhân hoặc logarithm được mong đợi khi sử dụng đồ thị hoặc cây.

Bài viết liên quan