--------------- <> -----------------
--- KHOA HỌC - CÔNG NGHỆ - GIÁO DỤC - VIỆC LÀM ---
--- Học để đi cùng bà con trên thế giới ---

Tìm kiếm trong Blog

CNPM (5) - Thiết kế kiến trúc phần mềm

Bài trước: CNPM (4) - Lấy và phân tích yêu cầu
-----

Bài 5: Thiết kế kiến trúc phần mềm

5.1 Kiến trúc phần mềm là gì?

Kiến trúc phần mềm là bản thiết kế tổng thể của một hệ thống, giúp chúng ta hiểu rõ các bộ phận cấu thành, cách chúng kết nối và các quy tắc chi phối toàn bộ cấu trúc. Nó giống như bản vẽ kỹ thuật của một tòa nhà lớn, như: tòa nhà có bao nhiêu tầng, vật liệu chính là gì, hệ thống điện nước chạy như thế nào; mô tả các bộ phận (móng, cột, tường, mái), cách chúng được liên kết (hệ thống ống nước, dây điện) và mục tiêu chất lượng (chịu được động đất, tiết kiệm năng lượng).

Kiến trúc phần mềm trả lời câu hỏi: "Chúng ta nên xây dựng hệ thống này như thế nào?" thay vì "Hệ thống này làm được gì?" (câu hỏi của URS).

Kiến trúc phần mềm mô tả ba yếu tố chính của một hệ thống:

- Cấu trúc (structure): Hệ thống được chia thành các thành phần (components) hoặc mô-đun nhỏ hơn (ví dụ: mô-đun quản lý người dùng, mô-đun xử lý thanh toán)

- Tương tác (interaction): Các thành phần kết nối và trao đổi dữ liệu với nhau bằng cách nào (ví dụ: dùng API)

- Yếu tố chất lượng (quality attributes / NFR-Non-Functional Requirements): Đây là các yêu cầu phi chức năng mà kiến trúc phải có. Ví dụ: tốc độ, bảo mật, khả năng mở rộng, độ tin cậy

Vai trò quan trọng của Kiến trúc phần mềm:

- Tính ổn định: Đảm bảo hệ thống vững chắc, không bị sụp đổ khi lượng người dùng tăng lên

- Tính dễ hiểu: Giúp các lập trình viên “mới” hoặc các nhóm khác nhau dễ dàng nắm bắt được cách hệ thống hoạt động, từ đó đẩy nhanh quá trình phát triển, nâng cấp và sửa lỗi

- Tính mở rộng: Cho phép thêm tính năng hoặc tăng khả năng chịu tải một cách dễ dàng mà không cần phải phát triển lại toàn bộ hệ thống

5.2 Một số mô hình Kiến trúc phần mềm phổ biến

Có nhiều mô hình Kiến trúc phần mềm khác nhau, mỗi mô hình phù hợp với những yêu cầu và quy mô dự án cụ thể.

Bảng sau tóm tắt một số mô hình Kiến trúc phần mềm phổ biến:

Mô hình

Mô tả tóm tắt

Kiến trúc nguyên khối (monolithic)

Toàn bộ hệ thống được xây dựng như một đơn vị duy nhất, tất cả các chức năng đều nằm chung một khối mã và chia sẻ một cơ sở dữ liệu. (Giống như một căn nhà nhỏ, mọi thứ đều nằm chung một không gian)

Kiến trúc N-tầng (N-tiers)

Phân chia hệ thống thành các lớp (tầng) riêng biệt (thường là 3 tầng: Giao diện người dùng, Logic nghiệp vụ, Truy cập dữ liệu). Mỗi tầng chỉ giao tiếp với tầng cạnh nó. (Giống như một tòa nhà có các tầng chức năng rõ ràng)

Kiến trúc vi dịch vụ (Microservices)

(kiến trúc phân tán)

Hệ thống được chia thành nhiều dịch vụ nhỏ độc lập (microservices), mỗi dịch vụ chạy tiến trình riêng, có thể có cơ sở dữ liệu riêng, và giao tiếp với nhau qua mạng (thường là API). (Giống như một khu phức hợp với nhiều tòa nhà chuyên biệt, kết nối bằng hệ thống đường xá riêng)

Chúng ta cùng tìm hiểu sơ lược 3 mô hình kiến trúc phần mềm ở trên.

