Để hoàn thành đồ án “Notopia - Ứng dụng ghi chú thông minh hỗ trợ quản lý tri thức bằng biểu đồ quan hệ”, bên cạnh sự nỗ lực của các thành viên, chúng em đã may mắn nhận được sự đồng hành và chỉ dẫn vô cùng quý giá.
Đặc biệt, chúng em xin bày tỏ lòng tri ân sâu sắc nhất tới cô Trần Thị Hồng Yến. Không chỉ là người định hướng chuyên môn, cô còn dành tâm huyết để khích lệ, giúp chúng em tháo gỡ những nút thắt kỹ thuật và tư duy hệ thống trong suốt quá trình nghiên cứu. Những góp ý đầy tận tâm của cô chính là kim chỉ nam giúp Notopia hình thành và hoàn thiện như ngày hôm nay.
Đồng thời, nhóm xin gửi lời cảm ơn chân thành đến Khoa Công nghệ Phần mềm, trường Đại học Công nghệ Thông tin – ĐHQG TP.HCM. Cảm ơn Nhà trường đã tạo dựng một môi trường học tập hiện đại, cung cấp nền tảng tri thức và cơ sở vật chất tốt nhất để chúng em thực hiện đề tài này.
Mặc dù đã dồn hết tâm sức, song đồ án chắc chắn vẫn còn những điểm cần cải thiện. Chúng em rất mong nhận được sự chỉ dẫn và đóng góp từ quý thầy cô để nhóm có thể phát triển ứng dụng ngày một hoàn thiện hơn.
Đề tài tập trung nghiên cứu và xây dựng “Notopia - Ứng dụng ghi chú thông minh hỗ trợ quản lý tri thức bằng biểu đồ quan hệ”, với mục đích giúp người dùng quản lý tri thức cá nhân trên một nền tảng web trực quan, cộng tác theo thời gian thực. Đề tài không hướng đến việc giải quyết các nhược điểm của các nền tảng sẵn có trên thị trường, mà thay vào đó tập trung nghiên cứu kiến trúc, phương pháp phát triển phần mềm, triển khai hệ thống, khai thác và sử dụng các công nghệ hiện đại.
Báo cáo trình bày các nghiên cứu, quy trình thiết kế, cài đặt và triển khai hệ thống thông qua các chương sau:
Chương này trình bày bối cảnh thực tiễn và lý do lựa chọn đề tài, từ đó làm rõ mục tiêu, phạm vi và đối tượng nghiên cứu của dự án. Nội dung của chương nhằm giúp người đọc có cái nhìn toàn diện về định hướng nghiên cứu, cơ sở khoa học và giá trị ứng dụng thực tiễn của đề tài trước khi đi sâu vào các chương phân tích và thiết kế chi tiết ở các phần tiếp theo.
Trong kỷ nguyên bùng nổ thông tin, việc quản lý kiến thức cá nhân (Personal Knowledge Management - PKM) trở thành một kỹ năng thiết yếu. Các phương pháp ghi chú truyền thống theo dạng danh sách hoặc thư mục dần bộc lộ hạn chế trong việc kết nối các ý tưởng rời rạc. Lấy cảm hứng từ các ứng dụng như Notion, Obsidian, chúng em quyết định thực hiện đề tài xây dựng một nền tảng ghi chú hiện đại hỗ trợ liên kết hai chiều1, kết hợp trực quan hóa kiến thức thành biểu đồ quan hệ.
Giúp người dùng quản lý tri thức cá nhân trên nền tảng web, trực quan hoá bằng biểu đồ quan hệ (Graph View), cộng tác (collaborate) theo thời gian thực. Ứng dụng đảm bảo tối thiểu các chức năng của một trình quản lý ghi chú (note) bao gồm tạo, sửa, xoá tạm thời, xoá vĩnh viễn, khôi phục ghi chú, tìm kiếm toàn văn (full-text search), phân quyền truy cập vào không gian làm việc (workspace).
Đối tượng nghiên cứu về mặt nghiệp vụ
Đối tượng nghiên cứu về mặt kỹ thuật
Sử dụng dịch vụ thứ bên thứ 3 để quản lý đăng ký, đăng nhập, thông qua OAuth2/OpenID Connect, đảm bảo an toàn và dễ dàng tích hợp với các dịch vụ khác trong hệ thống.
Quản lý không gian làm việc, sắp xếp các ghi chú theo cấu trúc thư mục, biểu diễn quan hệ giữa các ghi chú, hỗ trợ cộng tác theo thời gian thực ở góc độ lưu trữ và tổ chức thông tin.
Đây là phạm vi cốt lõi của hệ thống, tập trung vào việc tổ chức và lưu trữ thông tin một cách hiệu quả.
Nội dung của ghi chú được lưu trữ dưới dạng tài liệu, sử dụng định dạng Block-based bởi thư viện BlockNote, cho phép linh hoạt trong việc trình bày và chỉnh sửa nội dung. Đồng thời, hỗ trợ cộng tác theo thời gian thực trên tài liệu. Các tệp tin đính kèm trong ghi chú thông qua giải pháp lưu trữ đối tượng (Object Storage).
Quản lý quyền truy cập vào không gian làm việc. Không gian làm việc có thể được chia sẻ với nhiều người dùng, mỗi người có quyền hạn khác nhau, đảm bảo an toàn và kiểm soát truy cập hiệu quả.
Lắng nghe và cập nhật nội dung ghi chú khi thay đổi thông qua một “worker”, cập nhật vào dịch vụ tìm kiếm bên thứ ba.
Dự án áp dụng phương pháp tiếp cận kỹ thuật hệ thống, kết hợp giữa nghiên cứu lý thuyết về quản lý tri thức và triển khai thực nghiệm các công nghệ phần mềm tiên tiến.
Khảo sát các ứng dụng quản lý ghi chú hiện có (Notion, Obsidian, jackyzha0/quartz) để rút ra các tính năng cần thiết.
Sử dụng UML để mô hình hoá kiến trúc hệ thống, bao gồm sơ đồ lớp (class diagram), sơ đồ tuần tự (sequence diagram). Sử dụng D2 để mô hình hoá sơ đồ triển khai (deployment diagram), cơ sở dữ liệu quan hệ. Trong đó:
Thiết kế API theo chuẩn RESTful, sử dụng OpenAPI 3.0 để mô tả API giữa Web App và API service, Protocol Buffers 3 cho giao tiếp giữa các service nội bộ. Áp dụng hướng tiếp cận “Contract First”, đảm bảo tính nhất quán từ giai đoạn thiết kế đến triển khai.
Nhờ vào việc đặc tả này, chúng ta có thể tự động sinh mã nguồn cho client và server, giảm thiểu lỗi và tăng tốc độ phát triển.
Hệ thống Notopia được xây dựng như một nền tảng quản lý tri thức cá nhân, hỗ trợ người dùng tổ chức, kết nối và trực quan hóa kiến thức trên môi trường web hợp tác theo thời gian thực. Các nhóm chức năng chính bao gồm:
Tạo và quản lý không gian làm việc
Người dùng có thể tạo các không gian làm việc riêng biệt, chia sẻ với người khác và quản lý quyền truy cập một cách linh hoạt. Mỗi không gian làm việc hoạt động như một vùng lưu trữ độc lập cho các ghi chú và dự án của người dùng.
Tổ chức ghi chú theo cấu trúc thư mục
Hệ thống cho phép người dùng sắp xếp ghi chú thành các thư mục lồng nhau, tạo thành một cấu trúc dữ liệu rõ ràng. Cách tổ chức này giúp người dùng quản lý số lượng lớn ghi chú một cách hiệu quả.
Tạo các liên kết hai chiều giữa ghi chú
Người dùng có thể liên kết các ghi chú với nhau thông qua cơ chế liên kết hai chiều, tạo thành một mạng lưới tri thức động. Hệ thống tự động ghi nhận các liên kết ngược, giúp hiểu rõ mối quan hệ giữa các ý tưởng.
Soạn thảo nội dung dạng block-based
Nội dung ghi chú được tổ chức thành các khối độc lập, cho phép người dùng linh hoạt trong việc xây dựng và chỉnh sửa tài liệu. Mỗi khối có thể là văn bản, hình ảnh, mã code hoặc các loại nội dung khác.
Cộng tác theo thời gian thực
Nhiều người dùng có thể làm việc cùng một ghi chú hoặc tài liệu một cách đồng thời. Hệ thống đảm bảo các thay đổi được đồng bộ hóa tức thời và các xung đột được giải quyết tự động mà không cần can thiệp thủ công.
Trực quan hóa mối quan hệ bằng biểu đồ quan hệ
Người dùng có thể xem toàn bộ mạng lưới tri thức của mình dưới dạng biểu đồ quan hệ (Graph View), giúp hiển thị các liên kết và mối quan hệ giữa các ghi chú. Biểu diễn này cung cấp một cái nhìn trực quan hơn so với cách tổ chức tuyến tính truyền thống.
Tìm kiếm nhanh chóng
Hệ thống hỗ trợ tìm kiếm toàn văn trên tất cả nội dung ghi chú, cho phép người dùng nhanh chóng tìm ra thông tin cần thiết.
Quản lý quyền truy cập và phân quyền
Chủ sở hữu không gian làm việc có thể quản lý chi tiết quyền của từng thành viên, chẳng hạn như quyền xem, chỉnh sửa hoặc xóa. Hệ thống hỗ trợ các cấp độ quyền khác nhau để đảm bảo an toàn và kiểm soát truy cập hiệu quả.
Quản lý vòng đời tài liệu
Ghi chú có thể được xóa tạm thời, di chuyển vào thùng rác hoặc xóa vĩnh viễn. Người dùng cũng có khả năng khôi phục ghi chú đã xóa tạm thời, giúp tránh mất dữ liệu không mong muốn.
Dự án Notopia ứng dụng một bộ công nghệ tiên tiến và được lựa chọn kỹ lưỡng, đảm bảo tính mở rộng, hiệu suất cao và khả năng bảo trì dài hạn.
Web App
Backend
document service: Viết bằng Typescript sử dụng framework NestJS, với kiến trúc mô-đun rõ ràng và Dependency Injection mạnh mẽ, được xây dựng với Rspack để tối ưu hiệu suất build.notes service: Viết bằng Go, một ngôn ngữ lập trình hiệu suất cao, được sử dụng với SQLC cho xử lý raw SQL tối ưu, đảm bảo truy vấn nhanh chóng đặc biệt cho các truy vấn đồ thị phức tạp.authorization service: Viết bằng Go, sử dụng thư viện Casbin, một framework authorization mã nguồn mở hỗ trợ RBAC cho phép quản lý quyền truy cập linh hoạt.search-worker worker: Viết bằng NestJS lắng nghe sự thay đổi dữ liệu, xử lý và đồng bộ nội dung với Meilisearch.Cơ sở dữ liệu
Cộng tác theo thời gian thực
Giao tiếp API
Bên cạnh đó, các dịch vụ không thể giao tiếp qua gRPC, nhưng có SDK, hoặc có thể giao tiếp qua REST API (ví dụ như Authentik hỗ trợ SDK cho Go và NodeJS).
Kiến trúc sự kiện
Đối với NestJS đã hỗ trợ sẵn event-driven architecture, không cần sử dụng thêm thư viện bên ngoài hệ sinh thái NestJS.
Giám sát
Hạ tầng
Go (còn gọi là Golang) là ngôn ngữ lập trình mã nguồn mở được phát triển bởi Google vào năm 2007 và chính thức phát hành vào năm 2009. Được thiết kế bởi Robert Griesemer, Rob Pike và Ken Thompson, Go nhằm mục tiêu tạo ra một ngôn ngữ hiệu quả, dễ học, và phù hợp cho lập trình hệ thống quy mô lớn.
Dự án sử dụng Go cùng với công cụ goforj/wire [1], một fork của google/wire, giúp dependencies injection có hỗ trợ cache fast để tối ưu thời gian build.
Go mang lại nhiều lợi ích trong phát triển backend:
Bên cạnh các ưu điểm, Go có một số hạn chế:
if err != nil xuất hiện lặp đi lặp lại làm code phồng phứcTypeScript là một ngôn ngữ lập trình mã nguồn mở được phát triển và duy trì bởi Microsoft. TypeScript là một superset của JavaScript, nghĩa là mọi code JavaScript hợp lệ đều là code TypeScript hợp lệ. Được ra mắt lần đầu vào năm 2012 bởi Anders Hejlsberg (người thiết kế C#), TypeScript bổ sung hệ thống kiểu dữ liệu tĩnh trên nền tảng JavaScript để tăng độ tin cậy và khả năng duy trì của code.
TypeScript hoạt động thông qua một bước biên dịch: mã TypeScript được biên dịch thành mã JavaScript, sau đó được chạy trên JavaScript runtime (trình duyệt hoặc Node.js).
TypeScript mang lại nhiều lợi ích khi phát triển ứng dụng JavaScript:
Bên cạnh các ưu điểm, TypeScript có một số hạn chế:
Dự án microsoft/typescript-go [3] đang phát triển một implementation của TypeScript được viết hoàn toàn bằng Go, thay vì TypeScript hiện tại được viết bằng TypeScript. Dự án này hướng tới việc cải thiện hiệu suất.
Dự án được thiết lập với monorepo, các package được chia build riêng biệt (SWC, Rspack, tsgo, vite) để tăng tốc thời gian build, Oxlint thay cho ESLint, Oxfmt thay cho Prettier để tăng tốc độ linting và formatting, CI, ngoại trừ web vì sử dụng NextJS.
Frontend của dự án được xây dựng với React [4], một thư viện JavaScript cho xây dựng user interfaces với component-based architecture. Next.js [5] được sử dụng như một framework trên React, cung cấp server-side rendering, static generation, và routing tích hợp. Redux Toolkit [6] được sử dụng cho state management.
React là thư viện JavaScript mã nguồn mở từ Meta (Facebook) cho xây dựng user interfaces [4]. React sử dụng virtual DOM để tối ưu rendering, component-based architecture cho reusability, và declarative syntax làm cho code dễ đọc hơn.
Next.js là framework React được phát triển bởi Vercel [5], cung cấp Server-Side Rendering (SSR), Static Site Generation (SSG), Incremental Static Regeneration (ISR), và API routes tích hợp. Next.js giúp tối ưu hiệu suất và SEO mà không cần setup phức tạp.
Redux Toolkit là state management library cho React [6], cung cấp một cách đơn giản để quản lý application state. Redux Toolkit giảm boilerplate của Redux truyền thống, hỗ trợ DevTools integration, middleware support, và immer integration cho immutable updates.
Styling được thực hiện bằng TailwindCSS [7], một CSS framework utility-first, kết hợp với PostCSS cho các biến đổi CSS nâng cao. ShadcnUI [8] cung cấp các pre-built components theo design system với styling được tích hợp sẵn.
TailwindCSS là CSS framework utility-first được viết bằng PostCSS [7]. Thay vì viết CSS tùy chỉnh, developer sử dụng các utility classes được định sẵn. Approach này tăng tốc độ phát triển, tăng consistency, và giảm CSS bundle size.
PostCSS là một công cụ cho việc biến đổi CSS sử dụng JavaScript plugins. PostCSS cung cấp khả năng mở rộng, hỗ trợ modern CSS features, vendor prefixing tự động, và tích hợp tốt với TailwindCSS.
ShadcnUI cung cấp một bộ sưu tập các component React đẹp mắt, accessible, và tùy chỉnh cao [8]. Components được xây dựng trên Radix UI cho accessibility và được styled bằng TailwindCSS cho consistency với design system.
NestJS là một framework progressive Node.js được xây dựng để phát triển các ứng dụng server-side hiệu quả, đáng tin cậy và có khả năng mở rộng cao. Được phát triển bởi Kamil Myśliwiec và ra mắt lần đầu vào năm 2017, NestJS kết hợp các khái niệm từ Angular, Spring Framework và các framework hiện đại khác.
NestJS được xây dựng trên nền tảng Express.js (hoặc Fastify) và sử dụng TypeScript [9] làm ngôn ngữ chính. Framework này tổ chức code theo mô hình kiến trúc mô-đun rõ ràng, bao gồm controllers, services, middleware, guards, interceptors, và pipes, tương tự như Spring Framework của Java.
Trong dự án này, NestJS được xây dựng với:
NestJS mang lại nhiều lợi ích cho phát triển backend:
Bên cạnh các ưu điểm, NestJS có một số hạn chế:
PostgreSQL (còn gọi là Postgres) là một hệ quản trị cơ sở dữ liệu quan hệ đối tượng (ORDBMS) mã nguồn mở [10], mạnh mẽ và tiên tiến. PostgreSQL được phát triển từ dự án POSTGRES tại Đại học California, Berkeley vào năm 1986 và đã phát triển hơn 35 năm với cộng đồng đóng góp tích cực.
PostgreSQL nổi tiếng vì sự tuân thủ nghiêm ngặt các chuẩn SQL, hỗ trợ ACID transactions đầy đủ, và khả năng mở rộng cao thông qua các kiểu dữ liệu tùy chỉnh và extensions như PostGIS, pgcrypto, và full-text search.
PostgreSQL mang lại nhiều lợi ích cho phát triển ứng dụng:
Bên cạnh các ưu điểm, PostgreSQL có một số hạn chế:
Dự án sử dụng nhiều tool và framework cho data persistence:
SQLC [11] là tool sinh Go code từ SQL queries. Thay vì viết ORM-style code, SQLC cho phép viết SQL trực tiếp và tự động sinh type-safe Go functions.
TypeORM [12] là ORM mã nguồn mở cho TypeScript, hỗ trợ multiple database backends (PostgreSQL [10], MySQL, SQLite, Oracle, v.v.). TypeORM cung cấp decorator-based API, tương thích với tốt NestJS. Đặc biệt, TypeORM tính đến thời điểm hiện tại sắp ra phiên bản 1.0.0 sau 9 năm phát triển4
Yjs là một thư viện CRDT (Conflict-free Replicated Data Type) được viết bằng JavaScript, thiết kế để hỗ trợ real-time collaboration trên nhiều nền tảng. CRDT cho phép các thay đổi từ nhiều người dùng được tự động hợp nhất mà không cần xung đột, làm cho Yjs trở thành lựa chọn lý tưởng cho các ứng dụng collaborative.
Yjs được sử dụng trong BlockNote [14] và nhiều editor khác để cung cấp khả năng collaborative editing. Thư viện này có thể hoạt động với các transport khác nhau như WebSocket, qua các dịch vụ như Hocuspocus [15].
Yjs mang lại nhiều lợi ích cho phát triển collaborative:
Bên cạnh các ưu điểm, Yjs có một số hạn chế:
BlockNote là một thư viện editor được xây dựng trên nền tảng Tiptap và ProseMirror [14]. BlockNote cung cấp một công cụ soạn thảo văn bản phong phú với kiến trúc block-based tương tự như Notion, cho phép người dùng xây dựng các khối nội dung một cách linh hoạt.
BlockNote được thiết kế để dễ tích hợp vào các ứng dụng React (Phần 2.3.2), với API rõ ràng và khả năng tùy chỉnh cao, tương thích với màu sắc của shadcnui (Phần 2.4.4) . Thư viện này hoạt động dựa trên ProseMirror, một editor framework mạnh mẽ và có cấu trúc rõ ràng. Có thể hình dung ProseMirror như một bộ công cụ xây dựng editor, trong khi BlockNote là một implementation cụ thể, dễ dàng sử dụng nhanh.
BlockNote là một hệ sinh thái mã nguồn mở hoàn toàn, miễn phí sử dụng công cộng. Chỉ riêng các gói thư viện @blocknote/xl-* có giấy phép copyleft, yêu cầu mua giấy phép nếu sử dụng trong sản phẩm mã nguồn đóng, hoặc thương mại.
Model dữ liệu của BlockNote được tổ chức thành các block, mỗi block đại diện cho một phần nội dung riêng biệt, như đoạn văn, hình ảnh, bảng,… Mỗi block có một cấu trúc dữ liệu riêng, bao gồm loại block, nội dung và các thuộc tính liên quan.
Dưới đây là ví dụ về cấu trúc dữ liệu của một block trong BlockNote:
BlockNote mang lại nhiều lợi ích cho phát triển editor:
Bên cạnh các ưu điểm, BlockNote có một số hạn chế:
OpenAPI là một specification cho mô tả HTTP APIs theo cách chuẩn hóa. OpenAPI cho phép sinh code từ contracts, tạo điều kiện cho contract-first development, và tự động tạo documentation.
OpenAPI (trước đây được gọi là Swagger) là một format standardized cho mô tả RESTful APIs. OpenAPI specification định nghĩa endpoints, parameters, request/response schemas, và error codes theo cách machine-readable.
heyapi/openapi-ts [19] sinh TypeScript types từ OpenAPI specoapi-codegen [20] sinh Go code từ OpenAPI spec, hỗ trợ HTTP API generationtypescript-nestjs-server [22]Contract-first approach định nghĩa API contracts trước khi implement logic, đảm bảo tính consistency, tạo điều kiện cho parallel development, và giảm integration issues.
GRPC là một framework RPC hiện đại được phát triển bởi Google [23], sử dụng Protocol Buffers cho định nghĩa service và HTTP/2 cho communication. GRPC được thiết kế để cung cấp hiệu suất cao, latency thấp, và tích hợp tốt với distributed systems.
Traefik là một API gateway mã nguồn mở [28], hiện đại, được viết bằng Go. Traefik được thiết kế để tự động phát hiện và kết nối các dịch vụ, loại bỏ nhu cầu cấu hình thủ công. Traefik hỗ trợ OpenTelemetry (Phần 2.17) cho distributed tracing, cho phép quan sát performance toàn bộ request flow.
Traefik hoạt động tốt trong các môi trường container-orchestrated như Docker, Kubernetes, Docker Swarm và cũng có thể chạy standalone cho các ứng dụng non-containerized.
Dự án sử dụng traefik-plugins/traefik-jwt-plugin [29] để xác minh request đến các API endpoint, thông tin được chuyển đổi sang request headers giúp cho các service không cần phải thực hiện quá trình xác minh riêng.
Traefik mang lại nhiều lợi ích cho phát triển API gateway:
Bên cạnh các ưu điểm, Traefik có một số hạn chế:
Casbin là một framework authorization mã nguồn mở mạnh mẽ và linh hoạt [30], hiện thuộc Apache Software Foundation. Casbin hỗ trợ các mô hình kiểm soát truy cập như ACL, RBAC, ABAC, và các biến thể khác. Framework này cho phép định nghĩa các rule authorization một cách khai báo thông qua cấu hình, thay vì hardcode logic kiểm soát.
Casbin được thiết kế để hoạt động với nhiều ngôn ngữ lập trình, bao gồm Go, Java, Python, Node.js và hơn thế nữa, giúp đảm bảo tính nhất quán trong authorization logic trên toàn bộ hệ sinh thái.
So với các giải pháp authorization khác như OPA, SpiceDB (Google Zanzibar opensource), Casbin tập trung vào sự đơn giản và hiệu quả, cung cấp một cách tiếp cận.
Casbin mang lại nhiều lợi ích cho phát triển authorization:
Bên cạnh các ưu điểm, Casbin có một số hạn chế:
Meilisearch là một search engine mã nguồn mở được viết bằng Rust [31], thiết kế để cung cấp trải nghiệm tìm kiếm nhanh, liên quan, và dễ sử dụng. Meilisearch được phát triển với mục tiêu là một giải pháp tìm kiếm dễ triển khai hơn Elasticsearch, phù hợp cho các ứng dụng từ nhỏ đến lớn.
Meilisearch mang lại nhiều lợi ích cho phát triển search:
Bên cạnh các ưu điểm, Meilisearch có một số hạn chế:
Authentik là một nền tảng identity provider mã nguồn mở được thiết kế để cung cấp các dịch vụ xác thực và cấp quyền tập trung [32]. Authentik hỗ trợ các chuẩn hiện đại như OIDC (OpenID Connect) và OAuth 2.0, cho phép kết nối dễ dàng với nhiều ứng dụng và dịch vụ.
Authentik có nhiều ưu điểm nổi bật:
Bên cạnh các ưu điểm, Authentik có một số hạn chế:
RustFS [33] là một hệ thống lưu trữ object storage được viết hoàn toàn bằng Rust, tương thích với API của Amazon S3. RustFS được phát triển như một giải pháp thay thế hiệu suất cao hơn cho MinIO (đã ngừng phát triển), đặc biệt là trong các trường hợp cần throughput lớn và latency thấp.
Rust được chọn vì hiệu suất và an toàn bộ nhớ, giúp RustFS cung cấp một cơ sở hạ tầng lưu trữ đáng tin cậy với resource overhead tối thiểu.
RustFS mang lại nhiều lợi ích cho phát triển object storage:
Bên cạnh các ưu điểm, RustFS có một số hạn chế:
OpenTelemetry Protocol (OTLP) là một tiêu chuẩn mã nguồn mở cho việc thu thập và xuất dữ liệu observability (metrics, logs, traces) [24]. OTLP được hỗ trợ bởi hầu hết các công cụ monitoring hiện đại.
Dự án sử dụng một stack observability hoàn chỉnh bao gồm:
Các service được instrumented bằng:
@opentelemetry/auto-instrumentations-node: auto instrumentation cho Node.jsgo.opentelemetry.io/contrib/exporters/autoexport cho auto setup exporters cho GoStack observability mang lại nhiều lợi ích:
Bên cạnh các ưu điểm, stack này có một số hạn chế:
Redpanda là một nền tảng event streaming mã nguồn mở được viết bằng C++ với API tương thích Kafka [39]. Redpanda được thiết kế để cung cấp hiệu suất cao hơn Kafka trong khi duy trì tính tương thích hoàn toàn với Kafka protocol và ecosystem.
Redpanda được sử dụng như một message broker trong dự án, hỗ trợ pub/sub patterns cho event-driven architecture.
Redpanda mang lại nhiều lợi ích cho phát triển event-driven:
Bên cạnh các ưu điểm, Redpanda có một số hạn chế:
postgres_cdc [40] của Redpanda Connect yêu cầu phiên bản trả phí, có thể sử dụng Debezium, hoặc sử dụng sql_select [41] của Redpanda ConnectWatermill [42] là library Go cho event-driven architecture, hỗ trợ multiple message routers. Watermill được thiết kế để tạo điều kiện cho asynchronous message processing, event streaming, request-response, và reactive architectures.
Watermill cung cấp abstraction trên các message brokers khác nhau, cho phép viết event processing logic một lần và sử dụng với nhiều transport backends.
Watermill được sử dụng trong dự án:
Watermill hỗ trợ OpenTelemetry (Phần 2.17) integration thông qua nkonev/watermill-opentelemetry [44], cho phép trace event processing workflow. Điều này cung cấp observability cho event-driven systems.
Nx [45] là build system và monorepo management tool được phát triển bởi Nrwl. Nx cung cấp các tính năng cho task orchestration, caching intelligent, visualization của dependency graph, code generation, và workspace analysis.
Trong dự án, Nx được sử dụng như task runner và build system, thiết lập toàn bộ luồng code gen cho GRPC, OpenAPI, SQLC,… Trước đây, nhóm đã từng thiết lập Bazel nhưng đã chuyển sang Nx vì Bazel chỉ giải quyết được vấn đề build, không giúp việc dev trở nên dễ dàng hơn.
Chương 3 trình bày quá trình phân tích và thiết kế hệ thống nhằm xác định rõ cấu trúc và cách thức hoạt động của hệ thống. Nội dung chương bao gồm khảo sát hiện trạng, xác định yêu cầu chức năng và phi chức năng, từ đó xây dựng kiến trúc hệ thống và mô tả các thành phần chính. Bên cạnh đó, chương sử dụng các sơ đồ UML để mô hình hóa hành vi, luồng xử lý và mối quan hệ giữa các đối tượng trong hệ thống, thiết kế dữ liệu thông qua các bảng cơ sở dữ liệu. Hệ thống cũng phân tích BlockNote schema tuỳ chỉnh, và mô hình thư viện Casbin của hệ thống.
Hiện nay có nhiều hệ thống quản lý kiến thức cá nhân khác nhau, mỗi hệ thống đều có những ưu điểm và nhược điểm riêng. Dưới đây là một số hệ thống phổ biến mà nhóm đã khảo sát.
Notion
Notion là một nền tảng làm việc tích hợp được ra mắt vào năm 2013, kết hợp tài liệu, cơ sở dữ liệu, wiki và quản lý dự án trong một nền tảng duy nhất. Với hơn 30 triệu người dùng toàn cầu, Notion đã phát triển từ một ứng dụng ghi chú đơn giản thành một hệ thống kiến thức toàn diện.
Lịch sử phát triển của Notion bắt đầu với phiên bản 1.0 tập trung vào hợp tác tài liệu thời gian thực và tổ chức tài liệu phong cách wiki. Điểm đột phá lớn nhất đến với phiên bản 2.0 vào tháng 3 năm 2018 khi giới thiệu tính năng Database, nâng cấp Notion lên một tầm cao mới và cho phép tạo ra các công cụ ứng dụng. Hiện tại, Notion liên tục được cập nhật với các tính năng mới như AI agents, hệ thống tự động hóa hoàn chỉnh và nhiều loại giao diện cơ sở dữ liệu mới.
Các tính năng chính bao gồm editor module hóa với các khối có thể sắp xếp linh hoạt, cơ sở dữ liệu đa dạng (bảng, Kanban, lịch trình, gallery), khả năng hợp tác thời gian thực, và tích hợp AI. Giao diện của Notion được đánh giá cao về tính thẩm mỹ và sự linh hoạt, cho phép người dùng tùy chỉnh không gian làm việc theo nhu cầu riêng.
Tuy nhiên, Notion cũng có những hạn chế nhất định, điển hình như không hỗ trợ graph view một cách trực quan.
Obsidian
Obsidian là một ứng dụng ghi chú và quản lý kiến thức dựa trên tệp Markdown, được phát triển bởi Shida Li và Erica Xu và ra mắt vào năm 2020. Với triết lý “local-first”, Obsidian đảm bảo dữ liệu của người dùng luôn nằm trên thiết bị của họ, mang lại quyền sở hữu và tính linh hoạt tối đa.
Obsidian hoạt động với một “vault” chứa các tài liệu văn bản, mỗi ghi chú mới tạo ra một tệp Markdown riêng. Điểm mạnh nổi bật nhất của Obsidian là khả năng liên kết hai chiều và chế độ xem graph giúp người dùng hình dung mối quan hệ giữa các ghi chú. Người dùng có thể tạo liên kết nội dung bằng Wikilinks hoặc liên kết Markdown thông thường, và tất cả các liên kết này đều được hiển thị trong graph view tương tác.
Các tính năng chính bao gồm chế độ xem graph giúp phân tích mối quan hệ giữa các ghi chú, Canvas cho phép sắp xếp ghi chú trực quan không giới hạn, hệ thống plugin mở rộng do cộng đồng phát triển, và khả năng tích hợp với nhiều công cụ khác. Obsidian cũng hỗ trợ chế độ chỉnh sửa song song giữa Source Mode và Live Preview, cùng với đầy đủ các công cụ tìm kiếm và tổ chức. Obsidian còn chính hỗ trợ vim motion cho những người dùng yêu thích trải nghiệm chỉnh sửa văn bản nâng cao.
Mặc dù mạnh mẽ, Obsidian cũng có những thách thức. Learning curve khá cao, đặc biệt với người mới bắt đầu. Thiếu tính năng hợp tác thời gian thực là một hạn chế quan trọng. Giao diện mặc định có thể trông đơn giản so với các đối thủ cạnh tranh. Obsidian phù hợp nhất với các nhà nghiên cứu, nhà văn, lập trình viên và người làm việc kiến thức muốn kiểm soát sâu sắc ghi chú của mình.
jackyzha0/quartz
Quartz là một static-site generator nhanh, được thiết kế để biến nội dung Markdown thành các website hoàn toàn chức năng. Được phát triển bởi jackyzha0, Quartz hoạt động như một giải pháp self-hosted thay thế cho Obsidian Publish, cho phép người dùng xuất bản ghi chú và digital gardens của mình lên web một cách dễ dàng.
Quartz tương thích hoàn toàn với Obsidian, hỗ trợ đầy đủ các tính năng như wikilinks, backlinks, transclusions, graph view và full-text search. Nó được xây dựng trên công nghệ TypeScript và cung cấp khả năng tùy chỉnh cao thông qua JSX layouts và page components. Một trong những điểm mạnh của Quartz là tốc độ tải trang cực nhanh và kích thước bundle nhỏ.
Các tính năng chính bao gồm hỗ trợ LaTeX, Mermaid diagrams, dark mode, breadcrumbs, popover previews, internationalization, và hệ thống comments. Quartz cũng hỗ trợ Docker, RSS feeds, và có hệ thống plugin mở rộng. Nó đặc biệt phù hợp cho các cá nhân muốn chia sẻ kiến thức của mình dưới dạng website công khai hoặc digital garden.
Mặc dù mạnh mẽ, Quartz cũng có những hạn chế. Nó yêu cầu kiến thức kỹ thuật về Git và hosting để thiết lập. Không có tính năng hợp tác thời gian thực như Notion. Giao diện quản lý nội dung không trực quan bằng các ứng dụng desktop. Quartz phù hợp nhất với các nhà phát triển, sinh viên, và giáo viên muốn xuất bản ghi chú của mình dưới dạng website.
| Hệ thống | Ưu điểm | Nhược điểm |
| Notion | Giao diện đẹp, đa tính năng, hợp tác thời gian thực | Phức tạp khi quản lý dự án lớn, tốc độ tải chậm với dữ liệu lớn |
| Obsidian | Local-first, graph view mạnh mẽ, liên kết hai chiều, plugin đa dạng | Thiếu hợp tác thời gian thực, learning curve cao |
| Quartz | Tốc độ nhanh, tương thích Obsidian, dễ xuất bản website, tùy chỉnh cao | Yêu cầu kiến thức kỹ thuật, không có hợp tác thời gian thực, giao diện đơn giản |
Xuất phát từ đó, nhóm đã xác định được những điểm mạnh và hạn chế của từng hệ thống, từ đó rút ra những bài học quan trọng để áp dụng vào thiết kế hệ thống của mình. Nhưng cũng cần nhấn mạnh rằng Notopia không nhằm mục đích thay thế hoàn toàn các hệ thống hiện có, mà chỉ thực hiện một số chức năng nhất định, mang tính chất nghiên cứu, học tập, áp dụng công nghệ và quy trình phát triển phần mềm.
| Giai đoạn | Thời gian | Hoạt động | Kết quả |
| 1 | 26/01 - 04/02 | Khởi động & Nghiên cứu | Xác định yêu cầu, nghiên cứu công nghệ |
| 2 | 05/02 - 12/02 | Thiết lập | Môi trường làm việc, CI Pipeline sẵn sàng |
| 3 | 13/02 - 25/02 | Phân tích & Thiết kế | Đặc tả Use Case, thiết kế Database Schema, UI/UX |
| 4 | 26/02 - 05/04 | Phát triển & Tích hợp (Đợt 1) | Hệ thống đăng nhập và quản lý ghi chú cơ bản hoạt động |
| 5 | 06/04 - 10/05 | Phát triển & Tích hợp (Đợt 2) | Ứng dụng đầy đủ tính năng |
| 6 | 11/05 - 17/05 | Hoàn thiện Sản phẩm | Sản phẩm hoàn thiện về tính năng và thẩm mỹ |
| 7 | 18/05 - 31/05 | Báo cáo & Tổng kết | Báo cáo cuối kỳ và source code hoàn chỉnh |
Dự án được chia thành nhiều service khác nhau, bao gồm note service, document service, authorization service, search-worker worker, và các thành phần tái sử dụng từ các dịch vụ sẵn có. Mỗi service có trách nhiệm riêng biệt và giao tiếp với nhau thông qua API và message queue để đảm bảo tính modular và dễ bảo trì.
Dưới đây là sơ đồ kiến trúc tổng quan của hệ thống.
Trong đó, các thành phần chính bao gồm:
| Thành phần | Mô tả |
| User | Người dùng tương tác với hệ thống thông qua giao diện web |
| Gateway | Điểm vào chính của hệ thống, chịu trách nhiệm định tuyến yêu cầu đến các service phù hợp |
| Web App | Ứng dụng web cung cấp giao diện người dùng để tạo và quản lý ghi chú |
| Note Service | Quản lý metadata và logic liên quan đến không gian làm việc và ghi chú |
| Document Service | Quản lý nội dung của các ghi chú |
| Authorization Service | Xác định quyền truy cập của người dùng đối với các tài nguyên |
| Identity Provider | Dịch vụ xác thực và quản lý người dùng |
| Object Storage | Lưu trữ các tệp liên quan đến ghi chú, như hình ảnh và tài liệu |
| Search Service | Cung cấp khả năng tìm kiếm nội dung trong ghi chú |
| Event Bus | Hệ thống message queue để giao tiếp giữa các service |
| Monitoring | Giám sát hiệu suất và trạng thái của hệ thống |
Chi tiết về các thành phần chính:
Gateway
Gateway là điểm vào chính của hệ thống, chịu trách nhiệm định tuyến các yêu cầu từ người dùng đến các service phù hợp. Nó cũng thực hiện các chức năng như xác thực, và cân bằng tải để đảm bảo hiệu suất và bảo mật của hệ thống.
Hệ thống sử dụng Traefik (Phần 2.12) làm API Gateway, với đặc điểm gọn nhẹ và dễ cấu hình, tương thích tốt với docker compose. Sử dụng traefik-plugins/traefik-jwt-plugin [29] để xử lý xác thực JWT, chuyển hoá OIDC claim thành header HTTP, giúp các service phía sau có thể dễ dàng xác định người dùng, không cần phải tích hợp trực tiếp với Identity Provider.
Web App
Web App là giao diện người dùng chính, cho phép người dùng tạo, chỉnh sửa và quản lý ghi chú. Ứng dụng được xây dựng với React và NextJS (Phần 2.3).
Note Service
Note Service (note service), viết bằng Go, chịu trách nhiệm quản lý metadata và logic liên quan đến không gian làm việc và ghi chú. Dịch vụ này cung cấp API để Web App có thể tương tác với không gian làm việc và ghi chú, đồng thời tích hợp với Authorization Service để kiểm tra quyền truy cập của người dùng trước khi thực hiện các hành động liên quan đến thư mục, ghi chú.
Document Service
Document Service (document service), viết bằng Typescript và NestJS framework, chịu trách nhiệm quản lý nội dung của các ghi chú, bao gồm lưu trữ và truy xuất dữ liệu. Nó cung cấp API để Web App có thể tương tác với nội dung ghi chú, tích hợp với Object Storage để lưu trữ các tệp liên quan, và sử dụng Hocuspocus (Phần 2.8) để hỗ trợ cộng tác thời gian thực trên nội dung ghi chú.
Authorization Service
Authorization Service (authorization service), viết bằng Go, sử dụng thư viên Casbin (Phần 2.13) để quản lý quyền truy cập của người dùng đối với không gian làm việc và ghi chú. Dịch vụ này cung cấp API để các service khác có thể kiểm tra quyền truy cập của người dùng trước khi thực hiện các hành động liên quan đến thư mục, ghi chú.
Identity Provider
Identity Provider chịu trách nhiệm xác thực người dùng và quản lý thông tin tài khoản. Hệ thống sử dụng Authentik (Phần 2.15) là giải pháp xác thực, cung cấp các tính năng như đăng nhập một lần (SSO), quản lý người dùng, và hỗ trợ nhiều phương thức xác thực. Hệ thống sử dụng OpenID Connect (OIDC) để tích hợp giữa Gateway, Web App với Identity Provider, giúp đơn giản hóa quá trình xác thực và quản lý người dùng.
Object Storage
Object Storage được sử dụng để lưu trữ các tệp liên quan đến ghi chú, như hình ảnh và tài liệu. Hệ thống sử dụng RustFS (Phần 2.16), một giải pháp lưu trữ đối tượng nhẹ và hiệu quả, cung cấp API tương thích với S3 để dễ dàng tích hợp với các service khác.
Search Service
Search Service (Meilisearch), viết bằng Rust, cung cấp khả năng tìm kiếm nội dung trong ghi chú (Phần 2.14). Trong đó, search-worker worker chịu trách nhiệm đồng bộ dữ liệu đến Search Service để đảm bảo dữ liệu tìm kiếm luôn được cập nhật.
Event Bus
Event Bus là hệ thống message queue được sử dụng để giao tiếp giữa các service, đảm bảo tính modular và giảm sự phụ thuộc trực tiếp giữa các service. Hệ thống sử dụng Redpanda (Phần 2.18), một giải pháp message queue hiệu suất cao, tương thích với Kafka API, giúp dễ dàng tích hợp với các service khác.
Monitoring
Monitoring là thành phần quan trọng để giám sát hiệu suất và trạng thái của hệ thống. Hệ thống sử dụng Grafana Stack và Prometheus (Phần 2.17) để thu thập và hiển thị các chỉ số về hiệu suất.
note serviceLà thành phần trung tâm trong hệ thống, note service chịu trách nhiệm quản lý metadata và logic liên quan đến ghi chú. Dịch vụ này được thiết kế theo kiến trúc Clean Architecture, Domain Driven Design, và Event-Driven Architecture để đảm bảo tính modular, dễ bảo trì, và khả năng mở rộng trong tương lai5. Dưới đây là sơ đồ kiến trúc chi tiết của note service.
note service
document servicedocument service áp dụng kiến trúc theo NestJS, với các module được tổ chức theo chức năng. Dưới đây là sơ đồ kiến trúc chi tiết của document service, mô tả đến cấp độ module.
document service
authorization serviceauthorization service được thiết kế theo kiến trúc Layered Architecture, với tầng logic không phân tách rõ vì tính đặc thù của thư viện Casbin. Dưới đây là sơ đồ kiến trúc của authorization service.
authorization service
search-worker workersearch-worker worker được thiết kế theo kiến trúc đơn giản, sử dụng trên 1 module chính của NestJS, không chia thành nhiều module nhỏ. Dưới đây là sơ đồ kiến trúc của search-worker worker.
search-worker worker
Nhóm chỉ xác định một vài use case phức tạp, có nhiều luồng, tác động đến nhiều dịch vụ.
| Trường | Nội dung |
|---|---|
| ID | UC01 |
| Name | Create Note |
| Description | Use case này mô tả quy trình tạo ghi chú mới |
| Actor(s) | User |
| Priority | Cao |
| Trigger | Người dùng muốn tạo một ghi chú mới |
| Pre-condition(s) |
|
| Post-condition(s) |
|
| Basic Flow |
|
| Alternate Flow |
|
| Exception Flow |
|
| Trường | Nội dung |
|---|---|
| ID | UC02 |
| Name | Get Note |
| Description | Use case này mô tả quy trình lấy nội dung ghi chú và chuyển sang chế độ chỉnh sửa |
| Actor(s) | User |
| Priority | Cao |
| Trigger | Người dùng chọn một ghi chú để xem hoặc chỉnh sửa |
| Pre-condition(s) |
|
| Post-condition(s) |
|
| Basic Flow |
|
| Alternate Flow |
|
| Exception Flow |
|
| Trường | Nội dung |
|---|---|
| ID | UC03 |
| Name | Commit Document |
| Description | Use case này mô tả quy trình lưu và publish event cập nhật tài liệu (nội dung của ghi chú) |
| Actor(s) | User |
| Priority | Cao |
| Trigger | Người dùng chọn lưu tài liệu để cập nhật |
| Pre-condition(s) |
|
| Post-condition(s) |
|
| Basic Flow |
|
| Alternate Flow |
|
| Exception Flow |
|
| Trường | Nội dung |
|---|---|
| ID | UC04 |
| Name | Update Note |
| Description | Use case này mô tả quy trình cập nhật thông tin cơ bản của ghi chú |
| Actor(s) | User |
| Priority | Cao |
| Trigger | Người dùng chỉnh sửa và lưu thay đổi thông tin ghi chú |
| Pre-condition(s) |
|
| Post-condition(s) |
|
| Basic Flow |
|
| Alternate Flow |
|
| Exception Flow |
|
Dưới đây là sơ đồ lớp cho các service trong hệ thống, bao gồm note service và document service, không bao gồm authorization service vì tính đặc thù của service không thể biểu diễn một cách dễ dàng, và search-worker vì xử lý dữ liệu không phức tạp. Sơ đồ lớp giúp minh họa cấu trúc của các lớp, các thuộc tính và phương thức của chúng, cũng như mối quan hệ giữa các lớp.
Sơ đồ lớp cho note service được chia thành hai phần: tầng application và tầng domain. Tầng application tập trung vào các lớp mô hình dữ liệu và interface của service, trong khi tầng domain tập trung vào các lớp đại diện cho các aggregate và logic nghiệp vụ.
Sơ đồ lớp tầng application
note service
Tầng application bao gồm các lớp mô hình dữ liệu (model) cho việc read (theo CQRS) và các interface của service. Các lớp mô hình dữ liệu đại diện cho các thực thể trong hệ thống như Note, Folder, Workspace, v.v. Các interface của service định nghĩa các phương thức mà tầng infrastructure sẽ triển khai.
Sơ đồ lớp tầng domain
note service
Tầng domain, áp dụng Domain Driven Design Pattern, bao gồm các lớp đại diện cho các aggregate như Note, Folder, Workspace, v.v. Các lớp này chứa các thuộc tính và phương thức liên quan đến logic nghiệp vụ của chúng. Ngoài ra, tầng domain cũng bao gồm các interface của repository để truy cập dữ liệu. Tuy nhiên, tầng domain không được trực tiếp gọi service được định nghĩa hay repository interface được khai báo.
document service
Sơ đồ tinh gọn cho document service, bao gồm các thực thể, services, và đối tượng liên quan. Ở thiết kế này không bao gồm repository vì đã được TypeORM abstract đi, tức, service sẽ trực tiếp gọi các phương thức của TypeORM để truy cập dữ liệu.
Các service trong hệ thống sẽ sử dụng cơ sở dữ liệu PostgreSQL để lưu trữ và quản lý dữ liệu. Dưới đây là thiết kế cơ sở dữ liệu cho các service, bao gồm các bảng chính và mối quan hệ giữa chúng.
note servicenote service
Bảng workspaces
| Tên cột | Kiểu dữ liệu | Mô tả | Khóa |
|---|---|---|---|
id |
UUID |
Mã định danh duy nhất của workspace | PK |
slug |
TEXT |
URL-friendly slug của workspace | UQ |
name |
TEXT |
Tên của workspace | |
created_at |
TIMESTAMPTZ |
Thời gian tạo | |
updated_at |
TIMESTAMPTZ |
Thời gian cập nhật gần nhất | |
deleted_at |
TIMESTAMPTZ |
Thời gian xóa (soft delete, Nullable) |
note service
Bảng folders
| Tên cột | Kiểu dữ liệu | Mô tả | Khóa |
|---|---|---|---|
id |
UUID |
Mã định danh duy nhất của folder | PK |
name |
TEXT |
Tên của folder (Nullable) | |
icon |
TEXT |
Icon của folder (Nullable) | |
workspace_id |
UUID |
ID workspace chứa folder này | FK |
parent_id |
UUID |
ID folder cha (nested structure) (Nullable) | FK |
created_at |
TIMESTAMPTZ |
Thời gian tạo | |
updated_at |
TIMESTAMPTZ |
Thời gian cập nhật gần nhất | |
trashed_by |
ENUM |
Loại xóa (purpose|parent, Nullable) | |
trashed_at |
TIMESTAMPTZ |
Thời gian xóa (Nullable) |
note service
Bảng notes
| Tên cột | Kiểu dữ liệu | Mô tả | Khóa |
|---|---|---|---|
id |
UUID |
Mã định danh duy nhất của ghi chú | PK |
name |
TEXT |
Tên của ghi chú | |
icon |
TEXT |
Icon của ghi chú (Nullable) | |
folder_id |
UUID |
ID folder chứa ghi chú này | FK |
tags |
TEXT[] |
Danh sách tag của ghi chú (Nullable) | |
size |
INTEGER |
Kích thước của ghi chú (bytes) | |
created_at |
TIMESTAMPTZ |
Thời gian tạo | |
updated_at |
TIMESTAMPTZ |
Thời gian cập nhật gần nhất | |
trashed_by |
ENUM |
Loại xóa (purpose|parent, Nullable) | |
trashed_at |
TIMESTAMPTZ |
Thời gian xóa (Nullable) |
note service
Bảng note_links
| Tên cột | Kiểu dữ liệu | Mô tả | Khóa |
|---|---|---|---|
source_id |
UUID |
ID ghi chú nguồn | PK, FK |
target_id |
UUID |
ID ghi chú đích | PK, FK |
note service
document servicedocument service
Bảng documents
| Tên cột | Kiểu dữ liệu | Mô tả | Khóa |
|---|---|---|---|
id |
UUID |
Mã định danh duy nhất của document | PK |
data |
BYTEA |
Dữ liệu nhị phân yjs6 | |
modified |
BOOLEAN |
Trạng thái đã được chỉnh sửa hay chưa |
document service
Bảng Revisions
| Tên cột | Kiểu dữ liệu | Mô tả | Khóa |
|---|---|---|---|
id |
UUID |
Mã định danh duy nhất của revision | PK |
name |
TEXT |
Tên của revision (Nullable) | |
data |
JSON |
Dữ liệu BlockNote | |
document_id |
UUID |
ID document liên kết với revision | FK |
created_at |
TIMESTAMPTZ |
Thời gian tạo | |
deleted_at |
TIMESTAMPTZ |
Thời gian xóa (Nullable) |
document service
authorization serviceVì tính đặc thù của thư viện Casbin, bảng dữ liệu của service này không thuộc phạm vi quản lý của hệ thống, mà sẽ được Casbin tự động tạo ra và quản lý.
authorization service
Bảng casbin_rules
| Tên cột | Kiểu dữ liệu | Mô tả | Khóa |
|---|---|---|---|
id |
UUID |
Mã định danh duy nhất của rule | PK |
ptype |
TEXT |
Loại rule (p, g, g2, v.v…) | |
v0 |
TEXT |
Trường dữ liệu 0 | |
v1 |
TEXT |
Trường dữ liệu 1 | |
v2 |
TEXT |
Trường dữ liệu 2 | |
v3 |
TEXT |
Trường dữ liệu 3 (Nullable) | |
v4 |
TEXT |
Trường dữ liệu 4 (Nullable) | |
v5 |
TEXT |
Trường dữ liệu 5 (Nullable) |
authorization service
BlockNote bao gồm nhiều schema nhiều schema, có thể xem tại Phần 2.9. Tuy nhiên, để có thể liên kết các ghi chú với nhau, cũng như hỗ trợ liên kết thông qua tag, hệ thống cần tuỳ chỉnh thêm hai config schema cho BlockNote, là reference và tag. Tham khảo từ tài liệu schema tuỳ chỉnh của BlockNote [48], hai config này sẽ được định nghĩa như sau.
Một khối reference sẽ chứa một thuộc tính noteId để xác định ghi chú mà nó đang tham chiếu đến. Khi người dùng chèn một khối reference vào ghi chú, họ sẽ có thể chọn một ghi chú khác trong hệ thống để liên kết đến. Điều này cho phép tạo ra các mối quan hệ giữa các ghi chú.
Khối tag sẽ chứa một thuộc tính tag để lưu trữ tên của tag. Người dùng có thể chèn một khối tag vào ghi chú và gán cho nó một tên tag cụ thể.
Casbin hỗ trợ biểu diễn nhiều mô hình kiểm soát truy cập khác nhau, chi tiết xem tại Phần 2.13. Trong đó, hệ thống tập trung sử dụng mô hình RBAC để quản lý quyền truy cập dựa trên vai trò (role) của người dùng, trong mỗi không gian làm việc. Đối với Casbin, workspace được xem như một domain.
Trong đó, các phần được định nghĩa như sau:
request_definition định nghĩa cấu trúc của một yêu cầu truy cập, bao gồm người dùng (sub), không gian làm việc (dom), đối tượng (obj) và hành động (act)policy_definition định nghĩa cấu trúc của một chính sách, bao gồm vai trò (sub), đối tượng (obj) và hành động (act)role_definition định nghĩa cách thức xác định vai trò của người dùng trong một không gian làm việc (g) và cách thức xác định mối quan hệ giữa đối tượng và chính sách (g2)policy_effect định nghĩa hiệu ứng của chính sách, trong trường hợp này là cho phép truy cập nếu có ít nhất một chính sách cho phépmatchers định nghĩa cách thức so khớp giữa yêu cầu truy cập và chính sách, trong đó yêu cầu truy cập sẽ được phép nếu người dùng có vai trò phù hợp trong không gian làm việc, đối tượng phù hợp với chính sách và hành động phù hợp với chính sáchTrong đó, các chính sách được định nghĩa như sau:
owner có quyền đọc, chỉnh sửa và xóa trên không gian làm việc và các mục trong không gian làm việceditor có quyền đọc trên không gian làm việc và quyền đọc, chỉnh sửa và xóa trên các mục trong không gian làm việcviewer chỉ có quyền đọc trên không gian làm việc và các mục trong không gian làm việcnote và folder được xác định là các mục trong không gian làm việc thông qua mối quan hệ g2Mẫu minh hoạ yêu cầu truy cập và so khớp chính sách
Giả sử ta có ba người dùng: 110, 111 và 112, với các vai trò và quyền truy cập như sau:
Trong đó:
111 là chủ sở hữu (owner) của không gian làm việc 111112 là biên tập viên (editor) của không gian làm việc 111 và chủ sở hữu của không gian làm việc 112110 là người xem (viewer) của không gian làm việc 111 và chủ sở hữu của không gian làm việc 110| Loại | Code |
|---|---|
| Request | |
| Result |
Giải thích cho yêu cầu truy cập đầu tiên
Người dùng 111 yêu cầu đọc không gian làm việc 111. Kết quả là true vì người dùng 111 có vai trò owner trong không gian làm việc 111 và có chính sách cho phép đọc không gian làm việc. Các bước suy luận dựa trên matcher m:
111 có vai trò owner trong không gian làm việc 111 thông qua chính sách g. Ta có thể xác định rằng g(user:111, owner, workspace:111) là true.workspace cũng chứa trong workspace (
)
thông qua chính sách g2. Ta có thể xác định rằngg2(workspace, workspace) là true.read phù hợp với chính sách read của vai trò owner trên không gian làm việc. Ta có thể xác định rằng read == read là true.Giải thích cho yêu cầu truy cập thứ hai
Người dùng 111 yêu cầu viết vào note trong không gian làm việc 111. Kết quả là true vì người dùng 111 có vai trò owner trong không gian làm việc 111 và có chính sách cho phép viết vào các mục trong không làm việc. Các bước suy luận:
111 có vai trò owner trong không gian làm việc 111 thông qua chính sách g. Ta có thể xác định rằng g(user:111, owner, workspace:111) là true.note chứa trong workspace_item (
)
thông qua chính sách g2 Ta có thể xác định rằngg2(note, workspace_item) là true.write phù hợp với chính sách write của vai trò owner trên các mục trong không làm việc. Ta có thể xác định rằng write == write là true.Giải thích cho yêu cầu truy cập thứ ba
Người dùng 112 yêu cầu xóa không gian làm việc 111. Kết quả là false vì người dùng 112 chỉ có vai trò editor trong không gian làm việc 111 và không có chính sách nào cho phép editor xóa không gian làm việc. Các bước suy luận:
112 có vai trò editor trong không gian làm việc 111 thông qua chính sách g. Ta có thể xác định rằng g(user:112, editor, workspace:111) là true.workspace cũng chứa trong workspace (
)
thông qua chính sách g2. Ta có thể xác định rằngg2(workspace, workspace) là true.delete không phù hợp với chính sách delete của vai trò editor trên không gian làm việc. Ta có thể xác định rằng delete == delete là true, nhưng vì không có chính sách nào cho phép editor xóa không gian làm việc, nên yêu cầu truy cập này bị từ chối.Tương tự, các yêu cầu truy cập khác cũng được đánh giá dựa trên vai trò của người dùng trong không gian làm việc và các chính sách đã định nghĩa. Kết quả của mỗi yêu cầu sẽ cho biết liệu yêu cầu đó có được phép hay không, cùng với lý do dựa trên chính sách nào đã cho phép hoặc từ chối yêu cầu đó. Chi tiết có thể xem tại website playground của Casbin cho ví dụ trên tại https://editor.casbin.org/#E7VKBXR2T.
Chương trình bày kết quả xây dựng Web App, bao gồm giao diện, các thành phần giao diện, và mô tả chức năng của từng thành phần.
| STT | Tên | Loại | Mô tả |
|---|---|---|---|
| 1 | Button | Button | Clickable element |
Chương này trình bày phần kết luận của đồ án, nhằm tổng hợp và đánh giá các kết quả đạt được trong quá trình nghiên cứu, phân tích, thiết kế và xây dựng hệ thống. Nội dung chương tập trung vào việc đánh giá sản phẩm đã triển khai, nhận xét những thuận lợi, khó khăn, ưu điểm và hạn chế của hệ thống, đồng thời đề xuất các hướng phát triển trong tương lai nhằm nâng cao tính hoàn thiện và khả năng ứng dụng thực tế.
Sau quá trình phát triển, nhóm đã hoàn thiện được hệ thống ghi chú với đầy đủ các tính năng đã đề ra:
Dự án đã áp dụng thành công các công nghệ hiện đại:
Trong đó, dự án đã đặc biệt tiếp cận đến một số công nghệ đặc biệt mới như:
Công cụ quản lý monorepo Nx
Nhóm đã tối ưu hoá workflow, thiết lập từ code generate, lint, test, build, deploy cho từng project nhỏ trong monorepo bằng công cụ Nx.
Hạn chế CI của Nx
Đối với CI, Nx hướng developer sử dụng hệ sinh thái của Nx Cloud, nhưng nhóm đã tự xây dựng một giải pháp cache riêng cho Github Actions, KevinNitroG/nx-cache-action [50]. Script hoạt động theo cơ chế cache từng project thay vì toàn bộ cache lớn của cả workspace.
Script được lấy cảm hứng từ raegen/nx [51]7, cache tại project level. Nhưng cơ chế hoạt động khác biệt:
actions/toolkit/cache [53].Vì Nx sẽ lưu cache từng task ước chừng khoảng một tháng kể từ lần cuối sử dụng mới được xoá. Đối với dự án khi không sử dụng KevinNitroG/nx-cache-action, cache được lưu theo dạng toàn bộ project task cache, có thể lên đến khoảng 5GB trong mỗi lần lưu cache. Và hiển nhiên rằng, mỗi lần chạy thay đổi là một cache mới được tạo ra. Nếu có 10 commit được tạo ra và thay đổi source code, thì sẽ có 10 cache được tạo ra, tổng dung lượng cache có thể lên đến 50GB, vượt qua mức 10GB giới hạn của Github Actions cache. Hơn nữa, vẫn cần chừa dung lượng để cache node modules, go packages, system dependencies, v.v…, nên việc cache toàn bộ project task cache là không tối ưu.
Khi sử dụng KevinNitroG/nx-cache-action, cache được lưu theo dạng từng project nhỏ, mỗi project task có thể chỉ khoảng vài trăm KB, đến hơn 10MB tùy vào task, và chỉ khi nào project đó thay đổi source code mới tạo cache mới. Điều này giúp tối ưu hóa dung lượng cache, tránh vượt quá giới hạn của Github Actions, và tăng hiệu quả cache hit.
Script có một nhược điểm là phải thông qua actions/toolkit/cache để download cache về local, và pipe cache vào lại Nx process. Có thể hiểu là cache đã được tải xuống nhưng lưu ở một nơi khác, và phải truyền vào Nx process thông qua HTTP request một lần nữa. Nhưng đây là cách dễ dàng nhất, không phải giao tiếp trực tiếp với Github Actions cache Rest API phức tạp hơn.
Contract First API development
Dự án áp dụng contract-first approach cho API development, đặc biệt với OpenAPI specification cho HTTP API được deploy và preview bằng Scalar, giao diện hiện đại, hỗ trợ mock API khi chưa có backend implementation, giúp frontend và backend phát triển song song hiệu quả.
SQLC Dynamic filter
SQLC là một công cụ code generation cho SQL queries, giúp tạo ra code type-safe cho database access (Phần 2.7.2). Tuy nhiên, SQLC không hỗ trợ dynamic query, cũng không phải là một ORM hay query builder, mà chỉ đơn thuần là code generator cho SQL queries đã viết sẵn. Điều này gây khó khăn khi cần sinh dynamic WHERE conditions, ví dụ như khi có nhiều optional filters khác nhau. Trước đây (cũng là phiên bản chính thức SQLC) phải sử dụng syntax WHERE và CASE ... WHEN ở dưới tầng SQL, ảnh hưởng đến hiệu năng dưới tầng database. Đặc biệt với FOR UPDATE queries, bắt buộc phải duplicate query cho từng trường hợp, dẫn đến code duplication và khó maintain.
Ví dụ đây là một query SQLC tiêu chuẩn cho việc lấy notes với nhiều optional filters:
Custom plugin vtuanjs/sqlc-gen-go [54] (được phát triển bởi anh Nguyễn Văn Tuấn) hỗ trợ dynamic filter queries, giải quyết vấn đề không thể sinh dynamic WHERE conditions trong SQLC tiêu chuẩn, cũng như hỗ trợ dynamic FOR UPDATE. Plugin hoạt động bằng cách parse SQL query đã viết sẵn, nhận diện các phần có thể trở thành dynamic filter bằng các comment, và sinh ra code Go tương ứng để xây dựng dynamic query tại runtime.
Dưới đây là ví dụ SQL query khi sử dụng với vtuanjs/sqlc-gen-go:
vtuanjs/sqlc-gen-go
Observability
Dự án đã thiết lập một Observability Stack cơ bản, làm tiền đề cho việc phát triển ổn định về sau.
Trong quá trình thực hiện đề tài, hệ thống nhận được nhiều thuận lợi trong khâu nghiên cứu, phát triển và triển khai, tạo điều kiện cho việc hoàn thiện các chức năng và đạt được mục tiêu đề ra.
Dự án cũng gặp phải nhiều khó khăn và thách thức trong quá trình thực hiện, đòi hỏi sự nỗ lực và kiên trì từ nhóm phát triển để vượt qua và hoàn thiện hệ thống.
TrshPuppy/obsidian-notes [55] không được chuẩn xác hoàn toàn. Vì cơ chế parse bằng text, không phải cây ngôn ngữ, nên các code block chứa comment như ký tự # hay shebang #! bị parse thành tag. Đồng thời, hệ thống không support nested tags như obsidian. Cũng như công việc chuyển hoá và seed vào service phức tạp (markdown → custom markdown/HTML → BlockNote/yjs binary).ThreeDotsLabs/Watermill nói riêng, và kiến trúc Event Drive Architecture nói chung, đòi hỏi phải hiểu rõ để thiết kế và triển khai đúng cách.ThreeDotsLabs/watermill-kafka sử dụng IBM/sarama không hỗ trợ subscribe regex topic, phải iterate toàn bộ topic bằng tay để subscribe.vtuanjs/sqlc-gen-go là một plugin mới, chưa được sử dụng rộng rãi, tính production ready chưa được kiểm chứng.rustfs/rustfs/issues/2587 - set server domains make RustFS cannot start [56]8.shadcn-ui/ui/issues/355 [57], nhưng vẫn phải dành thời gian để chỉnh sửa lại vì vẫn xảy ra một số lỗi.Hệ thống ghi chú thông minh đã đạt được nhiều ưu điểm đáng kể.
note có business phức tạp nhất.note, authorization) đều có health check endpoint, đảm bảo tính sẵn sàng, tin cậy cao cho hệ thống.Tuy đã đạt được nhiều ưu điểm, hệ thống cũng còn tồn tại một số nhược điểm cần được cải thiện trong tương lai.
note có retry và dead letter queue.Dự án đã đạt được mục tiêu đề ra, tuy nhiên vẫn còn nhiều tiềm năng để phát triển và cải thiện trong tương lai. Dưới đây là một số hướng phát triển có thể xem xét trong tương lai để nâng cao tính hoàn thiện và khả năng ứng dụng thực tế của hệ thống.
@blocknote/xl-ai [58] sử dụng thư viện ai đến từ Vercel, hybrid search nhờ vào tính năng hỗ trợ bởi Meilisearch [59], các tool thông qua API của hệ thống.Dự án đã đạt được mục tiêu đề ra và mang lại nhiều bài học quý giá cho nhóm phát triển. Hệ thống “Notopia - Ứng dụng ghi chú thông minh hỗ trợ quản lý tri thức bằng biểu đồ quan hệ” không chỉ là sản phẩm hoàn chỉnh mà còn là nền tảng để tiếp tục nghiên cứu và phát triển trong tương lai.
| Ký hiệu | Ý nghĩa |
| ABAC | Attribute-Based Access Control |
| ACL | Access Control List |
| API | Application Programming Interface |
| CDC | Change Data Capture |
| CI/CD | Continuous Integration/Continuous Deployment |
| CLI | Command-Line Interface |
| CRDT | Conflict-free Replicated Data Type |
| CSS | Cascading Style Sheets |
| DOM | Document Object Model |
| DX | Developer Experience |
| HTTP/2 | Hypertext Transfer Protocol Version 2 |
| ISR | Incremental Static Regeneration |
| JSX | JavaScript XML Syntax Extension |
| Nx | Monorepo Framework |
| OAuth2 | OAuth 2.0 Authorization Framework |
| OIDC | OpenID Connect |
| ORM | Object-Relational Mapping |
| OTLP | OpenTelemetry Protocol |
| OpenAPI | Open API Specification |
| OpenTelemetry | Open Telemetry Framework |
| Protobuf | Protocol Buffers |
| RBAC | Role-Based Access Control |
| REST | Representational State Transfer |
| RPC | Remote Procedure Call |
| SEO | Search Engine Optimization |
| SQL | Structured Query Language |
| SQLC | SQL Compiler for Type-Safe Code Generation |
| SSE | Server-Sent Events |
| SSG | Static Site Generation |
| SSO | Single Sign-On |
| SSR | Server-Side Rendering |
| TOML | Tom’s Obvious, Minimal Language |
| TSX | TypeScript XML Syntax Extension |
| UML | Unified Modeling Language |
| gRPC | Google Remote Procedure Call |
raegen/nx không được hỗ trợ chính thức bởi Nx, đã deprecatedrustfs/rustfs/issues/2587 do thành viên nhóm phát hiện