5.3 Kiến trúc nguyên khối

Kiến trúc nguyên khối là mô hình truyền thống, trong đó toàn bộ ứng dụng được xây dựng và triển khai dưới dạng một khối duy nhất. Tất cả các thành phần của ứng dụng như giao diện người dùng, logic nghiệp vụ và truy cập dữ liệu đều được tích hợp chặt chẽ trong cùng một ứng dụng.

Xem hình minh họa:

Trong đó:

- Giao diện người dùng (user interface)

- Logic nghiệp vụ (business layer)

- Truy cập dữ liệu (data interface)

Xem hình ảnh này để bạn dễ tưởng tượng hơn về kiến trúc monolithic:

Ưu điểm:

- Quá trình phát triển, triển khai đơn giản: Do tất cả nằm trong một khối duy nhất, việc phát triển, thử nghiệm và triển khai sẽ đơn giản hơn.

- Hiệu suất cao: Do không có giao tiếp liên bộ phận, ứng dụng thường có hiệu suất cao hơn.

Nhược điểm:

- Khó bảo trì và mở rộng: Khi ứng dụng phát triển lớn hơn, việc bảo trì và thêm tính năng mới trở nên phức tạp

- Khả năng chịu lỗi kém: Một lỗi nhỏ có thể làm gián đoạn toàn bộ hệ thống

Khi nào thì nên sử dụng mô hình nguyên khối:

- Dự án nhỏ, cần triển khai nhanh

- Đội ngũ phát triển ít thành viên

- Tài nguyên, máy móc ít

- Việc giao tiếp giữa các thành phần của ứng dụng cần hiệu suất cao

Ví dụ về các ứng dụng theo kiến trúc nguyên khối:

- Hầu hết các ứng dụng desktop truyền thống đều được thiết kế theo kiểu kiến trúc nguyên khối. Kiến trúc nguyên khối rất phù hợp với môi trường desktop vì toàn bộ ứng dụng chạy cục bộ trên một máy tính duy nhất.

- Hệ quản trị nội dung (CMS) cũ (wordpress), ứng dụng thương mại điện tử nhỏ

5.4 Kiến trúc N-tầng

Mô hình N-tầng (N-tiers) phân chia ứng dụng thành các tầng độc lập, chạy trên các máy chủ hoặc dịch vụ khác nhau. Sự phân tách này đạt được thông qua việc giới hạn sự giao tiếp: mỗi tầng chỉ được phép giao tiếp với tầng liền kề nó.

Mô hình N-tầng phổ biến nhất là Kiến trúc 3-tầng (3-tier architecture).

Kiến trúc 3-tầng

Kiến trúc 3-tầng là một mô hình kiến trúc phần mềm phổ biến. Nó được thiết kế để phân chia ứng dụng thành ba đơn vị logic và vật lý riêng biệt, giúp tăng tính linh hoạt, khả năng mở rộng, và bảo mật.

Kiến trúc 3-tầng được hình thành bằng cách tách ba chức năng chính của ứng dụng thành các tầng (tier) độc lập, thường chạy trên các máy chủ vật lý hoặc logic khác nhau. 

Ba tầng đó là: 

- Trình bày (presentation)

- Logic nghiệp vụ (business logic)

- Dữ liệu (data)

Xem hình minh họa:

Cụ thể hơn:

Tên lớp (tier)

Vai trò chính

Nhiệm vụ

Ví dụ công nghệ

Trình bày

(Presentation)

Giao diện người dùng

Xử lý việc hiển thị thông tin, nhận yêu cầu từ người dùng (nhập liệu, bấm chuột) và chuyển tiếp các yêu cầu xuống tầng Logic nghiệp vụ.

Frontend framework (React, Angular, Vue),

HTML/CSS, Ứng dụng di động

Logic nghiệp vụ

(Business logic/Application)

Logic cốt lõi

Chứa các quy tắc nghiệp vụ, logic xử lý dữ liệu và điều phối các tác vụ. Đây là “bộ não” của ứng dụng, nơi xử lý các yêu cầu từ tầng Trình bày trước khi tương tác với dữ liệu.

Backend framework (Spring Boot, .NET Core, Node.js/Express), REST APIs

Dữ liệu 

(Data)

Lưu trữ và quản lý dữ liệu

Chứa cơ sở dữ liệu (database management system - DBMS), lưu trữ, quản lý, truy xuất và đảm bảo tính toàn vẹn của dữ liệu

Cơ sở dữ liệu (SQL Server, PostgreSQL, MongoDB), Hệ thống lưu trữ tập tin

Ưu điểm:

- Khả năng tái sử dụng: Logic nghiệp vụ nằm ở tầng giữa, độc lập với tầng trình bày. Vì vậy, một logic nghiệp vụ có thể được sử dụng bởi nhiều giao diện khác nhau. Ví dụ: một API xử lý thanh toán có thể được gọi từ cả ứng dụng web và ứng dụng di động

- Khả năng mở rộng: Đây là ưu điểm lớn nhất so với kiến trúc nguyên khối. Bạn có thể mở rộng độc lập theo từng tầng. Ví dụ: thêm máy chủ cho tầng Logic nghiệp vụ khi có nhiều yêu cầu API hơn, mà không cần thêm máy chủ cho tầng Dữ liệu

-  Có tính bảo mật cao: Tầng Dữ liệu được cô lập, chỉ giao tiếp với tầng Logic nghiệp vụ, mà không giao tiếp với người dùng cuối. Điều này giúp bảo vệ cơ sở dữ liệu khỏi các truy cập trái phép từ bên ngoài

- Dễ phát triển và bảo trì: Vì mỗi tầng có trách nhiệm rõ ràng, các lập trình viên có thể tập trung vào một tầng cụ thể mà không làm ảnh hưởng đến các tầng khác. Đội Frontend có thể làm việc song song với đội Backend, chỉ cần tuân thủ giao diện API

Nhược điểm:

- Phức tạp: Việc thiết lập và quản lý kiến trúc phân tán nhiều tầng phức tạp hơn so với kiến trúc nguyên khối

- Độ trễ: Việc truyền dữ liệu qua nhiều tầng (qua mạng) có thể làm tăng độ trễ (latency) so với việc gọi hàm cục bộ

- Chi phí: Việc duy trì nhiều máy chủ/dịch vụ vật lý hoặc máy ảo cho mỗi tầng sẽ tốn kém hơn

Khi nào thì nên sử dụng kiến trúc 3-tầng:

- Khi ứng dụng của bạn phức tạp, dự kiến phát triển lớn trong tương lai, cần khả năng mở rộng cao và bảo mật dữ liệu được ưu tiên

5.5 Kiến trúc vi dịch vụ

Kiến trúc vi dịch vụ (microservices) là một phương pháp thiết kế phần mềm, trong đó một ứng dụng lớn được chia thành một tập hợp các dịch vụ nhỏ, độc lập. Nó là một kiến trúc phần mềm phân tán.

Một số đặc điểm:

- Tính độc lập: Mỗi dịch vụ (service) chạy trong tiến trình (process) riêng biệt của nó

- Chức năng chuyên biệt: Mỗi dịch vụ tập trung vào việc thực hiện một chức năng nghiệp vụ cụ thể (ví dụ: dịch vụ quản lý người dùng, dịch vụ xử lý thanh toán, dịch vụ kho hàng)

- Giao tiếp: Các dịch vụ giao tiếp với nhau qua mạng bằng các giao thức nhẹ, phổ biến nhất là RESTful API hoặc message broker (hàng đợi tin nhắn)

- Công nghệ đa dạng (polyglot): Các dịch vụ khác nhau có thể được xây dựng bằng các ngôn ngữ lập trình, framework và thậm chí là cơ sở dữ liệu riêng (independent database) phù hợp nhất với yêu cầu của dịch vụ đó

Mô hình này trái ngược hoàn toàn với kiến trúc nguyên khối (monolithic), nơi mọi thứ được đóng gói thành một đơn vị duy nhất.

Xem hình minh họa.

Ưu điểm:

- Khả năng mở rộng độc lập: Có thể mở rộng từng dịch vụ riêng biệt dựa trên nhu cầu tải thực tế, giúp tối ưu hóa tài nguyên (ví dụ: chỉ cần thêm server cho dịch vụ giỏ hàng mà không cần thêm server cho dịch vụ tài khoản)

- Linh hoạt công nghệ: Cho phép các nhóm sử dụng công nghệ (tech stack) tốt nhất cho từng dịch vụ cụ thể (polyglot programming)

- Dễ dàng bảo trì và phát triển: Mã nguồn của mỗi dịch vụ nhỏ và dễ hiểu hơn. Các nhóm phát triển có thể làm việc, triển khai và cập nhật dịch vụ của họ độc lập với các dịch vụ khác (decoupling)

- Khả năng chịu lỗi cao: Nếu một dịch vụ bị lỗi, các dịch vụ khác vẫn có thể tiếp tục hoạt động (fault isolation). Hệ thống không bị sập toàn bộ như trong monolithic

- Triển khai liên tục (CI/CD): Dễ dàng áp dụng DevOps và CI/CD vì chỉ cần triển khai lại các dịch vụ nhỏ đã thay đổi, không cần triển khai toàn bộ ứng dụng lớn

Nhược điểm:

- Phức tạp về vận hành: Việc quản lý, giám sát (monitoring) và gỡ lỗi (debugging) một hệ thống gồm hàng chục hoặc hàng trăm dịch vụ phân tán phức tạp hơn nhiều

- Độ trễ mạng (latency): Các dịch vụ giao tiếp qua mạng (API call) thay vì gọi hàm cục bộ, dẫn đến độ trễ cao hơn so với monolithic

- Quản lý dữ liệu phân tán: Xử lý giao dịch trải dài qua nhiều cơ sở dữ liệu (distributed transactions) rất phức tạp và cần các mẫu thiết kế như Saga

- Chi phí cơ sở hạ tầng: Cần nhiều tài nguyên hơn cho việc chạy nhiều tiến trình, nhiều cơ sở dữ liệu và các công cụ quản lý phức tạp (kubernetes, service mesh, API gateway)

- Kiểm thử tích hợp: Việc đảm bảo tất cả các dịch vụ phối hợp nhịp nhàng với nhau khó khăn hơn so với kiểm thử end-to-end trong monolithic

Khi nào thì nên sử dụng mô hình microservices:

Microservices không phải là giải pháp cho mọi vấn đề. Nó đặc biệt phù hợp khi dự án đạt đến một mức độ phức tạp và quy mô nhất định:

- Hệ thống phức tạp và quy mô lớn: Khi ứng dụng có nhiều chức năng nghiệp vụ riêng biệt và mã nguồn (codebase) trở nên quá cồng kềnh

- Yêu cầu khả năng mở rộng cao và độc lập: Khi một số chức năng (ví dụ: tìm kiếm, streaming) có nhu cầu tải (load) cao hơn nhiều so với các chức năng khác

- Tổ chức lớn với nhiều đội (team autonomy): Khi các nhóm khác nhau có thể sở hữu, phát triển và triển khai dịch vụ của riêng họ mà không bị phụ thuộc vào nhau

- Cần triển khai nhanh chóng và thường xuyên: Khi yêu cầu cập nhật, thêm tính năng mới liên tục và cần thời gian ngưng hệ thống (downtime) tối thiểu

- Có kinh nghiệm vận hành (DevOps maturity): Đội ngũ phát triển đã có kinh nghiệm với các công cụ như Docker, Kubernetes, CI/CD, và các hệ thống giám sát phân tán

5.6 Bài tập

Bài 5a. Dựa trên URS ứng dụng của bạn (nhóm), hãy phân tích và thiết kế theo kiến trúc 3-tầng. Bạn có thể tự phân tích, thiết kế hoặc nhờ sự hỗ trợ của chatbot.

Bài 5b. Dựa trên URS ứng dụng của bạn (nhóm), hãy phân tích và thiết kế theo kiến trúc vi dịch vụ. Bạn có thể tự phân tích, thiết kế hoặc nhờ sự hỗ trợ của chatbot.



-----
Bài sau:

Git thực hành (6) - Nguyên tắc làm việc của Git

Bài trước: Git thực hành (5) - Các khu vực làm việc của Git (tiếp)
-----

6. Nguyên tắc làm việc của Git

Tới bài học này, bạn đã được thực hành, trải nghiệm với Git, như:

- Nhúng Git vào thư mục dự án bằng lệnh git init, lệnh này sẽ tạo ra Thư mục làm việc (working directory, working tree), và Kho chứa (repository)

- Đánh dấu tập tin/thư mục sẽ được commit, thông tin đánh dấu này được ghi trong Khu tạm (staging area, tập tin .git\INDEX)

- Tạo ra 1 phiên bản của dự án (với các thông tin đã được đánh dấu trong .git\INDEX) và lưu phiên bản vào Kho chứa .git (lệnh git commit)

Trong phần này, chúng ta cùng tìm hiểu kỹ hơn về nguyên tắc làm việc của Git.

6.1 Chụp ảnh

Thông thường, khi bạn cần lưu trữ các phiên bản khác nhau của mỗi tập tin bạn sẽ làm như thế nào? 

Như trong phần “quản lý phiên bản cục bộ” đã đề cập: khi một tập tin có thay đổi và cần lưu thành một phiên bản khác, bạn sẽ lưu nó thành một tập tin mới với tên mới. Nghĩa là tập tin mới có thay đổi một lượng delta nào đó so với tập tin ban đầu. Để tối ưu việc lưu trữ, thực tế chỉ cần lưu tập tin gốc, cộng thêm thông tin của mỗi lần thay đổi (delta).

Khi đó, thông tin một phiên bản của dự án sẽ gồm các tập tin gốc, cùng với các thay đổi của từng tập tin. Đây chính là cách tiếp cận của đa số các hệ thống quản lý phiên bản, như: CVS, Subversion (SVN), Perforce, Bazaar. Xem hình minh họa.

Với Git thì khác, để lưu lại một phiên bản của dự án, Git sẽ chụp ảnh (snapshot) toàn bộ các tập tin của dự án. Tập tin nào có thay đổi thì “chụp” (tạo ra một ảnh mới cho tập tin); tập tin nào không thay đổi thì không “chụp”, mà chỉ tạo một liên kết, trỏ về phiên bản trước của nó. Xem hình minh họa.

Có thể thấy, dữ liệu trong Kho chứa của Git gồm một loạt các “ảnh chụp” của hệ thống tập tin đơn giản (miniature filesystem). Mỗi “ảnh chụp” là một phiên bản của dự án, tương đương với một “commit” trong hệ thống Git.

6.2 Một số đặc điểm của Git

Phần lớn các thao tác với Git được thực hiện trên máy cục bộ

Khi làm việc với Git, hầu hết các thông tin và tài nguyên đều có sẵn trên máy cục bộ, do vậy bạn không phải kết nối tới server ở xa.

Ví dụ, nếu bạn muốn xem lại thông tin hoặc nội dung các phiên bản trước của dự án, thì bạn chỉ việc xem trong cơ sở dữ liệu của Git tại máy cục bộ. Hoặc nếu bạn muốn lưu lại phiên bản hiện thời của dự án (commit), bạn cũng thực hiện trên máy cục bộ. Chỉ khi nào bạn muốn chia sẻ phiên bản mới cho thành viên khác thì mới cần tới mạng để tải phiên bản mới lên server (push).

Git đảm bảo tính toàn vẹn

Mọi thứ trong Git (ví dụ tập tin) đều được tạo mã kiểm tra (checksum) trước khi lưu vào cơ sở dữ liệu Git. Git tạo ra mã kiểm tra bằng cách chạy một thuật toán (SHA-1) dựa vào nội dung của tập tin.

Kết quả của thuật toán SHA-1 sẽ tạo ra một số hexa, tạm gọi là mã SHA-1, có chiều dài 40 ký số. Ví dụ, một mã SHA-1: 24b9da6552252987aa493b52f8696cd6d3b00373

Git dùng mã SHA-1 của tập tin để đặt tên mới cho nó và thao tác với tên mới này chứ không không quan tâm đến tên thật của tập tin.

Giả sử bằng cách nào đó, nội dung của một tập tin bị thay đổi, khi Git mở tập tin, nó sẽ tính lại mã SHA-1, so với mã SHA-1 đã được lưu trước đây, nếu hai mã này không khớp, Git sẽ phát hiện ra là nội dung của tập tin đã bị thay đổi.

Như vậy không thể có tình trạng nội dung của tập tin bị thay đổi mà Git không biết, cơ chế này đảm bảo dữ liệu do Git quản lý luôn có tính toàn vẹn.

Git chỉ “thêm” dữ liệu

Trong quá trình làm việc với Git, hầu như mọi thao tác đều được Git ghi vào cơ sở dữ liệu của nó. Bạn có thể bị mất hoặc bị xáo trộn thông tin nếu chưa lưu phiên bản (commit), nếu bạn đã lưu phiên bản thì rất khó bị mất dữ liệu, đặc biệt nếu bạn thường xuyên đẩy cơ sở dữ liệu Git lên server (push) hoặc tới kho chứa khác.

Cách làm việc theo kiểu luôn “thêm” dữ liệu (không xóa) tạo ra cảm giác yên tâm cho người sử dụng.

6.3 Ba trạng thái quan trọng của tập tin

Khi một tập tin (thư mục) đã được Git quản lý, nó có thể ở các trạng thái sau: modified, staged, committed.

- modified: tập tin đã bị thay đổi (nội dung hoặc tên), nhưng chưa được được lưu vào Kho chứa (chưa commit)

- staged: tập tin đã bị thay đổi, đã được đánh dấu vào trong Khu tạm

- committed: là tập tin đã bị thay đổi, đã được đánh dấu và đã được lưu vào Kho chứa

Ba trạng thái của tập tin tương ứng với 3 vùng làm việc của Git.

- Tập tin đã bị thay đổi, mà chưa commit sẽ nằm trong Thư mục làm việc. Như đã biết, Thư mục làm việc là bản sao một phiên bản của dự án, nó được lấy ra từ Kho chứa (.git). Đây là thư mục làm việc của lập trình viên, họ thao tác trên nó như một thư mục bình thường

- Tập tin đã bị thay đổi, được đánh dấu trong Khu tạm. Khu tạm là một tập tin (index) nằm trong thư mục .git, chứa thông tin về những gì sẽ được lưu phiên bản (commit) trong lần tới

- Tập tin tin đã bị thay đổi, đã được đánh dấu và đã được lưu vào Kho chứa. Kho chứa là thư mục .git, là nơi Git lưu trữ “siêu dữ kiện” (metadata), và cơ sở dữ liệu của Git (không phải cơ sở dữ liệu của ứng dụng đang được phát triển). Đây là phần quan trọng nhất của Git, nó là thành phần được sao lưu (copy) về khi bạn nhân bản một kho chứa từ máy khác (clone)

Xem hình minh họa.


Về cơ bản, tiến trình công việc (workflow) khi thao tác với Git gồm:

- Bạn thao tác (tạo mới, thêm dữ liệu, sửa, xóa) với tập tin/thư mục trong Thư mục làm việc (working directory)

- Đưa vào Khu tạm (staging area) các tập tin/thư mục mà bạn muốn lưu trữ vào Kho chứa (.git directory)

- Lưu phiên bản cho các nội dung đã được đánh dấu trong Khu tạm (staging area), cụ thể là thực hiện lệnh commit. Các tập tin/thư mục đã được đánh dấu trong Khu tạm sẽ được lưu trữ an toàn vào .git

Tóm lại: một tập tin nằm trong .git được xem là đã được committed. Nếu tập tin đã bị thay đổi và đã được đưa vào staging area được xem là staged. Và nếu một tập tin đã bị thay đổi tính từ thời điểm được sinh ra từ .git (check out) và chưa đưa vào staging area thì được xem là modified.

6.4 Bài tập và câu hỏi

Bài tập 6a. Quy trình làm việc và các trạng thái của tập tin trong Git.

Mục tiêu bài tập

- Thực hành quy trình: Thư mục làm việc (Working directory) > Khu tạm (Staging area) > Kho chứa (Repository)

- Phân biệt các trạng thái của tập tin: Chưa được theo dõi (Untracked), Đã được theo dõi (Tracked/Staged), và Đã lưu (Committed)

- Sử dụng thành thạo các lệnh: git init, git status, git add, git commit và git rm --cached

Các bước thực hiện

Bước 1: Khởi tạo dự án và Kho chứa

- Tạo một thư mục mới tên là LabGit trên máy tính

- Mở cửa sổ dòng lệnh (CMD), di chuyển vào thư mục LabGit

- Nhập lệnh để biến thư mục này thành một kho chứa Git

git init

- Kết quả: Xuất hiện thư mục ẩn .git. Đây chính là Kho chứa

Bước 2: Thao tác tại Thư mục làm việc (Trạng thái Untracked)

- Tạo một tập tin mới tên là readme.txt với nội dung bất kỳ

- Dùng lệnh kiểm tra trạng thái:

git status

- Quan sát: Git sẽ báo tập tin readme.txt ở trạng thái Untracked files (chưa được theo dõi) vì nó mới chỉ nằm ở Thư mục làm việc.

Bước 3: Đưa tập tin vào Khu tạm (Trạng thái Staged)

- Sử dụng lệnh sau để đánh dấu tập tin chuẩn bị lưu phiên bản:

git add readme.txt

- Kiểm tra lại trạng thái bằng git status.

- Quan sát: Tập tin bây giờ nằm trong mục Changes to be committed. Trạng thái này gọi là Staged (đã nằm trong Khu tạm/index).

Bước 4: Trải nghiệm việc gỡ bỏ khỏi Khu tạm

- Giả sử bạn chưa muốn lưu tập tin này vào kho, hãy đưa nó quay lại trạng thái chưa theo dõi:

git rm --cached readme.txt

- Kiểm tra git status. Tập tin sẽ quay lại trạng thái Untracked.

- Thực hiện git add readme.txt một lần nữa để tiếp tục bài tập

Bước 5: Lưu phiên bản vào Kho chứa (Trạng thái Committed)

- Thực hiện lệnh commit để lưu vĩnh viễn thay đổi vào Kho chứa:

git commit -m "Khoi tao du an voi tap tin readme"

- Kiểm tra git status

- Quan sát: Git báo "nothing to commit, working tree clean". Tập tin đã ở trạng thái Committed (đã lưu vào Kho chứa).

Bài tập 6b. Rèn kỹ năng phân tích

Ở phần này, thay vì chỉ làm theo hướng dẫn, bạn phải quan sát sự khác biệt giữa các trạng thái.

Tình huống 1: Chỉnh sửa tập tin đã Commit

- Mở lại tập tin readme.txt, thêm một dòng nội dung mới và lưu lại

- Chạy lệnh git status

- Câu hỏi phân tích: Tại sao tập tin này lại xuất hiện ở mục "Changes not staged for commit" mà không phải "Untracked"? Sự khác biệt giữa một tập mới tạo và một tập tin đã từng được commit nhưng bị sửa đổi là gì?

Tình huống 2: So sánh nội dung (git diff)

- Sử dụng lệnh git diff để xem sự thay đổi

- Câu hỏi phân tích: Lệnh này đang so sánh nội dung giữa khu vực nào với khu vực nào? (Thư mục làm việc với Khu tạm hay Thư mục làm việc với Kho chứa?).

Bài tập 6c. Rèn kỹ năng tổng hợp: "Thiết kế quy trình xử lý sự cố"

Bạn đóng vai trò là một trưởng nhóm kỹ thuật để đưa ra giải pháp cho các tình huống sau.

Tình huống 1: "Lỡ tay đưa nhầm tập tin quan trọng"

- Giả lập: Bạn tạo một tập tin tên là matkhau.txt (giả sử chứa mật khẩu cá nhân). Bạn vô tình gõ git add . (đưa tất cả vào khu tạm)

- Yêu cầu: Hãy tìm cách đưa tập tin matkhau.txt ra khỏi Khu tạm mà không được xóa tập tin đó khỏi Thư mục làm việc

- Tư duy tổng hợp: Viết ra quy trình các lệnh cần dùng và giải thích tại sao chọn cách đó thay vì xóa tập tin đi làm lại

Tình huống 2: "Khôi phục trạng thái cũ"

- Giả lập: Sau khi sửa tập tin readme.txt ở Bài tập 6.b (Tình huống 1), bạn nhận ra nội dung mới vừa sửa bị sai hoàn toàn và muốn quay về nội dung của lần Commit gần nhất

- Yêu cầu: Tìm lệnh để "đè" nội dung từ Kho chứa ngược trở lại Thư mục làm việc để xóa bỏ những sửa đổi sai lầm

- Tư duy tổng hợp: Kết hợp kiến thức về 3 khu vực để giải thích: Tại sao Kho chứa (Repository) được coi là “nguồn tin cậy” khi Thư mục làm việc xảy ra lỗi?

Bài tập 6d. Rèn tư duy suy luận. Dựa trên kiến thức về 3 khu vực (Working Directory, Staging Area, Repository). Giải thích:

- Nếu người A sửa tập tin nhưng chưa git add, người B có thấy được thay đổi đó trong Kho chứa không?

- Tại sao Git lại cần "Khu tạm" (Staging Area) mà không cho phép Commit thẳng từ Thư mục làm việc?

Bài sau: