{
    "version": "https://jsonfeed.org/version/1",
    "title": "Đào Anh Thành",
    "home_page_url": "https://daoanhthanh.vn/blog",
    "description": "Đào Anh Thành Blog",
    "items": [
        {
            "id": "https://daoanhthanh.vn/blog/mot-cach-khac-de-su-dung-git-log",
            "content_html": "<p>Tôi rất thích sử dụng git cli cho công việc. Thứ nhất, GUI chỉ dành cho mấy tay gà mờ 🐧 đùa hoy =)), và thứ 2 việc sử dụng command line sẽ nhanh và \"thuận tự nhiên\" hơn cho các lập trình viên trong quá trình code.</p>\n<!-- -->\n<p>Chia sẻ một chút: tôi đã từng sử dụng rất nhiều Git GUI client như <a href=\"https://www.sourcetreeapp.com/\" target=\"_blank\" rel=\"noopener noreferrer\"><em>SourceTree</em></a>, <a href=\"https://git-scm.com/tools/guis\" target=\"_blank\" rel=\"noopener noreferrer\"><em>Git GUI</em></a>, <a href=\"https://www.gitkraken.com/\" target=\"_blank\" rel=\"noopener noreferrer\"><em>GitKraken</em></a>, <a href=\"https://desktop.github.com/download/\" target=\"_blank\" rel=\"noopener noreferrer\"><em>GitHub Desktop</em></a>, <a href=\"https://www.sublimemerge.com/\" target=\"_blank\" rel=\"noopener noreferrer\"><em>Sublime Merge</em></a>, cũng như các giao diện git tích hợp trong IDE. Tuy nhiên, thực tế là tôi chỉ dùng những tính năng hết sức cơ bản như <strong>pull</strong>, <strong>push</strong>, <strong>commit</strong>, cùng lắm thì là diff file, còn những thao tác nâng cao thì vẫn phải thực hiện thủ công. Không phải vì các ứng dụng trên không hỗ trợ, mà vì các thao tác nâng cao đòi hỏi nhiều thao tác chuột dài dòng trên màn hình.</p>\n<p>Sau khi trải nghiệm hầu hết các giao diện Git UI tiêu biểu trên thị trường, cả miễn phí lẫn trả phí, tôi nhận ra rằng: <strong>git CLI vẫn là lựa chọn tối ưu</strong> vì:</p>\n<ul>\n<li class=\"\">Ít thao tác: tôi đã cài đặt các \"phím tắt\" (alias) để chỉ cần một câu lệnh là có ngay kết quả mong muốn.</li>\n<li class=\"\">Phản hồi nhanh: không cần rời tay khỏi bàn phím, thao tác dòng lệnh luôn nhanh hơn so với việc mở ứng dụng và sử dụng chuột.</li>\n</ul>\n<p>Một nỗi lo thường trực khi làm việc với Git là phải nắm được \"bức tranh toàn cảnh\" của repo — có những thay đổi nào từ upstream trong lúc tôi đang làm việc trên nhánh của mình? Xung đột nào có thể xảy ra? Khi tôi push thì liệu có \"đạp chân\" ai không?</p>\n<p>Trước đây tôi thường dùng git client để nắm được điều này, vì tính năng đó của git - <code>git log</code> - trông khá là củ chuối</p>\n<figure><p><img decoding=\"async\" loading=\"lazy\" alt=\"alt text\" src=\"https://daoanhthanh.vn/assets/images/bland-git-log-aaa4343342d1d45d8896f4c12b0062f9.png\" width=\"847\" height=\"584\" class=\"img_IV9f\">\n</p><figcaption><strong>Fig 1:</strong> Nội dung này không có gì hữu ích lắm</figcaption><p></p></figure>\n<p>Git log mặc định là liệt kê các commit gần nhất, nhưng thứ tôi cần lại là:</p>\n<ul>\n<li class=\"\">Liệu nhánh mình có thể có conflig với nhánh dev</li>\n<li class=\"\">khoảng 9 lịch sử commit gần nhất ở tất cả các nhánh. Tôi sẽ dựa vào đây để phán đoán liệu có conflict tiềm tàng nào với mọi người hay không. Bạn có thể cần nhiều hơn 9 nếu team bạn có nhiều người</li>\n</ul>\n<p>Sau khi tìm tòi google các kiểu, đây là câu lệnh mà tôi dùng hàng ngày:</p>\n<div class=\"language-bash codeBlockContainer_APcc theme-code-block\" style=\"--prism-background-color:hsl(230, 1%, 98%);--prism-color:hsl(230, 8%, 24%)\"><div class=\"codeBlockContent_m3Ux\"><pre tabindex=\"0\" class=\"prism-code language-bash codeBlock_qGQc thin-scrollbar\" style=\"background-color:hsl(230, 1%, 98%);color:hsl(230, 8%, 24%)\"><code class=\"codeBlockLines_p187\"><span class=\"token-line\" style=\"color:hsl(230, 8%, 24%)\"><span class=\"token function\" style=\"color:hsl(221, 87%, 60%)\">git</span><span class=\"token plain\"> log </span><span class=\"token parameter variable\" style=\"color:hsl(221, 87%, 60%)\">-9</span><span class=\"token plain\"> </span><span class=\"token parameter variable\" style=\"color:hsl(221, 87%, 60%)\">--graph</span><span class=\"token plain\"> --abbrev-commit </span><span class=\"token parameter variable\" style=\"color:hsl(221, 87%, 60%)\">--decorate</span><span class=\"token plain\"> </span><span class=\"token parameter variable\" style=\"color:hsl(221, 87%, 60%)\">--format</span><span class=\"token operator\" style=\"color:hsl(221, 87%, 60%)\">=</span><span class=\"token plain\">format:</span><span class=\"token string\" style=\"color:hsl(119, 34%, 47%)\">'%C(bold blue)%h%C(reset) - %C(bold cyan)%aD%C(reset) %C(bold green)(%ar)%C(reset)%C(bold yellow)%d%C(reset)%n'</span><span class=\"token plain\">'          %C</span><span class=\"token punctuation\" style=\"color:hsl(119, 34%, 47%)\">(</span><span class=\"token plain\">white</span><span class=\"token punctuation\" style=\"color:hsl(119, 34%, 47%)\">)</span><span class=\"token plain\">%s%C</span><span class=\"token punctuation\" style=\"color:hsl(119, 34%, 47%)\">(</span><span class=\"token plain\">reset</span><span class=\"token punctuation\" style=\"color:hsl(119, 34%, 47%)\">)</span><span class=\"token plain\"> %C</span><span class=\"token punctuation\" style=\"color:hsl(119, 34%, 47%)\">(</span><span class=\"token plain\">dim white</span><span class=\"token punctuation\" style=\"color:hsl(119, 34%, 47%)\">)</span><span class=\"token plain\">- %an%C</span><span class=\"token punctuation\" style=\"color:hsl(119, 34%, 47%)\">(</span><span class=\"token plain\">reset</span><span class=\"token punctuation\" style=\"color:hsl(119, 34%, 47%)\">)</span><span class=\"token plain\">' </span><span class=\"token parameter variable\" style=\"color:hsl(221, 87%, 60%)\">--all</span><br></span></code></pre><div class=\"buttonGroup_6DOT\"><button type=\"button\" aria-label=\"Sao chép mã nguồn\" title=\"Sao chép\" class=\"clean-btn\"><span class=\"copyButtonIcons_FhaS\" aria-hidden=\"true\"><svg viewBox=\"0 0 24 24\" class=\"copyButtonIcon_phi_\"><path fill=\"currentColor\" d=\"M19,21H8V7H19M19,5H8A2,2 0 0,0 6,7V21A2,2 0 0,0 8,23H19A2,2 0 0,0 21,21V7A2,2 0 0,0 19,5M16,1H4A2,2 0 0,0 2,3V17H4V3H16V1Z\"></path></svg><svg viewBox=\"0 0 24 24\" class=\"copyButtonSuccessIcon_FfTR\"><path fill=\"currentColor\" d=\"M21,7L9,19L3.5,13.5L4.91,12.09L9,16.17L19.59,5.59L21,7Z\"></path></svg></span></button></div></div></div>\n<p>đây là kết quả:</p>\n<figure><p><img decoding=\"async\" loading=\"lazy\" alt=\"alt text\" src=\"https://daoanhthanh.vn/assets/images/log-result-ee477f58f63ca29e609cf3791d6453dd.png\" width=\"1472\" height=\"633\" class=\"img_IV9f\">\n</p><figcaption><strong>Fig 2:</strong> Mọi thứ đã dễ nhìn hơn</figcaption><p></p></figure>\n<p>Mọi thứ đã được format gọn gàng và rất đầy đủ thông tin cho bạn.</p>\n<p>Nếu như bạn chỉ cần quan tâm đến một nhánh nhất định, ví dụ chỉ muốn xem nhánh hiện tại và <code>develop</code> thôi, bạn có thể chỉnh sửa một chút như sau:</p>\n<div class=\"language-bash codeBlockContainer_APcc theme-code-block\" style=\"--prism-background-color:hsl(230, 1%, 98%);--prism-color:hsl(230, 8%, 24%)\"><div class=\"codeBlockContent_m3Ux\"><pre tabindex=\"0\" class=\"prism-code language-bash codeBlock_qGQc thin-scrollbar\" style=\"background-color:hsl(230, 1%, 98%);color:hsl(230, 8%, 24%)\"><code class=\"codeBlockLines_p187\"><span class=\"token-line\" style=\"color:hsl(230, 8%, 24%)\"><span class=\"token function\" style=\"color:hsl(221, 87%, 60%)\">git</span><span class=\"token plain\"> log </span><span class=\"token parameter variable\" style=\"color:hsl(221, 87%, 60%)\">-6</span><span class=\"token plain\"> </span><span class=\"token parameter variable\" style=\"color:hsl(221, 87%, 60%)\">--graph</span><span class=\"token plain\"> --abbrev-commit </span><span class=\"token parameter variable\" style=\"color:hsl(221, 87%, 60%)\">--decorate</span><span class=\"token plain\"> </span><span class=\"token punctuation\" style=\"color:hsl(119, 34%, 47%)\">\\</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:hsl(230, 8%, 24%)\"><span class=\"token plain\">  </span><span class=\"token parameter variable\" style=\"color:hsl(221, 87%, 60%)\">--format</span><span class=\"token operator\" style=\"color:hsl(221, 87%, 60%)\">=</span><span class=\"token plain\">format:</span><span class=\"token string\" style=\"color:hsl(119, 34%, 47%)\">'%C(bold blue)%h%C(reset) - %C(bold cyan)%aD%C(reset) %C(bold green)(%ar)%C(reset)%C(bold yellow)%d%C(reset)%n'</span><span class=\"token plain\">'          %C</span><span class=\"token punctuation\" style=\"color:hsl(119, 34%, 47%)\">(</span><span class=\"token plain\">white</span><span class=\"token punctuation\" style=\"color:hsl(119, 34%, 47%)\">)</span><span class=\"token plain\">%s%C</span><span class=\"token punctuation\" style=\"color:hsl(119, 34%, 47%)\">(</span><span class=\"token plain\">reset</span><span class=\"token punctuation\" style=\"color:hsl(119, 34%, 47%)\">)</span><span class=\"token plain\"> %C</span><span class=\"token punctuation\" style=\"color:hsl(119, 34%, 47%)\">(</span><span class=\"token plain\">dim white</span><span class=\"token punctuation\" style=\"color:hsl(119, 34%, 47%)\">)</span><span class=\"token plain\">- %an%C</span><span class=\"token punctuation\" style=\"color:hsl(119, 34%, 47%)\">(</span><span class=\"token plain\">reset</span><span class=\"token punctuation\" style=\"color:hsl(119, 34%, 47%)\">)</span><span class=\"token plain\">' </span><span class=\"token punctuation\" style=\"color:hsl(119, 34%, 47%)\">\\</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:hsl(230, 8%, 24%)\"><span class=\"token plain\">  develop HEAD </span><span class=\"token comment\" style=\"color:hsl(230, 4%, 64%)\"># &lt;== khác biệt chính nằm ở đây</span><br></span></code></pre><div class=\"buttonGroup_6DOT\"><button type=\"button\" aria-label=\"Sao chép mã nguồn\" title=\"Sao chép\" class=\"clean-btn\"><span class=\"copyButtonIcons_FhaS\" aria-hidden=\"true\"><svg viewBox=\"0 0 24 24\" class=\"copyButtonIcon_phi_\"><path fill=\"currentColor\" d=\"M19,21H8V7H19M19,5H8A2,2 0 0,0 6,7V21A2,2 0 0,0 8,23H19A2,2 0 0,0 21,21V7A2,2 0 0,0 19,5M16,1H4A2,2 0 0,0 2,3V17H4V3H16V1Z\"></path></svg><svg viewBox=\"0 0 24 24\" class=\"copyButtonSuccessIcon_FfTR\"><path fill=\"currentColor\" d=\"M21,7L9,19L3.5,13.5L4.91,12.09L9,16.17L19.59,5.59L21,7Z\"></path></svg></span></button></div></div></div>\n<p>cùng xem kết quả</p>\n<figure><p><img decoding=\"async\" loading=\"lazy\" alt=\"alt text\" src=\"https://daoanhthanh.vn/assets/images/dev-only-25a06d168b48a84c26597b85255c9f53.png\" width=\"1487\" height=\"487\" class=\"img_IV9f\">\n</p><figcaption><strong>Fig 3:</strong> Khi bạn chỉ cần quan tâm một nhánh nhất định</figcaption><p></p></figure>\n<p>Giờ đây tôi đã biết được rằng nhánh của tôi (commit đầu tiên trong ảnh) đang ở sau một vài commit trong nhánh <code>develop</code>. Vì vậy tôi cần thực hiện rebase lại để cập nhật nhánh hiện tại trước khi tạo Merge Request.</p>\n<blockquote>\n<p>Chà, bạn thông minh đã nhận ra, tôi đã sử dụng <code>lg -9</code> và <code>lg develop -6</code> thay vì gõ cả 2 câu dài ngoằng kể trên vào terminal như đã đề cập. Tôi đã viết một bài giải thích về điều này, bạn đọc có thể tham khảo ở đây <a href=\"https://daoanhthanh.vn/blog/khai-mo-tiem-nang-command-alias\">Khai mở tiềm năng Command Alias</a>.</p>\n</blockquote>\n<p>Thường trước khi rebase hay merge nhánh, tôi sẽ kiểm tra trước xem liệu hai nhánh đó có conflict gì hay không. Một lần nữa, bạn không cần phải thao tác gì nhiều vì <code>git log</code> hoàn toàn có thể đáp ứng được.</p>\n<div class=\"language-bash codeBlockContainer_APcc theme-code-block\" style=\"--prism-background-color:hsl(230, 1%, 98%);--prism-color:hsl(230, 8%, 24%)\"><div class=\"codeBlockContent_m3Ux\"><pre tabindex=\"0\" class=\"prism-code language-bash codeBlock_qGQc thin-scrollbar\" style=\"background-color:hsl(230, 1%, 98%);color:hsl(230, 8%, 24%)\"><code class=\"codeBlockLines_p187\"><span class=\"token-line\" style=\"color:hsl(230, 8%, 24%)\"><span class=\"token function\" style=\"color:hsl(221, 87%, 60%)\">git</span><span class=\"token plain\"> log develop</span><span class=\"token punctuation\" style=\"color:hsl(119, 34%, 47%)\">..</span><span class=\"token plain\">HEAD </span><span class=\"token parameter variable\" style=\"color:hsl(221, 87%, 60%)\">--oneline</span><span class=\"token plain\"> --left-right --cherry-pick </span><span class=\"token parameter variable\" style=\"color:hsl(221, 87%, 60%)\">--color</span><span class=\"token plain\"> </span><span class=\"token punctuation\" style=\"color:hsl(119, 34%, 47%)\">\\</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:hsl(230, 8%, 24%)\"><span class=\"token plain\">  </span><span class=\"token parameter variable\" style=\"color:hsl(221, 87%, 60%)\">--pretty</span><span class=\"token operator\" style=\"color:hsl(221, 87%, 60%)\">=</span><span class=\"token plain\">format:</span><span class=\"token string\" style=\"color:hsl(119, 34%, 47%)\">'%C(yellow)%m%C(reset) %C(blue)%h%C(reset) %C(cyan)%an%C(reset) %C(green)%ar%C(reset) %s%n'</span><span class=\"token plain\"> </span><span class=\"token punctuation\" style=\"color:hsl(119, 34%, 47%)\">\\</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:hsl(230, 8%, 24%)\"><span class=\"token plain\">  </span><span class=\"token operator\" style=\"color:hsl(221, 87%, 60%)\">|</span><span class=\"token plain\"> </span><span class=\"token function\" style=\"color:hsl(221, 87%, 60%)\">head</span><span class=\"token plain\"> </span><span class=\"token parameter variable\" style=\"color:hsl(221, 87%, 60%)\">-9</span><br></span></code></pre><div class=\"buttonGroup_6DOT\"><button type=\"button\" aria-label=\"Sao chép mã nguồn\" title=\"Sao chép\" class=\"clean-btn\"><span class=\"copyButtonIcons_FhaS\" aria-hidden=\"true\"><svg viewBox=\"0 0 24 24\" class=\"copyButtonIcon_phi_\"><path fill=\"currentColor\" d=\"M19,21H8V7H19M19,5H8A2,2 0 0,0 6,7V21A2,2 0 0,0 8,23H19A2,2 0 0,0 21,21V7A2,2 0 0,0 19,5M16,1H4A2,2 0 0,0 2,3V17H4V3H16V1Z\"></path></svg><svg viewBox=\"0 0 24 24\" class=\"copyButtonSuccessIcon_FfTR\"><path fill=\"currentColor\" d=\"M21,7L9,19L3.5,13.5L4.91,12.09L9,16.17L19.59,5.59L21,7Z\"></path></svg></span></button></div></div></div>\n<p>Lệnh trên tiến hành lấy 9 conflict mới nhất của nhánh hiện tại và nhánh <code>develop</code>. Cùng xem kết của nó:</p>\n<figure><p><img decoding=\"async\" loading=\"lazy\" alt=\"alt text\" src=\"https://daoanhthanh.vn/assets/images/find-conflicts-77a40fb1a38c969b2c8b7f1d80b95b38.png\" width=\"1061\" height=\"126\" class=\"img_IV9f\">\n</p><figcaption><strong>Fig 4:</strong> Ố ồ, vậy là có một conflict</figcaption><p></p></figure>\n<p>Hi vọng những chia sẻ trên sẽ khiến công việc của bạn được thuận tiện, và bạn sẽ trở nên năng suất hơn.</p>",
            "url": "https://daoanhthanh.vn/blog/mot-cach-khac-de-su-dung-git-log",
            "title": "Một cách khác để sử dụng git log",
            "summary": "Sử dụng git log một cách chuyên nghiệp",
            "date_modified": "2025-11-16T00:00:00.000Z",
            "author": {
                "name": "Đào Anh Thành",
                "url": "https://github.com/daoanhthanh"
            },
            "tags": [
                "git"
            ]
        },
        {
            "id": "https://daoanhthanh.vn/blog/khai-mo-tiem-nang-command-alias",
            "content_html": "<p>Bài viết sẽ sẵn sàng sớm thôi. Hãy luôn cập nhật!</p>",
            "url": "https://daoanhthanh.vn/blog/khai-mo-tiem-nang-command-alias",
            "title": "Khai mở tiềm năng Command Alias",
            "summary": "Khai mở tiềm năng Command Alias",
            "date_modified": "2025-11-16T00:00:00.000Z",
            "author": {
                "name": "Đào Anh Thành",
                "url": "https://github.com/daoanhthanh"
            },
            "tags": [
                "cli",
                "linux"
            ]
        },
        {
            "id": "https://daoanhthanh.vn/blog/use-merge-and-rebase-in-professional-way",
            "content_html": "<p>Tôi nhận ra một số lượng đáng kể đồng nghiệp của tôi có một sự hiểu khá mơ hồ về <code>Merge</code> và <code>Rebase</code>. Vì vậy tôi biên một bài vừa để giải thích, vừa để chia sẻ với ae cách một git guru làm việc.</p>\n<!-- -->\n<p>Có thể bạn sẽ nghĩ: <strong>lại một bài so sánh nữa, tại sao mình lại phải đọc nó nhỉ?</strong></p>\n<p>Câu trả lời ngắn ngọn của tôi: <strong>Cách bạn xử lý lịch sử git sẽ lên một tầm cao mới</strong>.</p>\n<h2 class=\"anchor anchorTargetHideOnScrollNavbar_Omnb\" id=\"chúng-là-gì\">Chúng là gì<a href=\"https://daoanhthanh.vn/blog/use-merge-and-rebase-in-professional-way#ch%C3%BAng-l%C3%A0-g%C3%AC\" class=\"hash-link\" aria-label=\"Chuyển đến tiêu đề\" title=\"Chuyển đến tiêu đề\" translate=\"no\">​</a></h2>\n<p>Trước tiên, tôi sẽ nói lại ý nghĩa của 2 tính năng này. Tôi tin rằng nhiều anh em tuy lập trình đã lâu nhưng chưa hiểu hết vai trò của chúng.</p>\n<p>Tôi cho rằng bạn đã hiểu cơ bản git là gì và dùng git để làm gì. Nếu bạn là người mới bắt đầu, hãy dành thời gian để tìm hiểu git trước khi đọc bài.</p>\n<h3 class=\"anchor anchorTargetHideOnScrollNavbar_Omnb\" id=\"merge\">Merge<a href=\"https://daoanhthanh.vn/blog/use-merge-and-rebase-in-professional-way#merge\" class=\"hash-link\" aria-label=\"Chuyển đến tiêu đề\" title=\"Chuyển đến tiêu đề\" translate=\"no\">​</a></h3>\n<p>Đơn giản như cái tên của nó: khi bạn muốn kết hợp code của mình vào chung code của người khác, bạn dùng <code>merge</code>. Hãy xem hình minh họa sau:</p>\n<figure><p><img decoding=\"async\" loading=\"lazy\" alt=\"interface\" src=\"https://daoanhthanh.vn/assets/images/merger-simple-95e0af7fb960d1ae8be30588b5f163ee.png\" width=\"2425\" height=\"1127\" class=\"img_IV9f\"></p><figcaption><p><strong>Fig 1:</strong> Nếu thời điểm commit code của bạn mới hơn, sau khi merge thì code của bạn sẽ\n<br> ... ở vị trí mới hơn <code>¯\\_(ツ)_/¯</code></p></figcaption></figure>\n<p>Chiến lược merge trên người ta gọi là <code>Fast Forward Merge</code>. Thật là đơn giản đúng không? Tuy nhiên trong thực tế mọi thứ hiếm khi được \"vui vẻ\" như vậy!</p>\n<p>Khi làm việc nhóm và mọi người cùng phát triển song song, sẽ có lúc phần code của bạn được commit sau những thay đổi của người khác:</p>\n<p><img decoding=\"async\" loading=\"lazy\" alt=\"interface\" src=\"https://daoanhthanh.vn/assets/images/when-your-code-behind-35a6bec1ad1e51a1c349087f3cb04b36.png\" width=\"2163\" height=\"1231\" class=\"img_IV9f\"></p>\n<br>\n<p>Bạn muốn kết hợp chúng vào với nhau thì đây sẽ là công việc được làm sau hậu trường: Git sẽ <strong>TỰ\nĐỘNG TẠO MỘT COMMIT MỚI VÀ GHI VÀO TIMELINE</strong>. Commit mới này GỘP commit của bạn với commit mới nhất\ncủa nhánh đích.</p>\n<p>Hình dưới đây minh hoạ hoạ lệnh <code>git merge &lt;nhánh-đích&gt;</code> được thực thi:</p>\n<figure><p><img decoding=\"async\" loading=\"lazy\" alt=\"interface\" src=\"https://daoanhthanh.vn/assets/images/three-way-merge-b5424d0d266f1dce30cdce5228580cad.png\" width=\"2684\" height=\"1472\" class=\"img_IV9f\"></p><figcaption><p><strong>Fig 2:</strong> Commit mới được tạo để đánh dấu việc code được kết hợp, toàn bộ mọi thứ khác được giữ\nnguyên</p></figcaption></figure>\n<p>Ưu điểm của việc này là lịch sử phát triển của cả 2 nhánh được giữ nguyên vẹn. Bạn sẽ thấy được rất rõ rành mạch phát triển của từng nhánh, cùng với thời điểm chúng được kết hợp.</p>\n<figure><p><img decoding=\"async\" loading=\"lazy\" alt=\"interface\" src=\"https://daoanhthanh.vn/assets/images/merge-history-d7e6363aae7bf918173048bf28513173.png\" width=\"1012\" height=\"454\" class=\"img_IV9f\"></p><figcaption><p><strong>Fig 3:</strong> Thời điểm merge code, lịch sử phát triển của các nhánh được giữ nguyên vẹn</p></figcaption></figure>\n<p>Nhưng đôi khi việc này khiến lịch sử git bị rối, tăng rủi ro conflict trong quá trình merge sau này. Một trong những trường hợp kinh điển là khi bạn làm việc với nhiều nhánh feature nhỏ, liên tục cập nhật chúng với nhau (không phải với nhánh main). Git timeline của bạn có thể sẽ trông như thế này:</p>\n<figure><p><img decoding=\"async\" loading=\"lazy\" alt=\"alt text\" src=\"https://daoanhthanh.vn/assets/images/complex-merge-39bd6da936de0c39bff99acee04d428e.png\" width=\"1544\" height=\"1826\" class=\"img_IV9f\">\n</p><figcaption><strong>Fig 4:</strong> Nguy cơ conflict cao khi lịch sử git quá phức tạp</figcaption><p></p></figure>\n<p>Đây là lúc bạn nên nghĩ đến <code>Rebase</code>.</p>\n<h3 class=\"anchor anchorTargetHideOnScrollNavbar_Omnb\" id=\"rebase\">Rebase<a href=\"https://daoanhthanh.vn/blog/use-merge-and-rebase-in-professional-way#rebase\" class=\"hash-link\" aria-label=\"Chuyển đến tiêu đề\" title=\"Chuyển đến tiêu đề\" translate=\"no\">​</a></h3>\n<p>Rebase tạm dịch là \"tái cấu trúc\". Nó cũng là một cách để kết hợp code từ nhánh này sang nhánh khác, nhưng thay vì tạo một commit mới để ghi lại việc kết hợp, <code>Rebase</code> sẽ <strong>DI CHUYỂN TOÀN BỘ CÁC COMMIT CỦA BẠN LÊN TRÊN ĐẦU NHÁNH ĐÍCH</strong>.</p>\n<p>Dưới đây minh hoạ quá trình kết hợp code giữa 2 nhánh feature thành một sử dụng rebase:</p>\n<div class=\"language-bash codeBlockContainer_APcc theme-code-block\" style=\"--prism-background-color:hsl(230, 1%, 98%);--prism-color:hsl(230, 8%, 24%)\"><div class=\"codeBlockContent_m3Ux\"><pre tabindex=\"0\" class=\"prism-code language-bash codeBlock_qGQc thin-scrollbar\" style=\"background-color:hsl(230, 1%, 98%);color:hsl(230, 8%, 24%)\"><code class=\"codeBlockLines_p187\"><span class=\"token-line\" style=\"color:hsl(230, 8%, 24%)\"><span class=\"token function\" style=\"color:hsl(221, 87%, 60%)\">git</span><span class=\"token plain\"> checkout feature/abc </span><span class=\"token comment\" style=\"color:hsl(230, 4%, 64%)\"># &lt;== nhánh bạn đang làm việc</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:hsl(230, 8%, 24%)\"><span class=\"token plain\"></span><span class=\"token function\" style=\"color:hsl(221, 87%, 60%)\">git</span><span class=\"token plain\"> rebase feature/xyz </span><span class=\"token comment\" style=\"color:hsl(230, 4%, 64%)\"># &lt;== nhánh bạn muốn kết hợp vào</span><br></span></code></pre><div class=\"buttonGroup_6DOT\"><button type=\"button\" aria-label=\"Sao chép mã nguồn\" title=\"Sao chép\" class=\"clean-btn\"><span class=\"copyButtonIcons_FhaS\" aria-hidden=\"true\"><svg viewBox=\"0 0 24 24\" class=\"copyButtonIcon_phi_\"><path fill=\"currentColor\" d=\"M19,21H8V7H19M19,5H8A2,2 0 0,0 6,7V21A2,2 0 0,0 8,23H19A2,2 0 0,0 21,21V7A2,2 0 0,0 19,5M16,1H4A2,2 0 0,0 2,3V17H4V3H16V1Z\"></path></svg><svg viewBox=\"0 0 24 24\" class=\"copyButtonSuccessIcon_FfTR\"><path fill=\"currentColor\" d=\"M21,7L9,19L3.5,13.5L4.91,12.09L9,16.17L19.59,5.59L21,7Z\"></path></svg></span></button></div></div></div>\n<figure><p><img decoding=\"async\" loading=\"lazy\" alt=\"alt text\" src=\"https://daoanhthanh.vn/assets/images/rebase-ref-f4129eb3a02465ff7120359e40346d9e.jpeg\" width=\"2732\" height=\"1008\" class=\"img_IV9f\"></p><figcaption><p><strong>Fig 5:</strong> Để ý rằng các commit của bạn đã được \"di chuyển\" lên trên cùng của nhánh đích</p></figcaption></figure>\n<p>Việc này có mặt lợi là làm cho lịch sử git của bạn trở nên thẳng hơn, dễ theo dõi hơn. Đồng thời giảm thiểu rủi ro conflict khi merge vào nhánh chính (main) sau này.</p>\n<figure><p><img decoding=\"async\" loading=\"lazy\" alt=\"alt text\" src=\"https://daoanhthanh.vn/assets/images/rebase-clean-history-9e3885ba3e87dba69e63ae0f4bb13a9f.png\" width=\"1292\" height=\"1388\" class=\"img_IV9f\"></p><figcaption><p><strong>Fig 6:</strong> Lịch sử git trở nên thẳng và sạch hơn rất nhiều so với <strong>Fig 4</strong></p></figcaption></figure>\n<p>NHƯNG NHƯNG NHƯNG ...</p>\n<h2 class=\"anchor anchorTargetHideOnScrollNavbar_Omnb\" id=\"khi-nào-dùng-merge-khi-nào-dùng-rebase\">Khi nào dùng Merge, khi nào dùng Rebase?<a href=\"https://daoanhthanh.vn/blog/use-merge-and-rebase-in-professional-way#khi-n%C3%A0o-d%C3%B9ng-merge-khi-n%C3%A0o-d%C3%B9ng-rebase\" class=\"hash-link\" aria-label=\"Chuyển đến tiêu đề\" title=\"Chuyển đến tiêu đề\" translate=\"no\">​</a></h2>\n<p>Không phải tự nhiên lại tồn tại 2 cách kết hợp code như vậy. Hiểu rõ về chúng sẽ giúp bạn làm việc dễ dàng hơn rất nhiều.</p>\n<p>Trước tiên nhớ kỹ điều này trong đầu, tôi sẽ giải thích sau:</p>\n<blockquote>\n<p>\"Merge dùng cho KẾT HỢP nhánh, Rebase dùng cho CẬP NHẬT nhánh.\"</p>\n</blockquote>\n<h3 class=\"anchor anchorTargetHideOnScrollNavbar_Omnb\" id=\"dùng-merge-khi-kết-hợp-nhánh-hoàn-chỉnh\">Dùng Merge khi kết hợp nhánh hoàn chỉnh<a href=\"https://daoanhthanh.vn/blog/use-merge-and-rebase-in-professional-way#d%C3%B9ng-merge-khi-k%E1%BA%BFt-h%E1%BB%A3p-nh%C3%A1nh-ho%C3%A0n-ch%E1%BB%89nh\" class=\"hash-link\" aria-label=\"Chuyển đến tiêu đề\" title=\"Chuyển đến tiêu đề\" translate=\"no\">​</a></h3>\n<p>Để hiểu được lý do, hãy cùng so sánh lịch sử git sau khi dùng <code>merge</code> và <code>rebase</code> để kết hợp nhánh <code>feature</code> vào nhánh <code>main</code>.</p>\n<figure><p><img decoding=\"async\" loading=\"lazy\" alt=\"alt text\" src=\"https://daoanhthanh.vn/assets/images/merge-vs-rebase-983b57e29d7d6c6ac3e490eb2daa8bbd.png\" width=\"1473\" height=\"671\" class=\"img_IV9f\"></p><figcaption><p><strong>Fig 7:</strong> Lịch sử gốc được bảo tồn khi dùng <code>merge</code> (trái), <br>\nbị xáo trộn khi khi dùng <code>rebase</code> (phải)</p></figcaption></figure>\n<p>Vì <code>merge</code> giữ nguyên trạng lịch sử phát triển của các nhánh - <code>rebase</code> thì không - nên nó rất phù hợp để sử dụng khi bạn muốn kết hợp một <strong>nhánh hoàn chỉnh</strong> vào nhánh chính (main/develop). Có 3 lý do chính tại sao lại như vậy:</p>\n<ol>\n<li class=\"\"><strong>Dễ dàng theo dõi:</strong> Giữ nguyên lịch sử phát triển giúp các thành viên trong nhóm dễ dàng theo dõi và hiểu các thay đổi đã được thực hiện.</li>\n<li class=\"\"><strong>Giảm rủi ro xung đột:</strong> Khi kết hợp một nhánh hoàn chỉnh, việc giữ nguyên lịch sử phát triển giúp giảm rủi ro xung đột khi các thay đổi đã được kiểm thử và xác nhận.</li>\n<li class=\"\"><strong>Dễ dàng khôi phục:</strong> Nếu cần khôi phục lại một phiên bản trước đó, việc giữ nguyên lịch sử phát triển giúp dễ dàng xác định và khôi phục các thay đổi đã được thực hiện.</li>\n</ol>\n<h3 class=\"anchor anchorTargetHideOnScrollNavbar_Omnb\" id=\"dùng-rebase-để-cập-nhật-nhánh\">Dùng Rebase để cập nhật nhánh<a href=\"https://daoanhthanh.vn/blog/use-merge-and-rebase-in-professional-way#d%C3%B9ng-rebase-%C4%91%E1%BB%83-c%E1%BA%ADp-nh%E1%BA%ADt-nh%C3%A1nh\" class=\"hash-link\" aria-label=\"Chuyển đến tiêu đề\" title=\"Chuyển đến tiêu đề\" translate=\"no\">​</a></h3>\n<p>Vì đặc điểm của <code>rebase</code> là di chuyển các commit của bạn lên trên cùng của nhánh đích và không tạo thêm commit mới, nên nó rất phù hợp để sử dụng khi bạn muốn <strong>cập nhật nhánh của mình</strong> với các thay đổi mới nhất từ nhánh chính (main/develop), hoặc từ các nhánh feature khác.</p>\n<p>Xem xét các ví dụ sau:</p>\n<ul>\n<li class=\"\">Bạn đang làm việc trên nhánh <code>feature/abc</code>, trong khi đó nhánh <code>main</code> đã có những thay đổi mới. Bạn muốn cập nhật nhánh <code>feature/abc</code> với những thay đổi mới nhất từ <code>main</code> để tránh xung đột khi merge sau này.</li>\n<li class=\"\">Trước khi tạo pull request, bạn muốn đảm đảm bảo mọi thứ đã được cập nhật với nhánh chính (main/develop).</li>\n<li class=\"\">Bạn đang làm việc với nhiều nhánh feature nhỏ, và bạn muốn cập nhật nhánh <code>feature/abc</code> với các thay đổi từ nhánh <code>feature/xyz</code>, <code>feature/mnk</code>,... để đảm bảo rằng bạn đang làm việc với phiên bản mới nhất của code.</li>\n<li class=\"\">Bạn muốn giữ lịch sử git của mình sạch sẽ, tránh việc tạo quá nhiều commit merge không cần thiết.</li>\n</ul>\n<p>Chúng đều là những trường hợp điển hình để sử dụng <code>rebase</code>. Đặc điểm chung là chúng không quá cần thiết phải giữ nguyên lịch sử phát triển của nhánh, mà quan trọng là cập nhật code mới nhất để làm việc hiệu quả.</p>\n<h2 class=\"anchor anchorTargetHideOnScrollNavbar_Omnb\" id=\"làm-sao-để-pro\">Làm sao để PRO?<a href=\"https://daoanhthanh.vn/blog/use-merge-and-rebase-in-professional-way#l%C3%A0m-sao-%C4%91%E1%BB%83-pro\" class=\"hash-link\" aria-label=\"Chuyển đến tiêu đề\" title=\"Chuyển đến tiêu đề\" translate=\"no\">​</a></h2>\n<p>PRO là khi bạn đã \"ăn hành\" đủ với merge và rebase, trải qua đủ các pha mất code, conflict, bị đồng đội cà khịa, rồi mới ngộ ra chân lý git! Dù không dám nhận là một git guru, nhưng dưới đây là vài chia sẻ của tôi sau khi đã lãnh đủ thứ conflict, mất code, mất thời gian, quở trách,... để bạn không phải trải qua những điều đó nữa.</p>\n<h3 class=\"anchor anchorTargetHideOnScrollNavbar_Omnb\" id=\"cập-nhật-code-đúng-cách\">Cập nhật code đúng cách<a href=\"https://daoanhthanh.vn/blog/use-merge-and-rebase-in-professional-way#c%E1%BA%ADp-nh%E1%BA%ADt-code-%C4%91%C3%BAng-c%C3%A1ch\" class=\"hash-link\" aria-label=\"Chuyển đến tiêu đề\" title=\"Chuyển đến tiêu đề\" translate=\"no\">​</a></h3>\n<p>Hãy dùng <code>rebase</code> để cập nhật code, dù nó có là từ nhánh chính hay từ nhánh feature khác đi chăng nữa. Trong quá trình này bạn rất có thể sẽ gặp conflict, hãy bình tĩnh giải quyết chúng từng bước một. Đây là cách tôi thường làm:</p>\n<div class=\"language-bash codeBlockContainer_APcc theme-code-block\" style=\"--prism-background-color:hsl(230, 1%, 98%);--prism-color:hsl(230, 8%, 24%)\"><div class=\"codeBlockContent_m3Ux\"><pre tabindex=\"0\" class=\"prism-code language-bash codeBlock_qGQc thin-scrollbar\" style=\"background-color:hsl(230, 1%, 98%);color:hsl(230, 8%, 24%)\"><code class=\"codeBlockLines_p187\"><span class=\"token-line\" style=\"color:hsl(230, 8%, 24%)\"><span class=\"token function\" style=\"color:hsl(221, 87%, 60%)\">git</span><span class=\"token plain\"> checkout feature/abc </span><span class=\"token comment\" style=\"color:hsl(230, 4%, 64%)\"># &lt;== nhánh bạn đang làm việc</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:hsl(230, 8%, 24%)\"><span class=\"token plain\"></span><span class=\"token function\" style=\"color:hsl(221, 87%, 60%)\">git</span><span class=\"token plain\"> fetch origin main </span><span class=\"token comment\" style=\"color:hsl(230, 4%, 64%)\"># &lt;== lấy code mới nhất từ nhánh chính</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:hsl(230, 8%, 24%)\"><span class=\"token plain\"></span><span class=\"token function\" style=\"color:hsl(221, 87%, 60%)\">git</span><span class=\"token plain\"> rebase origin/main </span><span class=\"token comment\" style=\"color:hsl(230, 4%, 64%)\"># &lt;== cập nhật nhánh của bạn với nhánh chính</span><br></span></code></pre><div class=\"buttonGroup_6DOT\"><button type=\"button\" aria-label=\"Sao chép mã nguồn\" title=\"Sao chép\" class=\"clean-btn\"><span class=\"copyButtonIcons_FhaS\" aria-hidden=\"true\"><svg viewBox=\"0 0 24 24\" class=\"copyButtonIcon_phi_\"><path fill=\"currentColor\" d=\"M19,21H8V7H19M19,5H8A2,2 0 0,0 6,7V21A2,2 0 0,0 8,23H19A2,2 0 0,0 21,21V7A2,2 0 0,0 19,5M16,1H4A2,2 0 0,0 2,3V17H4V3H16V1Z\"></path></svg><svg viewBox=\"0 0 24 24\" class=\"copyButtonSuccessIcon_FfTR\"><path fill=\"currentColor\" d=\"M21,7L9,19L3.5,13.5L4.91,12.09L9,16.17L19.59,5.59L21,7Z\"></path></svg></span></button></div></div></div>\n<p>và voila, conflict:</p>\n<div class=\"language-bash codeBlockContainer_APcc theme-code-block\" style=\"--prism-background-color:hsl(230, 1%, 98%);--prism-color:hsl(230, 8%, 24%)\"><div class=\"codeBlockContent_m3Ux\"><pre tabindex=\"0\" class=\"prism-code language-bash codeBlock_qGQc thin-scrollbar\" style=\"background-color:hsl(230, 1%, 98%);color:hsl(230, 8%, 24%)\"><code class=\"codeBlockLines_p187\"><span class=\"token-line\" style=\"color:hsl(230, 8%, 24%)\"><span class=\"token plain\">Auto-merging src/app.js</span><br></span><span class=\"token-line\" style=\"color:hsl(230, 8%, 24%)\"><span class=\"token plain\">CONFLICT </span><span class=\"token punctuation\" style=\"color:hsl(119, 34%, 47%)\">(</span><span class=\"token plain\">content</span><span class=\"token punctuation\" style=\"color:hsl(119, 34%, 47%)\">)</span><span class=\"token plain\">: Merge conflict </span><span class=\"token keyword\" style=\"color:hsl(301, 63%, 40%)\">in</span><span class=\"token plain\"> src/app.js</span><br></span><span class=\"token-line\" style=\"color:hsl(230, 8%, 24%)\"><span class=\"token plain\">error: could not apply 1234abcd</span><span class=\"token punctuation\" style=\"color:hsl(119, 34%, 47%)\">..</span><span class=\"token plain\">. Thêm chức năng đăng nhập</span><br></span><span class=\"token-line\" style=\"color:hsl(230, 8%, 24%)\"><span class=\"token plain\">Resolve all conflicts manually, mark them as resolved with </span><span class=\"token string\" style=\"color:hsl(119, 34%, 47%)\">'git add &lt;file&gt;'</span><span class=\"token plain\">, </span><span class=\"token keyword\" style=\"color:hsl(301, 63%, 40%)\">then</span><span class=\"token plain\"> run </span><span class=\"token string\" style=\"color:hsl(119, 34%, 47%)\">'git rebase --continue'</span><span class=\"token builtin class-name\" style=\"color:hsl(35, 99%, 36%)\">.</span><br></span></code></pre><div class=\"buttonGroup_6DOT\"><button type=\"button\" aria-label=\"Sao chép mã nguồn\" title=\"Sao chép\" class=\"clean-btn\"><span class=\"copyButtonIcons_FhaS\" aria-hidden=\"true\"><svg viewBox=\"0 0 24 24\" class=\"copyButtonIcon_phi_\"><path fill=\"currentColor\" d=\"M19,21H8V7H19M19,5H8A2,2 0 0,0 6,7V21A2,2 0 0,0 8,23H19A2,2 0 0,0 21,21V7A2,2 0 0,0 19,5M16,1H4A2,2 0 0,0 2,3V17H4V3H16V1Z\"></path></svg><svg viewBox=\"0 0 24 24\" class=\"copyButtonSuccessIcon_FfTR\"><path fill=\"currentColor\" d=\"M21,7L9,19L3.5,13.5L4.91,12.09L9,16.17L19.59,5.59L21,7Z\"></path></svg></span></button></div></div></div>\n<p>=&gt; mở IDE/editor của bạn lên, tìm file bị conflict, giải quyết chúng, sau đó:</p>\n<div class=\"language-bash codeBlockContainer_APcc theme-code-block\" style=\"--prism-background-color:hsl(230, 1%, 98%);--prism-color:hsl(230, 8%, 24%)\"><div class=\"codeBlockContent_m3Ux\"><pre tabindex=\"0\" class=\"prism-code language-bash codeBlock_qGQc thin-scrollbar\" style=\"background-color:hsl(230, 1%, 98%);color:hsl(230, 8%, 24%)\"><code class=\"codeBlockLines_p187\"><span class=\"token-line\" style=\"color:hsl(230, 8%, 24%)\"><span class=\"token function\" style=\"color:hsl(221, 87%, 60%)\">git</span><span class=\"token plain\"> </span><span class=\"token function\" style=\"color:hsl(221, 87%, 60%)\">add</span><span class=\"token plain\"> </span><span class=\"token builtin class-name\" style=\"color:hsl(35, 99%, 36%)\">.</span><span class=\"token plain\"> </span><span class=\"token comment\" style=\"color:hsl(230, 4%, 64%)\"># hoặc git add &lt;tên_file&gt;</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:hsl(230, 8%, 24%)\"><span class=\"token plain\"></span><span class=\"token function\" style=\"color:hsl(221, 87%, 60%)\">git</span><span class=\"token plain\"> rebase </span><span class=\"token parameter variable\" style=\"color:hsl(221, 87%, 60%)\">--continue</span><br></span></code></pre><div class=\"buttonGroup_6DOT\"><button type=\"button\" aria-label=\"Sao chép mã nguồn\" title=\"Sao chép\" class=\"clean-btn\"><span class=\"copyButtonIcons_FhaS\" aria-hidden=\"true\"><svg viewBox=\"0 0 24 24\" class=\"copyButtonIcon_phi_\"><path fill=\"currentColor\" d=\"M19,21H8V7H19M19,5H8A2,2 0 0,0 6,7V21A2,2 0 0,0 8,23H19A2,2 0 0,0 21,21V7A2,2 0 0,0 19,5M16,1H4A2,2 0 0,0 2,3V17H4V3H16V1Z\"></path></svg><svg viewBox=\"0 0 24 24\" class=\"copyButtonSuccessIcon_FfTR\"><path fill=\"currentColor\" d=\"M21,7L9,19L3.5,13.5L4.91,12.09L9,16.17L19.59,5.59L21,7Z\"></path></svg></span></button></div></div></div>\n<p>Lặp lại quá trình này cho đến khi rebase hoàn tất, hoặc dùng <code>git rebase --abort</code> để hủy nếu bạn thấy rối hoặc không chắc về điều mình đang làm.</p>\n<p>Đến đây thì 96,69% các trường hợp cập nhật code của bạn đã xong xuôi. Chỉ còn vài phần trăm còn lại là những trường hợp đặc biệt, ví dụ như bạn đang rebase mà phát hiện ra nhánh đích đã được squash (gộp commit) hoặc rebase rồi.</p>\n<p>Trong trường hợp này, bạn có thể làm theo các bước sau để xử lý:</p>\n<div class=\"language-bash codeBlockContainer_APcc theme-code-block\" style=\"--prism-background-color:hsl(230, 1%, 98%);--prism-color:hsl(230, 8%, 24%)\"><div class=\"codeBlockContent_m3Ux\"><pre tabindex=\"0\" class=\"prism-code language-bash codeBlock_qGQc thin-scrollbar\" style=\"background-color:hsl(230, 1%, 98%);color:hsl(230, 8%, 24%)\"><code class=\"codeBlockLines_p187\"><span class=\"token-line\" style=\"color:hsl(230, 8%, 24%)\"><span class=\"token function\" style=\"color:hsl(221, 87%, 60%)\">git</span><span class=\"token plain\"> fetch origin main </span><span class=\"token comment\" style=\"color:hsl(230, 4%, 64%)\"># Lấy code mới nhất từ nhánh chính</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:hsl(230, 8%, 24%)\"><span class=\"token plain\"></span><span class=\"token function\" style=\"color:hsl(221, 87%, 60%)\">git</span><span class=\"token plain\"> checkout feature/abc </span><span class=\"token comment\" style=\"color:hsl(230, 4%, 64%)\"># Chuyển về nhánh của bạn</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:hsl(230, 8%, 24%)\"><span class=\"token plain\"></span><span class=\"token function\" style=\"color:hsl(221, 87%, 60%)\">git</span><span class=\"token plain\"> rebase </span><span class=\"token parameter variable\" style=\"color:hsl(221, 87%, 60%)\">--onto</span><span class=\"token plain\"> origin/main </span><span class=\"token operator\" style=\"color:hsl(221, 87%, 60%)\">&lt;</span><span class=\"token plain\">commit_hash_cũ</span><span class=\"token operator\" style=\"color:hsl(221, 87%, 60%)\">&gt;</span><span class=\"token plain\"> feature/abc</span><br></span></code></pre><div class=\"buttonGroup_6DOT\"><button type=\"button\" aria-label=\"Sao chép mã nguồn\" title=\"Sao chép\" class=\"clean-btn\"><span class=\"copyButtonIcons_FhaS\" aria-hidden=\"true\"><svg viewBox=\"0 0 24 24\" class=\"copyButtonIcon_phi_\"><path fill=\"currentColor\" d=\"M19,21H8V7H19M19,5H8A2,2 0 0,0 6,7V21A2,2 0 0,0 8,23H19A2,2 0 0,0 21,21V7A2,2 0 0,0 19,5M16,1H4A2,2 0 0,0 2,3V17H4V3H16V1Z\"></path></svg><svg viewBox=\"0 0 24 24\" class=\"copyButtonSuccessIcon_FfTR\"><path fill=\"currentColor\" d=\"M21,7L9,19L3.5,13.5L4.91,12.09L9,16.17L19.59,5.59L21,7Z\"></path></svg></span></button></div></div></div>\n<p>Trong đó <code>&lt;commit_hash_cũ&gt;</code> là commit hash của commit cuối cùng trên nhánh <code>feature/abc</code> trước khi bạn bắt đầu rebase. Lệnh này sẽ giúp bạn di chuyển các commit của nhánh <code>feature/abc</code> lên trên cùng của nhánh <code>main</code>, bỏ qua các commit đã bị squash hoặc rebase. Tôi sẽ nói kỹ hơn về cách xử lý các trường hợp đặc biệt này trong một bài viết khác.</p>\n<h3 class=\"anchor anchorTargetHideOnScrollNavbar_Omnb\" id=\"luôn-kiểm-tra-lại-với-nhánh-devmain-trước-khi-tạo-pull-request\">Luôn kiểm tra lại với nhánh dev/main trước khi tạo Pull Request<a href=\"https://daoanhthanh.vn/blog/use-merge-and-rebase-in-professional-way#lu%C3%B4n-ki%E1%BB%83m-tra-l%E1%BA%A1i-v%E1%BB%9Bi-nh%C3%A1nh-devmain-tr%C6%B0%E1%BB%9Bc-khi-t%E1%BA%A1o-pull-request\" class=\"hash-link\" aria-label=\"Chuyển đến tiêu đề\" title=\"Chuyển đến tiêu đề\" translate=\"no\">​</a></h3>\n<p>Khi rebase trong trường hợp các nhánh đích đã được squash hoặc rebase mà tôi đề cập bên trên, hoàn toàn có thể xảy ra trường hợp bạn vô tình bỏ sót một số commit quan trọng. Hay nói một cách đơn giản là <strong>bạn đã làm mất code</strong>. Độ nghiêm trọng của việc này thì khỏi bàn. Nguy hiểm hơn là nó không để lại dấu vết gì trong lịch sử git cả, và bạn sẽ cực kỳ khó để phát hiện điều đó.</p>\n<p>Để tránh điều này, LUÔN LUÔN kiểm tra lại nhánh của bạn với nhánh <code>dev/main</code> trước khi tạo Pull Request. Cách đơn giản nhất là dùng lệnh:</p>\n<div class=\"language-bash codeBlockContainer_APcc theme-code-block\" style=\"--prism-background-color:hsl(230, 1%, 98%);--prism-color:hsl(230, 8%, 24%)\"><div class=\"codeBlockContent_m3Ux\"><pre tabindex=\"0\" class=\"prism-code language-bash codeBlock_qGQc thin-scrollbar\" style=\"background-color:hsl(230, 1%, 98%);color:hsl(230, 8%, 24%)\"><code class=\"codeBlockLines_p187\"><span class=\"token-line\" style=\"color:hsl(230, 8%, 24%)\"><span class=\"token function\" style=\"color:hsl(221, 87%, 60%)\">git</span><span class=\"token plain\"> </span><span class=\"token function\" style=\"color:hsl(221, 87%, 60%)\">diff</span><span class=\"token plain\"> origin/main</span><span class=\"token punctuation\" style=\"color:hsl(119, 34%, 47%)\">..</span><span class=\"token plain\">.feature/abc</span><br></span></code></pre><div class=\"buttonGroup_6DOT\"><button type=\"button\" aria-label=\"Sao chép mã nguồn\" title=\"Sao chép\" class=\"clean-btn\"><span class=\"copyButtonIcons_FhaS\" aria-hidden=\"true\"><svg viewBox=\"0 0 24 24\" class=\"copyButtonIcon_phi_\"><path fill=\"currentColor\" d=\"M19,21H8V7H19M19,5H8A2,2 0 0,0 6,7V21A2,2 0 0,0 8,23H19A2,2 0 0,0 21,21V7A2,2 0 0,0 19,5M16,1H4A2,2 0 0,0 2,3V17H4V3H16V1Z\"></path></svg><svg viewBox=\"0 0 24 24\" class=\"copyButtonSuccessIcon_FfTR\"><path fill=\"currentColor\" d=\"M21,7L9,19L3.5,13.5L4.91,12.09L9,16.17L19.59,5.59L21,7Z\"></path></svg></span></button></div></div></div>\n<p>hoặc dùng công cụ so sánh của IDE/editor bạn đang dùng.</p>\n<p>Nếu bạn phát hiện bị mất code, đừng hoảng loạn, hãy đọc bài viết này của tôi để xử lý <em>like a pro</em>: <a href=\"https://daoanhthanh.vn/blog/use-merge-and-rebase-in-professional-way#\">Cách khôi phục code đã mất trong Git</a></p>\n<h3 class=\"anchor anchorTargetHideOnScrollNavbar_Omnb\" id=\"sử-dụng-merge-khi-kết-hợp-nhánh-hoàn-chỉnh\">Sử dụng Merge khi kết hợp nhánh hoàn chỉnh<a href=\"https://daoanhthanh.vn/blog/use-merge-and-rebase-in-professional-way#s%E1%BB%AD-d%E1%BB%A5ng-merge-khi-k%E1%BA%BFt-h%E1%BB%A3p-nh%C3%A1nh-ho%C3%A0n-ch%E1%BB%89nh\" class=\"hash-link\" aria-label=\"Chuyển đến tiêu đề\" title=\"Chuyển đến tiêu đề\" translate=\"no\">​</a></h3>\n<p>Nếu bạn chịu trách nhiệm merge Pull Request, hãy sử dụng <code>merge</code> để kết hợp nhánh hoàn chỉnh vào nhánh chính (main/develop). Đó là lý do người ta gọi nó là \"Merge Pull Request\" chứ không phải \"Rebase Pull Request\".</p>\n<h2 class=\"anchor anchorTargetHideOnScrollNavbar_Omnb\" id=\"lời-kết\">Lời kết<a href=\"https://daoanhthanh.vn/blog/use-merge-and-rebase-in-professional-way#l%E1%BB%9Di-k%E1%BA%BFt\" class=\"hash-link\" aria-label=\"Chuyển đến tiêu đề\" title=\"Chuyển đến tiêu đề\" translate=\"no\">​</a></h2>\n<p>Tự hỏi bản thân trước thao tác cập nhật code: Bạn đang muốn kết hợp nhánh hoàn chỉnh, hay chỉ đơn giản là cập nhật nhánh của mình với những thay đổi mới nhất?</p>\n<p>Cái gì quan trọng thì phải nói lại lần nữa:</p>\n<blockquote>\n<p>\"Merge dùng cho KẾT HỢP nhánh, Rebase dùng cho CẬP NHẬT nhánh.\"</p>\n</blockquote>",
            "url": "https://daoanhthanh.vn/blog/use-merge-and-rebase-in-professional-way",
            "title": "Sử dụng Merge và Rebase một cách chuyên nghiệp",
            "summary": "Sử dụng Merge và Rebase trong Git một cách chuyên nghiệp",
            "date_modified": "2025-10-25T00:00:00.000Z",
            "author": {
                "name": "Đào Anh Thành",
                "url": "https://github.com/daoanhthanh"
            },
            "tags": [
                "git"
            ]
        },
        {
            "id": "https://daoanhthanh.vn/blog/linux-file-permission",
            "content_html": "<p>Tìm hiểu về hệ thống quản lý, phân quyền cho file trong linux</p>\n<!-- -->\n<h2 class=\"anchor anchorTargetHideOnScrollNavbar_Omnb\" id=\"file-permission\">File permission<a href=\"https://daoanhthanh.vn/blog/linux-file-permission#file-permission\" class=\"hash-link\" aria-label=\"Chuyển đến tiêu đề\" title=\"Chuyển đến tiêu đề\" translate=\"no\">​</a></h2>\n<p>Trong linux, có 3 loại quyền cho file, đó là&nbsp;<span style=\"font-weight:bold;color:#2c8261\">Read</span>, <span style=\"font-weight:bold;color:#8d6953\">Write</span> và&nbsp;<span style=\"font-weight:bold;color:#ab1924\">Execute</span>.</p>\n<ul>\n<li class=\"\"><span style=\"font-weight:bold;color:#2c8261\">Read (r)</span>:&nbsp;Provides access to open\nand view the contents</li>\n<li class=\"\"><span style=\"font-weight:bold;color:#8d6953\">Write (w)</span>&nbsp;Provides access to modify\n(or delete) the content or file</li>\n<li class=\"\"><span style=\"font-weight:bold;color:#ab1924\">Execute (x)</span>&nbsp;Provides access to run\nthe file as a program</li>\n</ul>\n<table><thead><tr><th style=\"text-align:center\"><strong>Letters</strong></th><th style=\"text-align:left\"><strong>Definition</strong></th></tr></thead><tbody><tr><td style=\"text-align:center\"><span style=\"font-weight:bold;color:#2c8261\">r</span></td><td style=\"text-align:left\">Có quyền đọc nội dung file</td></tr><tr><td style=\"text-align:center\"><span style=\"font-weight:bold;color:#8d6953\">w</span></td><td style=\"text-align:left\">Viết hoặc chỉnh sửa nội dung file</td></tr><tr><td style=\"text-align:center\"><span style=\"font-weight:bold;color:#ab1924\">x</span></td><td style=\"text-align:left\">Thực thi file, nếu file đó có thể thực thi (*.sh, .v..v..)</td></tr></tbody></table>\n<h2 class=\"anchor anchorTargetHideOnScrollNavbar_Omnb\" id=\"permission-group\">Permission Group<a href=\"https://daoanhthanh.vn/blog/linux-file-permission#permission-group\" class=\"hash-link\" aria-label=\"Chuyển đến tiêu đề\" title=\"Chuyển đến tiêu đề\" translate=\"no\">​</a></h2>\n<p>Ta có thể xem permission group của một file trong linux bằng lệnh sau:</p>\n<div class=\"language-bash codeBlockContainer_APcc theme-code-block\" style=\"--prism-background-color:hsl(230, 1%, 98%);--prism-color:hsl(230, 8%, 24%)\"><div class=\"codeBlockContent_m3Ux\"><pre tabindex=\"0\" class=\"prism-code language-bash codeBlock_qGQc thin-scrollbar\" style=\"background-color:hsl(230, 1%, 98%);color:hsl(230, 8%, 24%)\"><code class=\"codeBlockLines_p187\"><span class=\"token-line\" style=\"color:hsl(230, 8%, 24%)\"><span class=\"token function\" style=\"color:hsl(221, 87%, 60%)\">ls</span><span class=\"token plain\"> </span><span class=\"token parameter variable\" style=\"color:hsl(221, 87%, 60%)\">-l</span><span class=\"token plain\"> </span><span class=\"token operator\" style=\"color:hsl(221, 87%, 60%)\">&lt;</span><span class=\"token plain\">file-name</span><span class=\"token operator\" style=\"color:hsl(221, 87%, 60%)\">&gt;</span><br></span></code></pre><div class=\"buttonGroup_6DOT\"><button type=\"button\" aria-label=\"Sao chép mã nguồn\" title=\"Sao chép\" class=\"clean-btn\"><span class=\"copyButtonIcons_FhaS\" aria-hidden=\"true\"><svg viewBox=\"0 0 24 24\" class=\"copyButtonIcon_phi_\"><path fill=\"currentColor\" d=\"M19,21H8V7H19M19,5H8A2,2 0 0,0 6,7V21A2,2 0 0,0 8,23H19A2,2 0 0,0 21,21V7A2,2 0 0,0 19,5M16,1H4A2,2 0 0,0 2,3V17H4V3H16V1Z\"></path></svg><svg viewBox=\"0 0 24 24\" class=\"copyButtonSuccessIcon_FfTR\"><path fill=\"currentColor\" d=\"M21,7L9,19L3.5,13.5L4.91,12.09L9,16.17L19.59,5.59L21,7Z\"></path></svg></span></button></div></div></div>\n<p>ví dụ:</p>\n<figure><p><img decoding=\"async\" loading=\"lazy\" src=\"https://daoanhthanh.vn/assets/images/file_permission-5c45c9c4bfe45862ccbdc756cbf1adbd.png\" width=\"732\" height=\"55\" class=\"img_IV9f\">\n</p><figcaption>Hãy để ý 9 ký tự phần khoanh đỏ, đó chính là 'permission group' của file</figcaption><p></p></figure>\n<p>Đầu tiên, hãy nghĩ về chín ký tự nêu trên như là 3 tập hợp, mỗi tập hợp 3 ký tự. Mỗi tập hợp lần lượt định nghĩa quyền cho một đối tượng cụ thể, lần lượt như sau:</p>\n<table><thead><tr><th style=\"text-align:center\"><span style=\"font-weight:bold;color:#2c8261\">r</span><span style=\"font-weight:bold;color:#8d6953\">w</span><span style=\"font-weight:bold;color:#ab1924\">x</span></th><th style=\"text-align:center\"><span style=\"font-weight:bold;color:#2c8261\">r</span><span style=\"font-weight:bold;color:#8d6953\">w</span>-</th><th style=\"text-align:center\"><span style=\"font-weight:bold;color:#2c8261\">r</span>--</th></tr></thead><tbody><tr><td style=\"text-align:center\"><strong>u</strong>ser</td><td style=\"text-align:center\"><strong>g</strong>roup</td><td style=\"text-align:center\"><strong>o</strong>ther</td></tr></tbody></table>\n<p>Mỗi một tập hợp <span style=\"font-weight:bold;color:#2c8261\">r</span><span style=\"font-weight:bold;color:#8d6953\">w</span><span style=\"font-weight:bold;color:#ab1924\">x</span> định nghĩa những hành động khác nhau được phép làm với file tương ứng. Trong trường hợp với file <code>sayHi.sh</code> như ví dụ trên ảnh, ta có thể hiểu rằng:</p>\n<table><thead><tr><th>Nhóm quyền</th><th>Đối tượng</th><th>Cách hiểu</th></tr></thead><tbody><tr><td><span style=\"font-weight:bold;color:#2c8261\">r</span><span style=\"font-weight:bold;color:#8d6953\">w</span><span style=\"font-weight:bold;color:#ab1924\">x</span></td><td><strong>u</strong>ser</td><td>Người dùng (ở đây mang nghĩa 'owner') <span style=\"color:#ffffd7\">thanh_da</span> - có toàn quyền đọc, sửa, và thực thi file.</td></tr><tr><td><span style=\"font-weight:bold;color:#2c8261\">r</span><span style=\"font-weight:bold;color:#8d6953\">w</span>-</td><td><strong>g</strong>roup</td><td>các thành viên khác trong nhóm <span style=\"color:#d7d7af\">developers</span> sẽ có quyền đọc và sửa file.</td></tr><tr><td><span style=\"font-weight:bold;color:#2c8261\">r</span>--</td><td><strong>o</strong>ther</td><td>Những người khác sẽ chỉ có quyền đọc</td></tr></tbody></table>\n<p>Ví dụ:</p>\n<p>Nếu người đang đăng nhập là <span style=\"color:#ffffd7\">thanh_da</span>, anh ấy có thể thực thi file như sau:</p>\n<p><img decoding=\"async\" loading=\"lazy\" src=\"https://daoanhthanh.vn/assets/images/admin_execution-6663b3f6cebfbb2f2e807e5044861e05.png\" width=\"646\" height=\"278\" class=\"img_IV9f\"></p>\n<p>Giả sử trong nhóm <code>developers</code> có thêm một thành viên nữa:</p>\n<p><img decoding=\"async\" loading=\"lazy\" alt=\"Hoặc có thể dùng lib command &amp;#39;members group_name&amp;#39;\" src=\"https://daoanhthanh.vn/assets/images/list_user_in_group-282c61acf115e4dd7c23ced673ec12de.png\" width=\"429\" height=\"115\" class=\"img_IV9f\"> Thì khi thực thi bằng <code>normal_user</code>, ta kỳ vọng hệ thống sẽ không cho phép vì ngoài <span style=\"color:#ffffd7\">thanh_da</span> ra, không ai có quyền thực thi file. Cùng xem kết quả:</p>\n<p><img decoding=\"async\" loading=\"lazy\" src=\"https://daoanhthanh.vn/assets/images/user_execution-c8a829b037c80249e2eb5babf89aee8b.png\" width=\"456\" height=\"147\" class=\"img_IV9f\"></p>\n<p>Vậy là ta đã thiết lập được kiến thức cơ bản về quyền của file trong linux.</p>\n<h2 class=\"anchor anchorTargetHideOnScrollNavbar_Omnb\" id=\"chỉnh-sửa-file-permission\">Chỉnh sửa file permission<a href=\"https://daoanhthanh.vn/blog/linux-file-permission#ch%E1%BB%89nh-s%E1%BB%ADa-file-permission\" class=\"hash-link\" aria-label=\"Chuyển đến tiêu đề\" title=\"Chuyển đến tiêu đề\" translate=\"no\">​</a></h2>\n<p>Ta làm vậy bằng cách <em>thêm</em> hoặc <em>bớt</em> quyền cho một nhóm. Dưới đây là các phép toán hỗ trợ:</p>\n<table><thead><tr><th style=\"text-align:center\"><strong>Operators</strong></th><th style=\"text-align:center\"><strong>Definition</strong></th></tr></thead><tbody><tr><td style=\"text-align:center\"><strong><code>+</code></strong></td><td style=\"text-align:center\">Thêm quyền</td></tr><tr><td style=\"text-align:center\"><strong><code>-</code></strong></td><td style=\"text-align:center\">Xóa quyền</td></tr><tr><td style=\"text-align:center\"><strong><code>=</code></strong></td><td style=\"text-align:center\">Gán quyền</td></tr></tbody></table>\n<p>Thao tác như sau:</p>\n<ol>\n<li class=\"\">Đầu tiên gõ <em>chmod &lt;đối_tượng _muốn_chỉnh_sửa&gt;</em>. Ví dụ đối tượng ở đây là <em>g</em>roup:</li>\n</ol>\n<div class=\"language-bash codeBlockContainer_APcc theme-code-block\" style=\"--prism-background-color:hsl(230, 1%, 98%);--prism-color:hsl(230, 8%, 24%)\"><div class=\"codeBlockContent_m3Ux\"><pre tabindex=\"0\" class=\"prism-code language-bash codeBlock_qGQc thin-scrollbar\" style=\"background-color:hsl(230, 1%, 98%);color:hsl(230, 8%, 24%)\"><code class=\"codeBlockLines_p187\"><span class=\"token-line\" style=\"color:hsl(230, 8%, 24%)\"><span class=\"token function\" style=\"color:hsl(221, 87%, 60%)\">chmod</span><span class=\"token plain\"> g</span><br></span></code></pre><div class=\"buttonGroup_6DOT\"><button type=\"button\" aria-label=\"Sao chép mã nguồn\" title=\"Sao chép\" class=\"clean-btn\"><span class=\"copyButtonIcons_FhaS\" aria-hidden=\"true\"><svg viewBox=\"0 0 24 24\" class=\"copyButtonIcon_phi_\"><path fill=\"currentColor\" d=\"M19,21H8V7H19M19,5H8A2,2 0 0,0 6,7V21A2,2 0 0,0 8,23H19A2,2 0 0,0 21,21V7A2,2 0 0,0 19,5M16,1H4A2,2 0 0,0 2,3V17H4V3H16V1Z\"></path></svg><svg viewBox=\"0 0 24 24\" class=\"copyButtonSuccessIcon_FfTR\"><path fill=\"currentColor\" d=\"M21,7L9,19L3.5,13.5L4.91,12.09L9,16.17L19.59,5.59L21,7Z\"></path></svg></span></button></div></div></div>\n<ol start=\"2\">\n<li class=\"\">Ta muốn đối tượng này được THÊM quyền <span style=\"font-weight:bold;color:#ab1924\">execute</span>:</li>\n</ol>\n<div class=\"language-bash codeBlockContainer_APcc theme-code-block\" style=\"--prism-background-color:hsl(230, 1%, 98%);--prism-color:hsl(230, 8%, 24%)\"><div class=\"codeBlockContent_m3Ux\"><pre tabindex=\"0\" class=\"prism-code language-bash codeBlock_qGQc thin-scrollbar\" style=\"background-color:hsl(230, 1%, 98%);color:hsl(230, 8%, 24%)\"><code class=\"codeBlockLines_p187\"><span class=\"token-line\" style=\"color:hsl(230, 8%, 24%)\"><span class=\"token function\" style=\"color:hsl(221, 87%, 60%)\">chmod</span><span class=\"token plain\"> g+x</span><br></span></code></pre><div class=\"buttonGroup_6DOT\"><button type=\"button\" aria-label=\"Sao chép mã nguồn\" title=\"Sao chép\" class=\"clean-btn\"><span class=\"copyButtonIcons_FhaS\" aria-hidden=\"true\"><svg viewBox=\"0 0 24 24\" class=\"copyButtonIcon_phi_\"><path fill=\"currentColor\" d=\"M19,21H8V7H19M19,5H8A2,2 0 0,0 6,7V21A2,2 0 0,0 8,23H19A2,2 0 0,0 21,21V7A2,2 0 0,0 19,5M16,1H4A2,2 0 0,0 2,3V17H4V3H16V1Z\"></path></svg><svg viewBox=\"0 0 24 24\" class=\"copyButtonSuccessIcon_FfTR\"><path fill=\"currentColor\" d=\"M21,7L9,19L3.5,13.5L4.91,12.09L9,16.17L19.59,5.59L21,7Z\"></path></svg></span></button></div></div></div>\n<ol start=\"3\">\n<li class=\"\">Cuối cùng ta chỉ định file nào sẽ chịu tác động:</li>\n</ol>\n<div class=\"language-bash codeBlockContainer_APcc theme-code-block\" style=\"--prism-background-color:hsl(230, 1%, 98%);--prism-color:hsl(230, 8%, 24%)\"><div class=\"codeBlockContent_m3Ux\"><pre tabindex=\"0\" class=\"prism-code language-bash codeBlock_qGQc thin-scrollbar\" style=\"background-color:hsl(230, 1%, 98%);color:hsl(230, 8%, 24%)\"><code class=\"codeBlockLines_p187\"><span class=\"token-line\" style=\"color:hsl(230, 8%, 24%)\"><span class=\"token function\" style=\"color:hsl(221, 87%, 60%)\">chmod</span><span class=\"token plain\"> g+x ./sayHi.sh</span><br></span></code></pre><div class=\"buttonGroup_6DOT\"><button type=\"button\" aria-label=\"Sao chép mã nguồn\" title=\"Sao chép\" class=\"clean-btn\"><span class=\"copyButtonIcons_FhaS\" aria-hidden=\"true\"><svg viewBox=\"0 0 24 24\" class=\"copyButtonIcon_phi_\"><path fill=\"currentColor\" d=\"M19,21H8V7H19M19,5H8A2,2 0 0,0 6,7V21A2,2 0 0,0 8,23H19A2,2 0 0,0 21,21V7A2,2 0 0,0 19,5M16,1H4A2,2 0 0,0 2,3V17H4V3H16V1Z\"></path></svg><svg viewBox=\"0 0 24 24\" class=\"copyButtonSuccessIcon_FfTR\"><path fill=\"currentColor\" d=\"M21,7L9,19L3.5,13.5L4.91,12.09L9,16.17L19.59,5.59L21,7Z\"></path></svg></span></button></div></div></div>\n<p>Sau thao tác trên, cùng xem lại thay đổi của nhóm quyền của file <code>sayHi.sh</code></p>\n<p>--- TODO: chụp cái ảnh vào đây ---</p>\n<p>Ta có thể sửa nhiều nhóm quyền cùng lúc. Ví dụ, nếu bạn muốn xóa hết quyền cho toàn bộ mọi người, bạn làm như sau:</p>\n<div class=\"language-bash codeBlockContainer_APcc theme-code-block\" style=\"--prism-background-color:hsl(230, 1%, 98%);--prism-color:hsl(230, 8%, 24%)\"><div class=\"codeBlockContent_m3Ux\"><pre tabindex=\"0\" class=\"prism-code language-bash codeBlock_qGQc thin-scrollbar\" style=\"background-color:hsl(230, 1%, 98%);color:hsl(230, 8%, 24%)\"><code class=\"codeBlockLines_p187\"><span class=\"token-line\" style=\"color:hsl(230, 8%, 24%)\"><span class=\"token function\" style=\"color:hsl(221, 87%, 60%)\">chmod</span><span class=\"token plain\"> ugo-rwx ./sayHi.sh</span><br></span></code></pre><div class=\"buttonGroup_6DOT\"><button type=\"button\" aria-label=\"Sao chép mã nguồn\" title=\"Sao chép\" class=\"clean-btn\"><span class=\"copyButtonIcons_FhaS\" aria-hidden=\"true\"><svg viewBox=\"0 0 24 24\" class=\"copyButtonIcon_phi_\"><path fill=\"currentColor\" d=\"M19,21H8V7H19M19,5H8A2,2 0 0,0 6,7V21A2,2 0 0,0 8,23H19A2,2 0 0,0 21,21V7A2,2 0 0,0 19,5M16,1H4A2,2 0 0,0 2,3V17H4V3H16V1Z\"></path></svg><svg viewBox=\"0 0 24 24\" class=\"copyButtonSuccessIcon_FfTR\"><path fill=\"currentColor\" d=\"M21,7L9,19L3.5,13.5L4.91,12.09L9,16.17L19.59,5.59L21,7Z\"></path></svg></span></button></div></div></div>\n<p>Lệnh bên trên sẽ lấy hết quyền đọc (<span style=\"font-weight:bold;color:#2c8261\">r</span>), ghi (<span style=\"font-weight:bold;color:#8d6953\">w</span>) và thực thi (<span style=\"font-weight:bold;color:#ab1924\">x</span>) từ tất cả người dùng (<strong>u</strong>), nhóm (<strong>g</strong>) và những người khác (<strong>o</strong>). Điều này áp dụng cho file <code>sayHi.sh</code>.</p>\n<p>Hoặc ta có thể gán nhiều nhóm với nhiều mức quyền khác nhau cùng lúc, ví dụ:</p>\n<div class=\"language-bash codeBlockContainer_APcc theme-code-block\" style=\"--prism-background-color:hsl(230, 1%, 98%);--prism-color:hsl(230, 8%, 24%)\"><div class=\"codeBlockContent_m3Ux\"><pre tabindex=\"0\" class=\"prism-code language-bash codeBlock_qGQc thin-scrollbar\" style=\"background-color:hsl(230, 1%, 98%);color:hsl(230, 8%, 24%)\"><code class=\"codeBlockLines_p187\"><span class=\"token-line\" style=\"color:hsl(230, 8%, 24%)\"><span class=\"token function\" style=\"color:hsl(221, 87%, 60%)\">chmod</span><span class=\"token plain\"> ug+rw,o-x abc.xyz</span><br></span></code></pre><div class=\"buttonGroup_6DOT\"><button type=\"button\" aria-label=\"Sao chép mã nguồn\" title=\"Sao chép\" class=\"clean-btn\"><span class=\"copyButtonIcons_FhaS\" aria-hidden=\"true\"><svg viewBox=\"0 0 24 24\" class=\"copyButtonIcon_phi_\"><path fill=\"currentColor\" d=\"M19,21H8V7H19M19,5H8A2,2 0 0,0 6,7V21A2,2 0 0,0 8,23H19A2,2 0 0,0 21,21V7A2,2 0 0,0 19,5M16,1H4A2,2 0 0,0 2,3V17H4V3H16V1Z\"></path></svg><svg viewBox=\"0 0 24 24\" class=\"copyButtonSuccessIcon_FfTR\"><path fill=\"currentColor\" d=\"M21,7L9,19L3.5,13.5L4.91,12.09L9,16.17L19.59,5.59L21,7Z\"></path></svg></span></button></div></div></div>\n<p>Như trên là ta đang thêm quyền đọc (<span style=\"font-weight:bold;color:#2c8261\">r</span>), ghi (<span style=\"font-weight:bold;color:#8d6953\">w</span>) cho người dùng (<strong>u</strong>) và nhóm (<strong>g</strong>); cùng lúc xóa bỏ quyền thực thi (<span style=\"font-weight:bold;color:#ab1924\">x</span>) cho những người dùng khác (<strong>o</strong>).</p>\n<p>Lệnh sau:</p>\n<div class=\"language-bash codeBlockContainer_APcc theme-code-block\" style=\"--prism-background-color:hsl(230, 1%, 98%);--prism-color:hsl(230, 8%, 24%)\"><div class=\"codeBlockContent_m3Ux\"><pre tabindex=\"0\" class=\"prism-code language-bash codeBlock_qGQc thin-scrollbar\" style=\"background-color:hsl(230, 1%, 98%);color:hsl(230, 8%, 24%)\"><code class=\"codeBlockLines_p187\"><span class=\"token-line\" style=\"color:hsl(230, 8%, 24%)\"><span class=\"token function\" style=\"color:hsl(221, 87%, 60%)\">chmod</span><span class=\"token plain\"> </span><span class=\"token assign-left variable\" style=\"color:hsl(221, 87%, 60%)\">ug</span><span class=\"token operator\" style=\"color:hsl(221, 87%, 60%)\">=</span><span class=\"token plain\">rw,o</span><span class=\"token operator\" style=\"color:hsl(221, 87%, 60%)\">=</span><span class=\"token plain\">x abc.d</span><br></span></code></pre><div class=\"buttonGroup_6DOT\"><button type=\"button\" aria-label=\"Sao chép mã nguồn\" title=\"Sao chép\" class=\"clean-btn\"><span class=\"copyButtonIcons_FhaS\" aria-hidden=\"true\"><svg viewBox=\"0 0 24 24\" class=\"copyButtonIcon_phi_\"><path fill=\"currentColor\" d=\"M19,21H8V7H19M19,5H8A2,2 0 0,0 6,7V21A2,2 0 0,0 8,23H19A2,2 0 0,0 21,21V7A2,2 0 0,0 19,5M16,1H4A2,2 0 0,0 2,3V17H4V3H16V1Z\"></path></svg><svg viewBox=\"0 0 24 24\" class=\"copyButtonSuccessIcon_FfTR\"><path fill=\"currentColor\" d=\"M21,7L9,19L3.5,13.5L4.91,12.09L9,16.17L19.59,5.59L21,7Z\"></path></svg></span></button></div></div></div>\n<p><em>GÁN CHÍNH XÁC</em> quyền đọc (<span style=\"font-weight:bold;color:#2c8261\">r</span>), ghi (<span style=\"font-weight:bold;color:#8d6953\">w</span>) cho người dùng (<strong>u</strong>) và nhóm (<strong>g</strong>); cùng lúc <em>GÁN CHÍNH XÁC</em> quyền thực thi (<span style=\"font-weight:bold;color:#ab1924\">x</span>) cho những người dùng khác (<strong>o</strong>).</p>\n<p>Điều này có nghĩa là bất kể trước đó <strong>u</strong>ser và <strong>g</strong>roup có những quyền gì, sau khi chạy lệnh trên sẽ chỉ còn quyền <span style=\"font-weight:bold;color:#2c8261\">read</span> và <span style=\"font-weight:bold;color:#8d6953\">write</span>. Tương tự với <strong>o</strong>thers, bất kể trước đó có quyền gì thì sau khi chạy lệnh, sẽ chỉ còn <span style=\"font-weight:bold;color:#ab1924\">execute</span>.</p>\n<p>Ví dụ như sau:</p>\n<p>--- TODO: thêm ảnh ---</p>\n<h2 class=\"anchor anchorTargetHideOnScrollNavbar_Omnb\" id=\"octal-notation\">Octal notation<a href=\"https://daoanhthanh.vn/blog/linux-file-permission#octal-notation\" class=\"hash-link\" aria-label=\"Chuyển đến tiêu đề\" title=\"Chuyển đến tiêu đề\" translate=\"no\">​</a></h2>\n<p>Một set quyền luôn là tập hợp <span style=\"font-weight:bold;color:#2c8261\">r</span><span style=\"font-weight:bold;color:#8d6953\">w</span><span style=\"font-weight:bold;color:#ab1924\">x</span>, do vậy ta có thể dùng hệ nhị phân 3-bit để biểu diễn chúng.</p>\n<p>Ví dụ:</p>\n<table><thead><tr><th style=\"text-align:center\">File permissions</th><th style=\"text-align:center\">Binary</th></tr></thead><tbody><tr><td style=\"text-align:center\"><span style=\"font-weight:bold;color:#2c8261\">r</span><span style=\"font-weight:bold;color:#8d6953\">w</span><span style=\"font-weight:bold;color:#ab1924\">x</span></td><td style=\"text-align:center\">111</td></tr><tr><td style=\"text-align:center\"><span style=\"font-weight:bold;color:#2c8261\">r</span>-<span style=\"font-weight:bold;color:#ab1924\">x</span></td><td style=\"text-align:center\">101</td></tr><tr><td style=\"text-align:center\"><span style=\"font-weight:bold;color:#2c8261\">r</span>--</td><td style=\"text-align:center\">100</td></tr></tbody></table>\n<p>Và mỗi số nhị phân ta lại có thể biểu diễn bằng một số thập phân. Dưới đây là bảng chuyển đổi đầy đủ</p>\n<table><thead><tr><th>File Mode</th><th>Binary</th><th>Octal</th></tr></thead><tbody><tr><td>---</td><td>000</td><td>0</td></tr><tr><td>--<span style=\"font-weight:bold;color:#ab1924\">x</span></td><td>001</td><td>1</td></tr><tr><td>-<span style=\"font-weight:bold;color:#8d6953\">w</span>-</td><td>010</td><td>2</td></tr><tr><td>-<span style=\"font-weight:bold;color:#8d6953\">w</span><span style=\"font-weight:bold;color:#ab1924\">x</span></td><td>011</td><td>3</td></tr><tr><td><span style=\"font-weight:bold;color:#2c8261\">r</span>--</td><td>100</td><td>4</td></tr><tr><td><span style=\"font-weight:bold;color:#2c8261\">r</span>-<span style=\"font-weight:bold;color:#ab1924\">x</span></td><td>101</td><td>5</td></tr><tr><td><span style=\"font-weight:bold;color:#2c8261\">r</span><span style=\"font-weight:bold;color:#8d6953\">w</span>-</td><td>110</td><td>6</td></tr><tr><td><span style=\"font-weight:bold;color:#2c8261\">r</span><span style=\"font-weight:bold;color:#8d6953\">w</span><span style=\"font-weight:bold;color:#ab1924\">x</span></td><td>111</td><td>7</td></tr></tbody></table>\n<p>Do vậy ta có thể thay thế operation bằng octal notation. Ví dụ, hai lệnh dưới đây là tương đương:</p>\n<div class=\"language-bash codeBlockContainer_APcc theme-code-block\" style=\"--prism-background-color:hsl(230, 1%, 98%);--prism-color:hsl(230, 8%, 24%)\"><div class=\"codeBlockContent_m3Ux\"><pre tabindex=\"0\" class=\"prism-code language-bash codeBlock_qGQc thin-scrollbar\" style=\"background-color:hsl(230, 1%, 98%);color:hsl(230, 8%, 24%)\"><code class=\"codeBlockLines_p187\"><span class=\"token-line\" style=\"color:hsl(230, 8%, 24%)\"><span class=\"token function\" style=\"color:hsl(221, 87%, 60%)\">chmod</span><span class=\"token plain\"> </span><span class=\"token assign-left variable\" style=\"color:hsl(221, 87%, 60%)\">u</span><span class=\"token operator\" style=\"color:hsl(221, 87%, 60%)\">=</span><span class=\"token plain\">rwx,g</span><span class=\"token operator\" style=\"color:hsl(221, 87%, 60%)\">=</span><span class=\"token plain\">rx,o</span><span class=\"token operator\" style=\"color:hsl(221, 87%, 60%)\">=</span><span class=\"token plain\">r </span><span class=\"token punctuation\" style=\"color:hsl(119, 34%, 47%)\">[</span><span class=\"token plain\">file_name</span><span class=\"token punctuation\" style=\"color:hsl(119, 34%, 47%)\">]</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:hsl(230, 8%, 24%)\"><span class=\"token plain\"></span><span class=\"token function\" style=\"color:hsl(221, 87%, 60%)\">chmod</span><span class=\"token plain\"> </span><span class=\"token number\" style=\"color:hsl(35, 99%, 36%)\">754</span><span class=\"token plain\"> </span><span class=\"token punctuation\" style=\"color:hsl(119, 34%, 47%)\">[</span><span class=\"token plain\">file_name</span><span class=\"token punctuation\" style=\"color:hsl(119, 34%, 47%)\">]</span><span class=\"token plain\"> </span><span class=\"token comment\" style=\"color:hsl(230, 4%, 64%)\"># (owner: rwx(7), group: r-x(5), others: r–-(4)).</span><br></span></code></pre><div class=\"buttonGroup_6DOT\"><button type=\"button\" aria-label=\"Sao chép mã nguồn\" title=\"Sao chép\" class=\"clean-btn\"><span class=\"copyButtonIcons_FhaS\" aria-hidden=\"true\"><svg viewBox=\"0 0 24 24\" class=\"copyButtonIcon_phi_\"><path fill=\"currentColor\" d=\"M19,21H8V7H19M19,5H8A2,2 0 0,0 6,7V21A2,2 0 0,0 8,23H19A2,2 0 0,0 21,21V7A2,2 0 0,0 19,5M16,1H4A2,2 0 0,0 2,3V17H4V3H16V1Z\"></path></svg><svg viewBox=\"0 0 24 24\" class=\"copyButtonSuccessIcon_FfTR\"><path fill=\"currentColor\" d=\"M21,7L9,19L3.5,13.5L4.91,12.09L9,16.17L19.59,5.59L21,7Z\"></path></svg></span></button></div></div></div>\n<p>hoặc</p>\n<div class=\"language-bash codeBlockContainer_APcc theme-code-block\" style=\"--prism-background-color:hsl(230, 1%, 98%);--prism-color:hsl(230, 8%, 24%)\"><div class=\"codeBlockContent_m3Ux\"><pre tabindex=\"0\" class=\"prism-code language-bash codeBlock_qGQc thin-scrollbar\" style=\"background-color:hsl(230, 1%, 98%);color:hsl(230, 8%, 24%)\"><code class=\"codeBlockLines_p187\"><span class=\"token-line\" style=\"color:hsl(230, 8%, 24%)\"><span class=\"token function\" style=\"color:hsl(221, 87%, 60%)\">chmod</span><span class=\"token plain\"> ugo+rwx </span><span class=\"token punctuation\" style=\"color:hsl(119, 34%, 47%)\">[</span><span class=\"token plain\">file_name</span><span class=\"token punctuation\" style=\"color:hsl(119, 34%, 47%)\">]</span><span class=\"token plain\"> </span><span class=\"token comment\" style=\"color:hsl(230, 4%, 64%)\"># thêm full quyền cho cả 3 nhóm</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:hsl(230, 8%, 24%)\"><span class=\"token plain\"></span><span class=\"token function\" style=\"color:hsl(221, 87%, 60%)\">chmod</span><span class=\"token plain\"> </span><span class=\"token number\" style=\"color:hsl(35, 99%, 36%)\">777</span><span class=\"token plain\"> </span><span class=\"token punctuation\" style=\"color:hsl(119, 34%, 47%)\">[</span><span class=\"token plain\">file_name</span><span class=\"token punctuation\" style=\"color:hsl(119, 34%, 47%)\">]</span><span class=\"token plain\">     </span><span class=\"token comment\" style=\"color:hsl(230, 4%, 64%)\"># tương đương lệnh phía trên</span><br></span></code></pre><div class=\"buttonGroup_6DOT\"><button type=\"button\" aria-label=\"Sao chép mã nguồn\" title=\"Sao chép\" class=\"clean-btn\"><span class=\"copyButtonIcons_FhaS\" aria-hidden=\"true\"><svg viewBox=\"0 0 24 24\" class=\"copyButtonIcon_phi_\"><path fill=\"currentColor\" d=\"M19,21H8V7H19M19,5H8A2,2 0 0,0 6,7V21A2,2 0 0,0 8,23H19A2,2 0 0,0 21,21V7A2,2 0 0,0 19,5M16,1H4A2,2 0 0,0 2,3V17H4V3H16V1Z\"></path></svg><svg viewBox=\"0 0 24 24\" class=\"copyButtonSuccessIcon_FfTR\"><path fill=\"currentColor\" d=\"M21,7L9,19L3.5,13.5L4.91,12.09L9,16.17L19.59,5.59L21,7Z\"></path></svg></span></button></div></div></div>\n<p>hoặc thậm chí:</p>\n<div class=\"language-bash codeBlockContainer_APcc theme-code-block\" style=\"--prism-background-color:hsl(230, 1%, 98%);--prism-color:hsl(230, 8%, 24%)\"><div class=\"codeBlockContent_m3Ux\"><pre tabindex=\"0\" class=\"prism-code language-bash codeBlock_qGQc thin-scrollbar\" style=\"background-color:hsl(230, 1%, 98%);color:hsl(230, 8%, 24%)\"><code class=\"codeBlockLines_p187\"><span class=\"token-line\" style=\"color:hsl(230, 8%, 24%)\"><span class=\"token function\" style=\"color:hsl(221, 87%, 60%)\">chmod</span><span class=\"token plain\"> ug+rwx,o</span><span class=\"token operator\" style=\"color:hsl(221, 87%, 60%)\">=</span><span class=\"token plain\">rx </span><span class=\"token punctuation\" style=\"color:hsl(119, 34%, 47%)\">[</span><span class=\"token plain\">file_name</span><span class=\"token punctuation\" style=\"color:hsl(119, 34%, 47%)\">]</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:hsl(230, 8%, 24%)\"><span class=\"token plain\"></span><span class=\"token function\" style=\"color:hsl(221, 87%, 60%)\">chmod</span><span class=\"token plain\"> </span><span class=\"token number\" style=\"color:hsl(35, 99%, 36%)\">775</span><span class=\"token plain\"> </span><span class=\"token punctuation\" style=\"color:hsl(119, 34%, 47%)\">[</span><span class=\"token plain\">file_name</span><span class=\"token punctuation\" style=\"color:hsl(119, 34%, 47%)\">]</span><br></span></code></pre><div class=\"buttonGroup_6DOT\"><button type=\"button\" aria-label=\"Sao chép mã nguồn\" title=\"Sao chép\" class=\"clean-btn\"><span class=\"copyButtonIcons_FhaS\" aria-hidden=\"true\"><svg viewBox=\"0 0 24 24\" class=\"copyButtonIcon_phi_\"><path fill=\"currentColor\" d=\"M19,21H8V7H19M19,5H8A2,2 0 0,0 6,7V21A2,2 0 0,0 8,23H19A2,2 0 0,0 21,21V7A2,2 0 0,0 19,5M16,1H4A2,2 0 0,0 2,3V17H4V3H16V1Z\"></path></svg><svg viewBox=\"0 0 24 24\" class=\"copyButtonSuccessIcon_FfTR\"><path fill=\"currentColor\" d=\"M21,7L9,19L3.5,13.5L4.91,12.09L9,16.17L19.59,5.59L21,7Z\"></path></svg></span></button></div></div></div>\n<p>Việc phân quyền cho file cũng giống như phần quyền cho bất cứ hệ thống nào khác, cần tuân thủ [[Principle of Least Privilege PoLP]] - Nguyên tắc đặc quyền tối thiểu - để giảm tối đa rủi cho cho hệ thống khi có nhiều người cùng thao tác.</p>",
            "url": "https://daoanhthanh.vn/blog/linux-file-permission",
            "title": "Linux file permissions",
            "summary": "Linux file permissions",
            "date_modified": "2024-12-21T00:00:00.000Z",
            "author": {
                "name": "Đào Anh Thành",
                "url": "https://github.com/daoanhthanh"
            },
            "tags": [
                "linux"
            ]
        },
        {
            "id": "https://daoanhthanh.vn/blog/chuyen-thu-nhap-va-zig",
            "content_html": "<p>Mơ ước của đa số sinh viên mới tốt nghiệp, đặc biệt trong lĩnh vực Công nghệ Thông tin, là tìm kiếm một công việc có mức lương hấp dẫn. Là một lập trình viên trẻ, tôi cũng không nằm ngoài xu thế này. Chính vì vậy, tôi nhận thấy vấn đề này vô cùng thú vị và đáng để thảo luận sâu sắc.</p>\n<!-- -->\n<p>Như một thói quen, mỗi đầu năm tôi đều lên kế hoạch lớn cho năm đó, gồm mục tiêu thu nhập, phát triển bản thân và gia đình. Một trong những thống kê tôi đọc là thu nhập của các lập trình viên khác trên thế giới.</p>\n<figure><p><img decoding=\"async\" loading=\"lazy\" alt=\"interface\" src=\"https://daoanhthanh.vn/assets/images/top_15_highest_paid_language-5f843786026422b525879461bfcc16c3.png\" width=\"2040\" height=\"1350\" class=\"img_IV9f\"></p><figcaption><p>Top 15 ngôn ngữ lập trình có mức lương cao nhất theo khảo sát của Stack Overflow. Chi tiết xem\ntại\n<a href=\"https://survey.stackoverflow.co/2023/#section-top-paying-technologies-top-paying-technologies\" target=\"_blank\" rel=\"noopener noreferrer\">đây</a>.</p></figcaption></figure>\n<p>Khảo khảo sát trên gồm gần 50.000 lập trình viên tham gia</p>",
            "url": "https://daoanhthanh.vn/blog/chuyen-thu-nhap-va-zig",
            "title": "Chuyện thu nhập và Zig",
            "summary": "Zig là một ngôn ngữ lập trình mới, nhưng lương của lập trình viên Zig lại cao hơn so với các ngôn ngữ khác. Bài viết này sẽ giải thích lý do tại sao lương lập trình viên Zig lại cao.",
            "date_modified": "2024-09-13T00:00:00.000Z",
            "author": {
                "name": "Đào Anh Thành",
                "url": "https://github.com/daoanhthanh"
            },
            "tags": [
                "zig"
            ]
        },
        {
            "id": "https://daoanhthanh.vn/blog/ports-and-adapters-architecture-in-net-core",
            "content_html": "<p>Gần đây tôi có tiếp xúc với C#, cảm thấy ngôn ngữ này khá thú vị và mạnh mẽ. Điều đầu tiên tôi muốn làm khi học C# là tìm hiểu về thiết kế DDD và cách triển khai nó trong .NET Core. Trong bài viết này, tôi sẽ chia sẻ về kiến trúc <code>Ports &amp; Adapters</code> - một cách implement DDD trong .NET Core.</p>\n<!-- -->\n<h2 class=\"anchor anchorTargetHideOnScrollNavbar_Omnb\" id=\"trước-khi-bắt-đầu\">Trước khi bắt đầu<a href=\"https://daoanhthanh.vn/blog/ports-and-adapters-architecture-in-net-core#tr%C6%B0%E1%BB%9Bc-khi-b%E1%BA%AFt-%C4%91%E1%BA%A7u\" class=\"hash-link\" aria-label=\"Chuyển đến tiêu đề\" title=\"Chuyển đến tiêu đề\" translate=\"no\">​</a></h2>\n<p>Bạn nên có kiến thức nền về c# và dotnet: cách làm việc với dotnet cli (hoặc có thể dùng Visual Studio), hiểu được các thuật ngữ (subproject, dependencies, reference) và trên hết là sự tò mò.</p>\n<h2 class=\"anchor anchorTargetHideOnScrollNavbar_Omnb\" id=\"một-kiến-trúc-nữa-ư-chẳng-phải-mvc-đã-là-đủ-rồi-sao\">Một kiến trúc nữa ư? Chẳng phải MVC đã là đủ rồi sao?<a href=\"https://daoanhthanh.vn/blog/ports-and-adapters-architecture-in-net-core#m%E1%BB%99t-ki%E1%BA%BFn-tr%C3%BAc-n%E1%BB%AFa-%C6%B0-ch%E1%BA%B3ng-ph%E1%BA%A3i-mvc-%C4%91%C3%A3-l%C3%A0-%C4%91%E1%BB%A7-r%E1%BB%93i-sao\" class=\"hash-link\" aria-label=\"Chuyển đến tiêu đề\" title=\"Chuyển đến tiêu đề\" translate=\"no\">​</a></h2>\n<p>Trước hết hãy cùng nói về MVC. Chắc hẳn đây là kiến trúc đầu tiên mà mọi lập trình viên được tiếp xúc khi làm việc với web. Nó một kiến trúc phần mềm chia ứng dụng thành 3 phần độc lập với mục đích khác nhau: Model, View, Controler.</p>\n<figure><p><img decoding=\"async\" loading=\"lazy\" alt=\"MVC\" src=\"https://daoanhthanh.vn/assets/images/image6-e63e60f5997cf53ffd6ea236d19a4029.png\" width=\"2940\" height=\"1660\" class=\"img_IV9f\">\n</p><figcaption>Mô tả luồng đi của dữ liệu trong mô hình MVC</figcaption><p></p></figure>\n<p>Luồng đi của ứng dụng sẽ như sau:</p>\n<ol>\n<li class=\"\">Dựa vào URL, request sẽ được gửi đến <code>Controller</code> tương ứng để xử lý. Controller là nơi nhận và validate meta data của request (như quyền của người gửi, validate các trường trong request,..)</li>\n<li class=\"\">Controller gửi yêu cầu xử lý data đến <code>Model</code>. Model là nơi xử lý mọi logic liên quan đến request bao gồm cả việc kết nối với DB. Controller không bao giờ nên kết nối trực tiếp với DB hay xử lý logic. Trong trường hợp xảy ra lỗi, <em>Controller</em> nên là thành phần xử lý lỗi đó để trả cho người dùng. Hay nói cách khác, tất cả logic liên quan đến tương tác người dùng đều được xử lý bởi <em>Controller</em>.</li>\n<li class=\"\">Sau khi nhận lại dữ liệu từ <em>Model</em>, <em>Controller</em> sẽ kết nối đến <code>View</code> để render data trả cho người dùng. <em>View</em> chỉ có tác dụng render giao diện dựa trên data được trả bởi controller và không có xử lý gì thêm.</li>\n</ol>\n<p>Không thể phủ nhận MVC là một kiến trúc hiệu quả. Nhiệm vụ của MVC là phân tách View - Controller - Model, như tên gọi của nó, trong <strong><em>một ứng dụng web</em></strong>, khiến chúng độc lập và dễ bảo trì. Tuy nhiên đây là kiến trúc áp dụng khi source code bao gồm luôn 2 phần: back-end (do <em>Controller</em> và <em>Model</em> đảm nhiệm) và front-end (do <em>View</em> đảm nhiệm).</p>\n<p>Hơn nữa với những dự án lớn, việc logic xử lý chỉ được chia thành <em>Controller</em> và <em>Model</em> chắc chắn không thể đủ để đảm bảo dự án có thể dễ mở rộng và bảo trì.</p>\n<p>Do đó, trong trường hợp source code tách làm 2 phần BE và FE, ta cần một kiến trúc khác áp dụng cho từng phần. Trong giới hạn của bài viết này, tôi sẽ giới thiệu một kiến trúc thường được dùng cho phía BE: <strong>Ports &amp; Adapters</strong></p>\n<h2 class=\"anchor anchorTargetHideOnScrollNavbar_Omnb\" id=\"ports--adapters-là-gì\"><strong>Ports &amp; Adapters</strong> là gì?<a href=\"https://daoanhthanh.vn/blog/ports-and-adapters-architecture-in-net-core#ports--adapters-l%C3%A0-g%C3%AC\" class=\"hash-link\" aria-label=\"Chuyển đến tiêu đề\" title=\"Chuyển đến tiêu đề\" translate=\"no\">​</a></h2>\n<p><strong>Ports &amp; Adapters</strong> - P&amp;A - (hay còn được gọi là <strong>Hexagonal Architecture</strong> - thú thực tôi không thích cái tên này lắm) là một kiến trúc phần mềm tuân theo nguyên lý thiết kế tập trung vào chủ thể. Có thể bạn đã nghe qua cái tên DDD (Domain-Driven Design). Hiểu đơn giản, <strong><em>DDD</em></strong> là tập hợp các nguyên tắc, mẫu thiết kế hệ thống giúp team bạn phát triển phần mềm một cách dễ dàng và hiệu quả. <strong><em>Ports &amp; Adapters</em></strong> là một trong những <strong>cách triển khai code base</strong> để tuân thủ những ý tưởng của <strong><em>DDD</em></strong>.</p>\n<figure><p><img decoding=\"async\" loading=\"lazy\" alt=\"DDD is an idea, P&amp;amp;A is an implementation\" src=\"https://daoanhthanh.vn/assets/images/image1-06adac8deec65295d23db417d30a7a52.png\" width=\"2446\" height=\"1630\" class=\"img_IV9f\">\n</p><figcaption>DDD là ý tưởng/nguyên tắc, còn Ports &amp; Adapters là một cách triển khai</figcaption><p></p></figure>\n<h2 class=\"anchor anchorTargetHideOnScrollNavbar_Omnb\" id=\"triển-khai-kiến-trúc-port--adapter-trong-net-core\">Triển khai kiến trúc Port &amp; Adapter trong .NET Core<a href=\"https://daoanhthanh.vn/blog/ports-and-adapters-architecture-in-net-core#tri%E1%BB%83n-khai-ki%E1%BA%BFn-tr%C3%BAc-port--adapter-trong-net-core\" class=\"hash-link\" aria-label=\"Chuyển đến tiêu đề\" title=\"Chuyển đến tiêu đề\" translate=\"no\">​</a></h2>\n<h3 class=\"anchor anchorTargetHideOnScrollNavbar_Omnb\" id=\"thế-cụ-thể-ý-tưởng-của-ports--adapters-là-gì\">Thế cụ thể, ý tưởng của Ports &amp; Adapters là gì?<a href=\"https://daoanhthanh.vn/blog/ports-and-adapters-architecture-in-net-core#th%E1%BA%BF-c%E1%BB%A5-th%E1%BB%83-%C3%BD-t%C6%B0%E1%BB%9Fng-c%E1%BB%A7a-ports--adapters-l%C3%A0-g%C3%AC\" class=\"hash-link\" aria-label=\"Chuyển đến tiêu đề\" title=\"Chuyển đến tiêu đề\" translate=\"no\">​</a></h3>\n<p>Trước tiên hãy nói một chút về cách tiếp cận vấn đề của MVC. Một góc nào đó, kiến trúc này vẫn mang theo ý tưởng của DDD: bạn có <code>model</code> là những class mô tả tất cả những chủ thể mà ứng dụng làm việc với (vd: config, người dùng, hàng hoá, xe cộ,...). Bạn có <code>service</code> để xử lý mọi nghiệp vụ liên quan đến domain. Ta thường tách Service ra thành - tạm gọi - <strong><em>Service_Interface</em></strong> và <strong><em>Service_Implementation</em></strong> để tối đa trạng thái loosely coupling.</p>\n<p>Tuy nhiên, mô hình của MVC có một vấn đề: nó không có cơ chế hạn chế việc gọi lẫn nhau giữa bất kỳ thành phần nào. Hay nói cách khác, các thành phần không được hoàn toàn tách biệt do vậy một developer có thể <strong>thoải mái truy cập database từ controller</strong>. Điều này rất tệ trong dự án lớn vì nó có thể tạo tiền lệ và phá vỡ kiến trúc tổng thể. Ai mà biết được liệu anh ta có validate và serialize response chuẩn không cơ chứ.</p>\n<p>Do vậy một kiến trúc tốt cần có cơ chế tách biệt hẳn các thành phần trong luồng đi của dữ liệu đồng thời bắt buộc chúng phải nằm trong luồng đi đó. Nói một cách đơn giản: kiến trúc tốt phải khiến dev dù có muốn, họ cũng không thể nào:</p>\n<ol>\n<li class=\"\">Truy cập trực tiếp <strong><em>Service_Implementation</em></strong> từ <strong><em>Controller</em></strong> mà không thông qua <strong><em>Service_Interface</em></strong></li>\n<li class=\"\">Gọi thẳng <strong><em>Database</em></strong> từ <strong><em>Controller</em></strong>.</li>\n</ol>\n<figure><p><img decoding=\"async\" loading=\"lazy\" alt=\"Lazy dev\" src=\"https://daoanhthanh.vn/assets/images/image2-807dc4fc2f406c7083f22e48e49272f9.png\" width=\"1590\" height=\"1592\" class=\"img_IV9f\">\n</p><figcaption>Người duy nhất ngăn điều này xảy ra là code reviewer</figcaption><p></p></figure>\n<p>Đây là lý do mà những kiến trúc như <strong>P&amp;A</strong> tồn tại. Thứ nhất nó phân tách các thành phần trong source code để đảm bảo chúng <em>loosely coupled</em>. Thứ hai nó đảm bảo luồng đi của dữ liệu theo một cấu trúc đồng nhất, dễ đọc dễ bảo trì và dễ mở rộng.</p>\n<p><img decoding=\"async\" loading=\"lazy\" alt=\"PowerPlant\" src=\"https://daoanhthanh.vn/assets/images/image3-aa75d22511bf781a9809468433ccd9e8.png\" width=\"2539\" height=\"1636\" class=\"img_IV9f\"></p>\n<blockquote>\n<p>Hãy hình dung P&amp;A như một mô hình sản xuất điện. Bên yêu cầu là cái quạt, gửi yêu cầu bằng cách cắm phích vào ổ điện. Nhà máy điện là nơi cung cấp dịch vụ điện cho bạn và họ có thể lấy điện từ nhiều nguồn: thuỷ điện hoặc nhiệt điện chẳng hạn.</p>\n</blockquote>\n<p>Tôi sẽ sử dụng <code>Driving Adapters</code>, <code>Driving Ports</code> để ám chỉ phía yêu cầu. <code>Driven Adapter</code>, <code>Driven Ports</code> để nói về bên thực hiện yêu cầu.</p>\n<p><img decoding=\"async\" loading=\"lazy\" alt=\"PowerPlant2\" src=\"https://daoanhthanh.vn/assets/images/image4-6ed4bd9cc886961368a931bf6b614525.png\" width=\"2533\" height=\"1705\" class=\"img_IV9f\"></p>\n<p>Với việc phân chia như trên, giờ luồng đi của dữ liệu đã rõ ràng hơn. Phích cắm không thể lấy điện bằng cách chọc thẳng vào motor phát điện - cũng giống như bạn không thể nào truy cập thẳng vào <strong><em>DB</em></strong> mà không thông qua <strong><em>Service</em></strong>. Đồng thời ta không thể bỏ qua bất kỳ <em>interfaces</em> nào do chúng đều là một mắt xích trong mạng lưới, do đó cấu trúc tổng thể của chương trình được đảm bảo. Hãy cùng xem ta có thể implement kiến trúc thú vị này như thế nào với .NET Core.</p>\n<h3 class=\"anchor anchorTargetHideOnScrollNavbar_Omnb\" id=\"đó-là-lý-thuyết-giờ-hãy-implement-nó-trong-code\">Đó là lý thuyết, giờ hãy implement nó trong code<a href=\"https://daoanhthanh.vn/blog/ports-and-adapters-architecture-in-net-core#%C4%91%C3%B3-l%C3%A0-l%C3%BD-thuy%E1%BA%BFt-gi%E1%BB%9D-h%C3%A3y-implement-n%C3%B3-trong-code\" class=\"hash-link\" aria-label=\"Chuyển đến tiêu đề\" title=\"Chuyển đến tiêu đề\" translate=\"no\">​</a></h3>\n<p>Chúng ta sẽ triển khai P&amp;A qua một dự án CRM nhỏ. Tính năng cần có:</p>\n<ul>\n<li class=\"\">Sử dụng cả Restful API và GraphQL</li>\n<li class=\"\">Có cơ chế reset admin account trong trường hợp họ quên mật khẩu</li>\n<li class=\"\">Sử dụng Postgres để lưu dữ liệu và Redis để lưu cache</li>\n</ul>\n<blockquote>\n<p>Tôi sẽ không tập trung vào logic xử lý trong bài viết này để tập trung nhấn mạnh vào kiến trúc. Hãy tham khảo source code ở phần cuối bài viết.</p>\n</blockquote>\n<p>Cấu trúc thư mục là cách trực quan nhất để trình bày kiến trúc, do vậy hãy bắt đầu với nó.</p>\n<p>Tham khảo cấu trúc thư mục dưới đây:</p>\n<div class=\"language-bash codeBlockContainer_APcc theme-code-block\" style=\"--prism-background-color:hsl(230, 1%, 98%);--prism-color:hsl(230, 8%, 24%)\"><div class=\"codeBlockContent_m3Ux\"><pre tabindex=\"0\" class=\"prism-code language-bash codeBlock_qGQc thin-scrollbar\" style=\"background-color:hsl(230, 1%, 98%);color:hsl(230, 8%, 24%)\"><code class=\"codeBlockLines_p187\"><span class=\"token-line\" style=\"color:hsl(230, 8%, 24%)\"><span class=\"token builtin class-name\" style=\"color:hsl(35, 99%, 36%)\">.</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:hsl(230, 8%, 24%)\"><span class=\"token plain\">├── Adapter</span><br></span><span class=\"token-line\" style=\"color:hsl(230, 8%, 24%)\"><span class=\"token plain\">│   ├── In</span><br></span><span class=\"token-line\" style=\"color:hsl(230, 8%, 24%)\"><span class=\"token plain\">│   │   ├── APIs</span><br></span><span class=\"token-line\" style=\"color:hsl(230, 8%, 24%)\"><span class=\"token plain\">│   │   │   ├── GraphQL</span><br></span><span class=\"token-line\" style=\"color:hsl(230, 8%, 24%)\"><span class=\"token plain\">│   │   │   └── RestfulAPI</span><br></span><span class=\"token-line\" style=\"color:hsl(230, 8%, 24%)\"><span class=\"token plain\">│   │   └── CLI</span><br></span><span class=\"token-line\" style=\"color:hsl(230, 8%, 24%)\"><span class=\"token plain\">│   └── Out</span><br></span><span class=\"token-line\" style=\"color:hsl(230, 8%, 24%)\"><span class=\"token plain\">│       ├── Databases</span><br></span><span class=\"token-line\" style=\"color:hsl(230, 8%, 24%)\"><span class=\"token plain\">│       │   ├── Postgres</span><br></span><span class=\"token-line\" style=\"color:hsl(230, 8%, 24%)\"><span class=\"token plain\">│       │   └── Redis</span><br></span><span class=\"token-line\" style=\"color:hsl(230, 8%, 24%)\"><span class=\"token plain\">│       └── ThirdPartyLibs</span><br></span><span class=\"token-line\" style=\"color:hsl(230, 8%, 24%)\"><span class=\"token plain\">└── Application</span><br></span><span class=\"token-line\" style=\"color:hsl(230, 8%, 24%)\"><span class=\"token plain\">    ├── Domain</span><br></span><span class=\"token-line\" style=\"color:hsl(230, 8%, 24%)\"><span class=\"token plain\">    │   ├── Domain.Core</span><br></span><span class=\"token-line\" style=\"color:hsl(230, 8%, 24%)\"><span class=\"token plain\">    │   ├── Domain.Services</span><br></span><span class=\"token-line\" style=\"color:hsl(230, 8%, 24%)\"><span class=\"token plain\">    │   └── Domain.Services.Tests</span><br></span><span class=\"token-line\" style=\"color:hsl(230, 8%, 24%)\"><span class=\"token plain\">    └── Ports</span><br></span><span class=\"token-line\" style=\"color:hsl(230, 8%, 24%)\"><span class=\"token plain\">        ├── In</span><br></span><span class=\"token-line\" style=\"color:hsl(230, 8%, 24%)\"><span class=\"token plain\">        └── Out</span><br></span></code></pre><div class=\"buttonGroup_6DOT\"><button type=\"button\" aria-label=\"Sao chép mã nguồn\" title=\"Sao chép\" class=\"clean-btn\"><span class=\"copyButtonIcons_FhaS\" aria-hidden=\"true\"><svg viewBox=\"0 0 24 24\" class=\"copyButtonIcon_phi_\"><path fill=\"currentColor\" d=\"M19,21H8V7H19M19,5H8A2,2 0 0,0 6,7V21A2,2 0 0,0 8,23H19A2,2 0 0,0 21,21V7A2,2 0 0,0 19,5M16,1H4A2,2 0 0,0 2,3V17H4V3H16V1Z\"></path></svg><svg viewBox=\"0 0 24 24\" class=\"copyButtonSuccessIcon_FfTR\"><path fill=\"currentColor\" d=\"M21,7L9,19L3.5,13.5L4.91,12.09L9,16.17L19.59,5.59L21,7Z\"></path></svg></span></button></div></div></div>\n<p>Hãy bắt đầu với <em>Adapter</em>, ta có 2 thư mục <code>In</code> và <code>Out</code> tương ứng với <code>Driving Adapter</code> và <code>Driven Adapter</code> (bên gửi - bên xử lý yêu cầu). Trong <code>In</code> ta nhận 2 dạng yêu cầu: <strong>qua command line</strong>, và <strong>qua http</strong> gồm hai giao thức: <strong>GraphQL</strong> và <strong>RestfulAPI</strong>. Tương tự như vậy với <code>Out</code> ta cần giao tiếp với 2 dạng hệ thống bên ngoài: <strong>Databases</strong> và các loại khác.</p>\n<p>Phần còn lại của kiến trúc được gộp trong thư mục <em>Application</em>. Đây là nơi chứa <code>Domain</code> và <code>Ports</code> - thứ chịu trách nhiệm giao tiếp với thế giới bên ngoài nên cũng cần 2 thư mục <code>In</code>/<code>Out</code> tương ứng.</p>\n<p>Khi bạn đã hiểu cấu trúc thư mục, sử dụng lệnh dưới đây để khởi tạo dự án:</p>\n<div class=\"language-bash codeBlockContainer_APcc theme-code-block\" style=\"--prism-background-color:hsl(230, 1%, 98%);--prism-color:hsl(230, 8%, 24%)\"><div class=\"codeBlockContent_m3Ux\"><pre tabindex=\"0\" class=\"prism-code language-bash codeBlock_qGQc thin-scrollbar\" style=\"background-color:hsl(230, 1%, 98%);color:hsl(230, 8%, 24%)\"><code class=\"codeBlockLines_p187\"><span class=\"token-line\" style=\"color:hsl(230, 8%, 24%)\"><span class=\"token comment\" style=\"color:hsl(230, 4%, 64%)\"># Create solution</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:hsl(230, 8%, 24%)\"><span class=\"token plain\">dotnet new sln </span><span class=\"token parameter variable\" style=\"color:hsl(221, 87%, 60%)\">-o</span><span class=\"token plain\"> Ports-and-Adapters-Demo </span><span class=\"token operator\" style=\"color:hsl(221, 87%, 60%)\">&amp;&amp;</span><span class=\"token plain\"> </span><span class=\"token builtin class-name\" style=\"color:hsl(35, 99%, 36%)\">cd</span><span class=\"token plain\"> Ports-and-Adapters-Demo</span><br></span><span class=\"token-line\" style=\"color:hsl(230, 8%, 24%)\"><span class=\"token plain\" style=\"display:inline-block\"></span><br></span><span class=\"token-line\" style=\"color:hsl(230, 8%, 24%)\"><span class=\"token plain\"></span><span class=\"token comment\" style=\"color:hsl(230, 4%, 64%)\"># Create projects</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:hsl(230, 8%, 24%)\"><span class=\"token plain\">dotnet new classlib </span><span class=\"token parameter variable\" style=\"color:hsl(221, 87%, 60%)\">-n</span><span class=\"token plain\"> Adapter.GraphQL </span><span class=\"token parameter variable\" style=\"color:hsl(221, 87%, 60%)\">-o</span><span class=\"token plain\"> Adapter/In/APIs/GraphQL</span><br></span><span class=\"token-line\" style=\"color:hsl(230, 8%, 24%)\"><span class=\"token plain\">dotnet new classlib </span><span class=\"token parameter variable\" style=\"color:hsl(221, 87%, 60%)\">-n</span><span class=\"token plain\"> Adapter.RestfulAPI </span><span class=\"token parameter variable\" style=\"color:hsl(221, 87%, 60%)\">-o</span><span class=\"token plain\"> Adapter/In/APIs/RestfulAPI</span><br></span><span class=\"token-line\" style=\"color:hsl(230, 8%, 24%)\"><span class=\"token plain\">dotnet new classlib </span><span class=\"token parameter variable\" style=\"color:hsl(221, 87%, 60%)\">-n</span><span class=\"token plain\"> Adapter.CLI </span><span class=\"token parameter variable\" style=\"color:hsl(221, 87%, 60%)\">-o</span><span class=\"token plain\"> Adapter/In/CLI</span><br></span><span class=\"token-line\" style=\"color:hsl(230, 8%, 24%)\"><span class=\"token plain\" style=\"display:inline-block\"></span><br></span><span class=\"token-line\" style=\"color:hsl(230, 8%, 24%)\"><span class=\"token plain\">dotnet new classlib </span><span class=\"token parameter variable\" style=\"color:hsl(221, 87%, 60%)\">-n</span><span class=\"token plain\"> Adapter.Redis </span><span class=\"token parameter variable\" style=\"color:hsl(221, 87%, 60%)\">-o</span><span class=\"token plain\"> Adapter/Out/Databases/Redis</span><br></span><span class=\"token-line\" style=\"color:hsl(230, 8%, 24%)\"><span class=\"token plain\">dotnet new classlib </span><span class=\"token parameter variable\" style=\"color:hsl(221, 87%, 60%)\">-n</span><span class=\"token plain\"> Adapter.Postgres </span><span class=\"token parameter variable\" style=\"color:hsl(221, 87%, 60%)\">-o</span><span class=\"token plain\"> Adapter/Out/Databases/Postgres</span><br></span><span class=\"token-line\" style=\"color:hsl(230, 8%, 24%)\"><span class=\"token plain\">dotnet new classlib </span><span class=\"token parameter variable\" style=\"color:hsl(221, 87%, 60%)\">-n</span><span class=\"token plain\"> Adapter.ThirdPartyLibs </span><span class=\"token parameter variable\" style=\"color:hsl(221, 87%, 60%)\">-o</span><span class=\"token plain\"> Adapter/Out/ThirdPartyLibs</span><br></span><span class=\"token-line\" style=\"color:hsl(230, 8%, 24%)\"><span class=\"token plain\" style=\"display:inline-block\"></span><br></span><span class=\"token-line\" style=\"color:hsl(230, 8%, 24%)\"><span class=\"token plain\">dotnet new classlib </span><span class=\"token parameter variable\" style=\"color:hsl(221, 87%, 60%)\">-n</span><span class=\"token plain\"> Domain.Core </span><span class=\"token parameter variable\" style=\"color:hsl(221, 87%, 60%)\">-o</span><span class=\"token plain\"> Application/Domain/Domain.Core</span><br></span><span class=\"token-line\" style=\"color:hsl(230, 8%, 24%)\"><span class=\"token plain\">dotnet new classlib </span><span class=\"token parameter variable\" style=\"color:hsl(221, 87%, 60%)\">-n</span><span class=\"token plain\"> Domain.Services </span><span class=\"token parameter variable\" style=\"color:hsl(221, 87%, 60%)\">-o</span><span class=\"token plain\"> Application/Domain/Domain.Services</span><br></span><span class=\"token-line\" style=\"color:hsl(230, 8%, 24%)\"><span class=\"token plain\">dotnet new xunit </span><span class=\"token parameter variable\" style=\"color:hsl(221, 87%, 60%)\">-n</span><span class=\"token plain\"> Domain.Services.Tests </span><span class=\"token parameter variable\" style=\"color:hsl(221, 87%, 60%)\">-o</span><span class=\"token plain\"> Application/Domain/Domain.Services.Tests</span><br></span><span class=\"token-line\" style=\"color:hsl(230, 8%, 24%)\"><span class=\"token plain\" style=\"display:inline-block\"></span><br></span><span class=\"token-line\" style=\"color:hsl(230, 8%, 24%)\"><span class=\"token plain\">dotnet new classlib </span><span class=\"token parameter variable\" style=\"color:hsl(221, 87%, 60%)\">-n</span><span class=\"token plain\"> Ports.In </span><span class=\"token parameter variable\" style=\"color:hsl(221, 87%, 60%)\">-o</span><span class=\"token plain\"> Application/Ports/In</span><br></span><span class=\"token-line\" style=\"color:hsl(230, 8%, 24%)\"><span class=\"token plain\">dotnet new classlib </span><span class=\"token parameter variable\" style=\"color:hsl(221, 87%, 60%)\">-n</span><span class=\"token plain\"> Ports.Out </span><span class=\"token parameter variable\" style=\"color:hsl(221, 87%, 60%)\">-o</span><span class=\"token plain\"> Application/Ports/Out</span><br></span><span class=\"token-line\" style=\"color:hsl(230, 8%, 24%)\"><span class=\"token plain\" style=\"display:inline-block\"></span><br></span><span class=\"token-line\" style=\"color:hsl(230, 8%, 24%)\"><span class=\"token plain\"></span><span class=\"token comment\" style=\"color:hsl(230, 4%, 64%)\"># Add all projects to solution</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:hsl(230, 8%, 24%)\"><span class=\"token plain\">dotnet sln </span><span class=\"token function\" style=\"color:hsl(221, 87%, 60%)\">add</span><span class=\"token plain\"> **/*.csproj</span><br></span></code></pre><div class=\"buttonGroup_6DOT\"><button type=\"button\" aria-label=\"Sao chép mã nguồn\" title=\"Sao chép\" class=\"clean-btn\"><span class=\"copyButtonIcons_FhaS\" aria-hidden=\"true\"><svg viewBox=\"0 0 24 24\" class=\"copyButtonIcon_phi_\"><path fill=\"currentColor\" d=\"M19,21H8V7H19M19,5H8A2,2 0 0,0 6,7V21A2,2 0 0,0 8,23H19A2,2 0 0,0 21,21V7A2,2 0 0,0 19,5M16,1H4A2,2 0 0,0 2,3V17H4V3H16V1Z\"></path></svg><svg viewBox=\"0 0 24 24\" class=\"copyButtonSuccessIcon_FfTR\"><path fill=\"currentColor\" d=\"M21,7L9,19L3.5,13.5L4.91,12.09L9,16.17L19.59,5.59L21,7Z\"></path></svg></span></button></div></div></div>\n<p>Để đảm ràng buộc phụ thuộc giữa các subproject với nhau cần thêm reference cho các project, sử dụng lệnh sau:</p>\n<div class=\"language-bash codeBlockContainer_APcc theme-code-block\" style=\"--prism-background-color:hsl(230, 1%, 98%);--prism-color:hsl(230, 8%, 24%)\"><div class=\"codeBlockContent_m3Ux\"><pre tabindex=\"0\" class=\"prism-code language-bash codeBlock_qGQc thin-scrollbar\" style=\"background-color:hsl(230, 1%, 98%);color:hsl(230, 8%, 24%)\"><code class=\"codeBlockLines_p187\"><span class=\"token-line\" style=\"color:hsl(230, 8%, 24%)\"><span class=\"token comment\" style=\"color:hsl(230, 4%, 64%)\"># services depends on domain</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:hsl(230, 8%, 24%)\"><span class=\"token plain\">dotnet </span><span class=\"token function\" style=\"color:hsl(221, 87%, 60%)\">add</span><span class=\"token plain\"> **/Domain.Services.csproj reference **/Domain.Core.csproj **/Ports.Out.csproj **/Ports.In.csproj</span><br></span><span class=\"token-line\" style=\"color:hsl(230, 8%, 24%)\"><span class=\"token plain\">dotnet </span><span class=\"token function\" style=\"color:hsl(221, 87%, 60%)\">add</span><span class=\"token plain\"> **/Domain.Services.Tests.csproj reference **/Domain.Core.csproj **/Domain.Services.csproj</span><br></span><span class=\"token-line\" style=\"color:hsl(230, 8%, 24%)\"><span class=\"token plain\" style=\"display:inline-block\"></span><br></span><span class=\"token-line\" style=\"color:hsl(230, 8%, 24%)\"><span class=\"token plain\"></span><span class=\"token comment\" style=\"color:hsl(230, 4%, 64%)\"># adapters depends on domain and ports.</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:hsl(230, 8%, 24%)\"><span class=\"token plain\">dotnet </span><span class=\"token function\" style=\"color:hsl(221, 87%, 60%)\">add</span><span class=\"token plain\"> **/Adapter.GraphQL.csproj reference **/Domain.Core.csproj **/Ports.In.csproj</span><br></span><span class=\"token-line\" style=\"color:hsl(230, 8%, 24%)\"><span class=\"token plain\">dotnet </span><span class=\"token function\" style=\"color:hsl(221, 87%, 60%)\">add</span><span class=\"token plain\"> **/Adapter.RestfulAPI.csproj reference **/Domain.Core.csproj **/Ports.In.csproj</span><br></span><span class=\"token-line\" style=\"color:hsl(230, 8%, 24%)\"><span class=\"token plain\">dotnet </span><span class=\"token function\" style=\"color:hsl(221, 87%, 60%)\">add</span><span class=\"token plain\"> **/Adapter.CLI.csproj reference **/Domain.Core.csproj **/Ports.In.csproj</span><br></span><span class=\"token-line\" style=\"color:hsl(230, 8%, 24%)\"><span class=\"token plain\" style=\"display:inline-block\"></span><br></span><span class=\"token-line\" style=\"color:hsl(230, 8%, 24%)\"><span class=\"token plain\">dotnet </span><span class=\"token function\" style=\"color:hsl(221, 87%, 60%)\">add</span><span class=\"token plain\"> **/Adapter.Redis.csproj reference **/Domain.Core.csproj **/Ports.Out.csproj</span><br></span><span class=\"token-line\" style=\"color:hsl(230, 8%, 24%)\"><span class=\"token plain\">dotnet </span><span class=\"token function\" style=\"color:hsl(221, 87%, 60%)\">add</span><span class=\"token plain\"> **/Adapter.Postgres.csproj reference **/Domain.Core.csproj **/Ports.Out.csproj</span><br></span><span class=\"token-line\" style=\"color:hsl(230, 8%, 24%)\"><span class=\"token plain\">dotnet </span><span class=\"token function\" style=\"color:hsl(221, 87%, 60%)\">add</span><span class=\"token plain\"> **/Adapter.ThirdPartyLibs.csproj reference **/Domain.Core.csproj **/Ports.Out.csproj</span><br></span><span class=\"token-line\" style=\"color:hsl(230, 8%, 24%)\"><span class=\"token plain\" style=\"display:inline-block\"></span><br></span><span class=\"token-line\" style=\"color:hsl(230, 8%, 24%)\"><span class=\"token plain\"></span><span class=\"token comment\" style=\"color:hsl(230, 4%, 64%)\">#ports depend on domain</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:hsl(230, 8%, 24%)\"><span class=\"token plain\">dotnet </span><span class=\"token function\" style=\"color:hsl(221, 87%, 60%)\">add</span><span class=\"token plain\"> **/Ports.In.csproj reference **/Domain.Core.csproj</span><br></span><span class=\"token-line\" style=\"color:hsl(230, 8%, 24%)\"><span class=\"token plain\">dotnet </span><span class=\"token function\" style=\"color:hsl(221, 87%, 60%)\">add</span><span class=\"token plain\"> **/Ports.Out.csproj reference **/Domain.Core.csproj</span><br></span></code></pre><div class=\"buttonGroup_6DOT\"><button type=\"button\" aria-label=\"Sao chép mã nguồn\" title=\"Sao chép\" class=\"clean-btn\"><span class=\"copyButtonIcons_FhaS\" aria-hidden=\"true\"><svg viewBox=\"0 0 24 24\" class=\"copyButtonIcon_phi_\"><path fill=\"currentColor\" d=\"M19,21H8V7H19M19,5H8A2,2 0 0,0 6,7V21A2,2 0 0,0 8,23H19A2,2 0 0,0 21,21V7A2,2 0 0,0 19,5M16,1H4A2,2 0 0,0 2,3V17H4V3H16V1Z\"></path></svg><svg viewBox=\"0 0 24 24\" class=\"copyButtonSuccessIcon_FfTR\"><path fill=\"currentColor\" d=\"M21,7L9,19L3.5,13.5L4.91,12.09L9,16.17L19.59,5.59L21,7Z\"></path></svg></span></button></div></div></div>\n<blockquote>\n<p>Có một lưu ý nhỏ khi bạn đặt thư mục với tên <code>Out</code> (viết hoa chữ đầu) như ví dụ trên: nó khiến git nhầm lẫn tên với thư mục <code>out</code>. Khi bạn ignore thư mục <code>out</code> (đây là nơi chứa những file được runtime c# tạo ra trên máy bạn, và bạn nên ignore nó), git có thể lẫn với thư mục <code>Out</code> (viết hoa chữ đầu) dẫn đến code không được commit. Khắc phục như sau: bật case-sensitive trong config của git với lệnh sau <code>git config core.ignorecase false</code> và định nghĩa thư mục cần ignore với case sentitive. Ví dụ: <code>**/[o]ut</code> sẽ ignore tất cả các thư mục <code>out</code> (viết thường) và giữ lại <code>Out</code>.</p>\n</blockquote>\n<p>Kinh nghiệm của tôi cho thấy rằng nếu sử dụng DDD nói chung và kiến trúc P&amp;A nói riêng, ta nên có thêm một thành phần nữa để kiểm soát tất cả các thành phần còn lại. Đây sẽ là project chứa file main (hoặc <code>Project.cs</code> trong ngữ cảnh của .NET) - điểm khởi tạo của ứng dụng và là nơi chứa config cho toàn bộ dự án.</p>\n<p>Tôi sẽ gọi project đó là <code>Src</code>, bạn tham khảo lệnh dưới để tạo <em>Src</em>:</p>\n<div class=\"language-bash codeBlockContainer_APcc theme-code-block\" style=\"--prism-background-color:hsl(230, 1%, 98%);--prism-color:hsl(230, 8%, 24%)\"><div class=\"codeBlockContent_m3Ux\"><pre tabindex=\"0\" class=\"prism-code language-bash codeBlock_qGQc thin-scrollbar\" style=\"background-color:hsl(230, 1%, 98%);color:hsl(230, 8%, 24%)\"><code class=\"codeBlockLines_p187\"><span class=\"token-line\" style=\"color:hsl(230, 8%, 24%)\"><span class=\"token plain\">dotnet new web </span><span class=\"token parameter variable\" style=\"color:hsl(221, 87%, 60%)\">-n</span><span class=\"token plain\"> Src</span><br></span><span class=\"token-line\" style=\"color:hsl(230, 8%, 24%)\"><span class=\"token plain\">dotnet sln </span><span class=\"token function\" style=\"color:hsl(221, 87%, 60%)\">add</span><span class=\"token plain\"> Src/Src.csproj</span><br></span><span class=\"token-line\" style=\"color:hsl(230, 8%, 24%)\"><span class=\"token plain\">dotnet </span><span class=\"token function\" style=\"color:hsl(221, 87%, 60%)\">add</span><span class=\"token plain\"> Src/Src.csproj reference **/*.csproj</span><br></span><span class=\"token-line\" style=\"color:hsl(230, 8%, 24%)\"><span class=\"token plain\">dotnet remove Src/Src.csproj reference Src/Src.csproj </span><span class=\"token comment\" style=\"color:hsl(230, 4%, 64%)\"># prevent circle loop</span><br></span></code></pre><div class=\"buttonGroup_6DOT\"><button type=\"button\" aria-label=\"Sao chép mã nguồn\" title=\"Sao chép\" class=\"clean-btn\"><span class=\"copyButtonIcons_FhaS\" aria-hidden=\"true\"><svg viewBox=\"0 0 24 24\" class=\"copyButtonIcon_phi_\"><path fill=\"currentColor\" d=\"M19,21H8V7H19M19,5H8A2,2 0 0,0 6,7V21A2,2 0 0,0 8,23H19A2,2 0 0,0 21,21V7A2,2 0 0,0 19,5M16,1H4A2,2 0 0,0 2,3V17H4V3H16V1Z\"></path></svg><svg viewBox=\"0 0 24 24\" class=\"copyButtonSuccessIcon_FfTR\"><path fill=\"currentColor\" d=\"M21,7L9,19L3.5,13.5L4.91,12.09L9,16.17L19.59,5.59L21,7Z\"></path></svg></span></button></div></div></div>\n<p>Sau cùng ta sẽ có cây phụ thuộc như hình dưới:</p>\n<p><img decoding=\"async\" loading=\"lazy\" alt=\"dev-tree\" src=\"https://daoanhthanh.vn/assets/images/image5-3b468c5e814b7ab3f112c2a7155a9c79.png\" width=\"1963\" height=\"1860\" class=\"img_IV9f\"></p>\n<h3 class=\"anchor anchorTargetHideOnScrollNavbar_Omnb\" id=\"cách-các-thành-phần-liên-kết-với-nhau\">Cách các thành phần liên kết với nhau<a href=\"https://daoanhthanh.vn/blog/ports-and-adapters-architecture-in-net-core#c%C3%A1ch-c%C3%A1c-th%C3%A0nh-ph%E1%BA%A7n-li%C3%AAn-k%E1%BA%BFt-v%E1%BB%9Bi-nhau\" class=\"hash-link\" aria-label=\"Chuyển đến tiêu đề\" title=\"Chuyển đến tiêu đề\" translate=\"no\">​</a></h3>\n<p>Để đơn giản, tôi sẽ demo một logic mà hệ thống nào cũng cần có: <strong>Đăng nhập</strong>. Cách triển khai chi tiết xin hãy xem repo đặt ở cuối bài.</p>\n<p>Luồng đi sẽ là:</p>\n<div class=\"language-bash codeBlockContainer_APcc theme-code-block\" style=\"--prism-background-color:hsl(230, 1%, 98%);--prism-color:hsl(230, 8%, 24%)\"><div class=\"codeBlockContent_m3Ux\"><pre tabindex=\"0\" class=\"prism-code language-bash codeBlock_qGQc thin-scrollbar\" style=\"background-color:hsl(230, 1%, 98%);color:hsl(230, 8%, 24%)\"><code class=\"codeBlockLines_p187\"><span class=\"token-line\" style=\"color:hsl(230, 8%, 24%)\"><span class=\"token plain\">Người dùng </span><span class=\"token operator\" style=\"color:hsl(221, 87%, 60%)\">=</span><span class=\"token operator\" style=\"color:hsl(221, 87%, 60%)\">&gt;</span><span class=\"token plain\"> Gửi request </span><span class=\"token operator\" style=\"color:hsl(221, 87%, 60%)\">=</span><span class=\"token operator\" style=\"color:hsl(221, 87%, 60%)\">&gt;</span><span class=\"token plain\"> Controler </span><span class=\"token punctuation\" style=\"color:hsl(119, 34%, 47%)\">(</span><span class=\"token plain\">Adapter</span><span class=\"token punctuation\" style=\"color:hsl(119, 34%, 47%)\">)</span><span class=\"token plain\"> </span><span class=\"token operator\" style=\"color:hsl(221, 87%, 60%)\">=</span><span class=\"token operator\" style=\"color:hsl(221, 87%, 60%)\">&gt;</span><span class=\"token plain\"> Port In </span><span class=\"token operator\" style=\"color:hsl(221, 87%, 60%)\">=</span><span class=\"token operator\" style=\"color:hsl(221, 87%, 60%)\">&gt;</span><span class=\"token plain\"> Domain Service </span><span class=\"token operator\" style=\"color:hsl(221, 87%, 60%)\">=</span><span class=\"token operator\" style=\"color:hsl(221, 87%, 60%)\">&gt;</span><span class=\"token plain\"> Port Out </span><span class=\"token operator\" style=\"color:hsl(221, 87%, 60%)\">=</span><span class=\"token operator\" style=\"color:hsl(221, 87%, 60%)\">&gt;</span><span class=\"token plain\"> Database </span><span class=\"token punctuation\" style=\"color:hsl(119, 34%, 47%)\">(</span><span class=\"token plain\">Adapter</span><span class=\"token punctuation\" style=\"color:hsl(119, 34%, 47%)\">)</span><br></span></code></pre><div class=\"buttonGroup_6DOT\"><button type=\"button\" aria-label=\"Sao chép mã nguồn\" title=\"Sao chép\" class=\"clean-btn\"><span class=\"copyButtonIcons_FhaS\" aria-hidden=\"true\"><svg viewBox=\"0 0 24 24\" class=\"copyButtonIcon_phi_\"><path fill=\"currentColor\" d=\"M19,21H8V7H19M19,5H8A2,2 0 0,0 6,7V21A2,2 0 0,0 8,23H19A2,2 0 0,0 21,21V7A2,2 0 0,0 19,5M16,1H4A2,2 0 0,0 2,3V17H4V3H16V1Z\"></path></svg><svg viewBox=\"0 0 24 24\" class=\"copyButtonSuccessIcon_FfTR\"><path fill=\"currentColor\" d=\"M21,7L9,19L3.5,13.5L4.91,12.09L9,16.17L19.59,5.59L21,7Z\"></path></svg></span></button></div></div></div>\n<p>Đầu tiên cần có controller:</p>\n<div class=\"language-typescript codeBlockContainer_APcc theme-code-block\" style=\"--prism-background-color:hsl(230, 1%, 98%);--prism-color:hsl(230, 8%, 24%)\"><div class=\"codeBlockContent_m3Ux\"><pre tabindex=\"0\" class=\"prism-code language-typescript codeBlock_qGQc thin-scrollbar\" style=\"background-color:hsl(230, 1%, 98%);color:hsl(230, 8%, 24%)\"><code class=\"codeBlockLines_p187\"><span class=\"token-line\" style=\"color:hsl(230, 8%, 24%)\"><span class=\"token plain\">using Ports</span><span class=\"token punctuation\" style=\"color:hsl(119, 34%, 47%)\">.</span><span class=\"token plain\">In</span><span class=\"token punctuation\" style=\"color:hsl(119, 34%, 47%)\">;</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:hsl(230, 8%, 24%)\"><span class=\"token plain\"></span><span class=\"token keyword\" style=\"color:hsl(301, 63%, 40%)\">namespace</span><span class=\"token plain\"> Adapter</span><span class=\"token punctuation\" style=\"color:hsl(119, 34%, 47%)\">.</span><span class=\"token plain\">RestfulAPI</span><span class=\"token punctuation\" style=\"color:hsl(119, 34%, 47%)\">;</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:hsl(230, 8%, 24%)\"><span class=\"token plain\" style=\"display:inline-block\"></span><br></span><span class=\"token-line\" style=\"color:hsl(230, 8%, 24%)\"><span class=\"token plain\"></span><span class=\"token punctuation\" style=\"color:hsl(119, 34%, 47%)\">[</span><span class=\"token function\" style=\"color:hsl(221, 87%, 60%)\">Route</span><span class=\"token punctuation\" style=\"color:hsl(119, 34%, 47%)\">(</span><span class=\"token string\" style=\"color:hsl(119, 34%, 47%)\">\"api/v{version:apiVersion}\"</span><span class=\"token punctuation\" style=\"color:hsl(119, 34%, 47%)\">)</span><span class=\"token punctuation\" style=\"color:hsl(119, 34%, 47%)\">]</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:hsl(230, 8%, 24%)\"><span class=\"token plain\"></span><span class=\"token keyword\" style=\"color:hsl(301, 63%, 40%)\">public</span><span class=\"token plain\"> </span><span class=\"token keyword\" style=\"color:hsl(301, 63%, 40%)\">class</span><span class=\"token plain\"> </span><span class=\"token class-name\" style=\"color:hsl(35, 99%, 36%)\">LoginController</span><span class=\"token punctuation\" style=\"color:hsl(119, 34%, 47%)\">(</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:hsl(230, 8%, 24%)\"><span class=\"token plain\">    IAuthenticator authenticator</span><span class=\"token punctuation\" style=\"color:hsl(119, 34%, 47%)\">,</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:hsl(230, 8%, 24%)\"><span class=\"token plain\"></span><span class=\"token punctuation\" style=\"color:hsl(119, 34%, 47%)\">)</span><span class=\"token plain\"> </span><span class=\"token operator\" style=\"color:hsl(221, 87%, 60%)\">:</span><span class=\"token plain\"> RestfulControllerBase</span><br></span><span class=\"token-line\" style=\"color:hsl(230, 8%, 24%)\"><span class=\"token plain\"></span><span class=\"token punctuation\" style=\"color:hsl(119, 34%, 47%)\">{</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:hsl(230, 8%, 24%)\"><span class=\"token plain\">    </span><span class=\"token punctuation\" style=\"color:hsl(119, 34%, 47%)\">[</span><span class=\"token function\" style=\"color:hsl(221, 87%, 60%)\">HttpPost</span><span class=\"token punctuation\" style=\"color:hsl(119, 34%, 47%)\">(</span><span class=\"token string\" style=\"color:hsl(119, 34%, 47%)\">\"login\"</span><span class=\"token punctuation\" style=\"color:hsl(119, 34%, 47%)\">)</span><span class=\"token punctuation\" style=\"color:hsl(119, 34%, 47%)\">]</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:hsl(230, 8%, 24%)\"><span class=\"token plain\">    </span><span class=\"token keyword\" style=\"color:hsl(301, 63%, 40%)\">public</span><span class=\"token plain\"> Task</span><span class=\"token operator\" style=\"color:hsl(221, 87%, 60%)\">&lt;</span><span class=\"token plain\">IActionResult</span><span class=\"token operator\" style=\"color:hsl(221, 87%, 60%)\">&gt;</span><span class=\"token plain\"> </span><span class=\"token function\" style=\"color:hsl(221, 87%, 60%)\">Login</span><span class=\"token punctuation\" style=\"color:hsl(119, 34%, 47%)\">(</span><span class=\"token punctuation\" style=\"color:hsl(119, 34%, 47%)\">[</span><span class=\"token plain\">FromBody</span><span class=\"token punctuation\" style=\"color:hsl(119, 34%, 47%)\">]</span><span class=\"token plain\"> LoginForm form</span><span class=\"token punctuation\" style=\"color:hsl(119, 34%, 47%)\">)</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:hsl(230, 8%, 24%)\"><span class=\"token plain\">    </span><span class=\"token punctuation\" style=\"color:hsl(119, 34%, 47%)\">{</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:hsl(230, 8%, 24%)\"><span class=\"token plain\">        </span><span class=\"token comment\" style=\"color:hsl(230, 4%, 64%)\">// Handle login request</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:hsl(230, 8%, 24%)\"><span class=\"token plain\">    </span><span class=\"token punctuation\" style=\"color:hsl(119, 34%, 47%)\">}</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:hsl(230, 8%, 24%)\"><span class=\"token plain\"></span><span class=\"token punctuation\" style=\"color:hsl(119, 34%, 47%)\">}</span><br></span></code></pre><div class=\"buttonGroup_6DOT\"><button type=\"button\" aria-label=\"Sao chép mã nguồn\" title=\"Sao chép\" class=\"clean-btn\"><span class=\"copyButtonIcons_FhaS\" aria-hidden=\"true\"><svg viewBox=\"0 0 24 24\" class=\"copyButtonIcon_phi_\"><path fill=\"currentColor\" d=\"M19,21H8V7H19M19,5H8A2,2 0 0,0 6,7V21A2,2 0 0,0 8,23H19A2,2 0 0,0 21,21V7A2,2 0 0,0 19,5M16,1H4A2,2 0 0,0 2,3V17H4V3H16V1Z\"></path></svg><svg viewBox=\"0 0 24 24\" class=\"copyButtonSuccessIcon_FfTR\"><path fill=\"currentColor\" d=\"M21,7L9,19L3.5,13.5L4.91,12.09L9,16.17L19.59,5.59L21,7Z\"></path></svg></span></button></div></div></div>\n<p>Kế tiếp cần tạo 1 <em>driving port</em> tương ứng cho controller này. Theo quy ước đặt tên của .Net, ta sẽ gọi đó là <code>IAuthenticator</code>:</p>\n<div class=\"language-typescript codeBlockContainer_APcc theme-code-block\" style=\"--prism-background-color:hsl(230, 1%, 98%);--prism-color:hsl(230, 8%, 24%)\"><div class=\"codeBlockContent_m3Ux\"><pre tabindex=\"0\" class=\"prism-code language-typescript codeBlock_qGQc thin-scrollbar\" style=\"background-color:hsl(230, 1%, 98%);color:hsl(230, 8%, 24%)\"><code class=\"codeBlockLines_p187\"><span class=\"token-line\" style=\"color:hsl(230, 8%, 24%)\"><span class=\"token keyword\" style=\"color:hsl(301, 63%, 40%)\">namespace</span><span class=\"token plain\"> Ports</span><span class=\"token punctuation\" style=\"color:hsl(119, 34%, 47%)\">.</span><span class=\"token plain\">In</span><span class=\"token punctuation\" style=\"color:hsl(119, 34%, 47%)\">;</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:hsl(230, 8%, 24%)\"><span class=\"token plain\" style=\"display:inline-block\"></span><br></span><span class=\"token-line\" style=\"color:hsl(230, 8%, 24%)\"><span class=\"token plain\"></span><span class=\"token keyword\" style=\"color:hsl(301, 63%, 40%)\">public</span><span class=\"token plain\"> </span><span class=\"token keyword\" style=\"color:hsl(301, 63%, 40%)\">interface</span><span class=\"token plain\"> </span><span class=\"token class-name\" style=\"color:hsl(35, 99%, 36%)\">IAuthenticator</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:hsl(230, 8%, 24%)\"><span class=\"token plain\"></span><span class=\"token punctuation\" style=\"color:hsl(119, 34%, 47%)\">{</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:hsl(230, 8%, 24%)\"><span class=\"token plain\">    </span><span class=\"token keyword\" style=\"color:hsl(301, 63%, 40%)\">public</span><span class=\"token plain\"> Task</span><span class=\"token operator\" style=\"color:hsl(221, 87%, 60%)\">&lt;</span><span class=\"token plain\">SessionUser</span><span class=\"token operator\" style=\"color:hsl(221, 87%, 60%)\">&gt;</span><span class=\"token plain\"> </span><span class=\"token function\" style=\"color:hsl(221, 87%, 60%)\">Login</span><span class=\"token punctuation\" style=\"color:hsl(119, 34%, 47%)\">(</span><span class=\"token builtin\" style=\"color:hsl(119, 34%, 47%)\">string</span><span class=\"token plain\"> phoneOrEmail</span><span class=\"token punctuation\" style=\"color:hsl(119, 34%, 47%)\">,</span><span class=\"token plain\"> </span><span class=\"token builtin\" style=\"color:hsl(119, 34%, 47%)\">string</span><span class=\"token plain\"> password</span><span class=\"token punctuation\" style=\"color:hsl(119, 34%, 47%)\">)</span><span class=\"token punctuation\" style=\"color:hsl(119, 34%, 47%)\">;</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:hsl(230, 8%, 24%)\"><span class=\"token plain\"></span><span class=\"token punctuation\" style=\"color:hsl(119, 34%, 47%)\">}</span><br></span></code></pre><div class=\"buttonGroup_6DOT\"><button type=\"button\" aria-label=\"Sao chép mã nguồn\" title=\"Sao chép\" class=\"clean-btn\"><span class=\"copyButtonIcons_FhaS\" aria-hidden=\"true\"><svg viewBox=\"0 0 24 24\" class=\"copyButtonIcon_phi_\"><path fill=\"currentColor\" d=\"M19,21H8V7H19M19,5H8A2,2 0 0,0 6,7V21A2,2 0 0,0 8,23H19A2,2 0 0,0 21,21V7A2,2 0 0,0 19,5M16,1H4A2,2 0 0,0 2,3V17H4V3H16V1Z\"></path></svg><svg viewBox=\"0 0 24 24\" class=\"copyButtonSuccessIcon_FfTR\"><path fill=\"currentColor\" d=\"M21,7L9,19L3.5,13.5L4.91,12.09L9,16.17L19.59,5.59L21,7Z\"></path></svg></span></button></div></div></div>\n<p>Ta cần service cho interface đó, tôi tạm gọi <code>Authenticator</code>. Class này sẽ nằm trong <code>Domain.Services</code>:</p>\n<div class=\"language-typescript codeBlockContainer_APcc theme-code-block\" style=\"--prism-background-color:hsl(230, 1%, 98%);--prism-color:hsl(230, 8%, 24%)\"><div class=\"codeBlockContent_m3Ux\"><pre tabindex=\"0\" class=\"prism-code language-typescript codeBlock_qGQc thin-scrollbar\" style=\"background-color:hsl(230, 1%, 98%);color:hsl(230, 8%, 24%)\"><code class=\"codeBlockLines_p187\"><span class=\"token-line\" style=\"color:hsl(230, 8%, 24%)\"><span class=\"token plain\">using Ports</span><span class=\"token punctuation\" style=\"color:hsl(119, 34%, 47%)\">.</span><span class=\"token plain\">In</span><span class=\"token punctuation\" style=\"color:hsl(119, 34%, 47%)\">;</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:hsl(230, 8%, 24%)\"><span class=\"token plain\"></span><span class=\"token keyword\" style=\"color:hsl(301, 63%, 40%)\">namespace</span><span class=\"token plain\"> Domain</span><span class=\"token punctuation\" style=\"color:hsl(119, 34%, 47%)\">.</span><span class=\"token plain\">Services</span><span class=\"token punctuation\" style=\"color:hsl(119, 34%, 47%)\">;</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:hsl(230, 8%, 24%)\"><span class=\"token plain\" style=\"display:inline-block\"></span><br></span><span class=\"token-line\" style=\"color:hsl(230, 8%, 24%)\"><span class=\"token plain\"></span><span class=\"token keyword\" style=\"color:hsl(301, 63%, 40%)\">public</span><span class=\"token plain\"> </span><span class=\"token keyword\" style=\"color:hsl(301, 63%, 40%)\">class</span><span class=\"token plain\"> </span><span class=\"token class-name\" style=\"color:hsl(35, 99%, 36%)\">Authenticator</span><span class=\"token punctuation\" style=\"color:hsl(119, 34%, 47%)\">(</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:hsl(230, 8%, 24%)\"><span class=\"token plain\">    IUserRepository repo</span><span class=\"token punctuation\" style=\"color:hsl(119, 34%, 47%)\">,</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:hsl(230, 8%, 24%)\"><span class=\"token plain\">    IPassword pwdService</span><span class=\"token punctuation\" style=\"color:hsl(119, 34%, 47%)\">,</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:hsl(230, 8%, 24%)\"><span class=\"token plain\">    ICache cache</span><br></span><span class=\"token-line\" style=\"color:hsl(230, 8%, 24%)\"><span class=\"token plain\">    </span><span class=\"token punctuation\" style=\"color:hsl(119, 34%, 47%)\">)</span><span class=\"token plain\"> </span><span class=\"token operator\" style=\"color:hsl(221, 87%, 60%)\">:</span><span class=\"token plain\"> IAuthenticator</span><br></span><span class=\"token-line\" style=\"color:hsl(230, 8%, 24%)\"><span class=\"token plain\"></span><span class=\"token punctuation\" style=\"color:hsl(119, 34%, 47%)\">{</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:hsl(230, 8%, 24%)\"><span class=\"token plain\">    </span><span class=\"token keyword\" style=\"color:hsl(301, 63%, 40%)\">public</span><span class=\"token plain\"> </span><span class=\"token keyword\" style=\"color:hsl(301, 63%, 40%)\">async</span><span class=\"token plain\"> Task</span><span class=\"token operator\" style=\"color:hsl(221, 87%, 60%)\">&lt;</span><span class=\"token plain\">SessionUser</span><span class=\"token operator\" style=\"color:hsl(221, 87%, 60%)\">&gt;</span><span class=\"token plain\"> </span><span class=\"token function\" style=\"color:hsl(221, 87%, 60%)\">Login</span><span class=\"token punctuation\" style=\"color:hsl(119, 34%, 47%)\">(</span><span class=\"token builtin\" style=\"color:hsl(119, 34%, 47%)\">string</span><span class=\"token plain\"> phoneOrEmail</span><span class=\"token punctuation\" style=\"color:hsl(119, 34%, 47%)\">,</span><span class=\"token plain\"> </span><span class=\"token builtin\" style=\"color:hsl(119, 34%, 47%)\">string</span><span class=\"token plain\"> password</span><span class=\"token punctuation\" style=\"color:hsl(119, 34%, 47%)\">)</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:hsl(230, 8%, 24%)\"><span class=\"token plain\">    </span><span class=\"token punctuation\" style=\"color:hsl(119, 34%, 47%)\">{</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:hsl(230, 8%, 24%)\"><span class=\"token plain\">        </span><span class=\"token comment\" style=\"color:hsl(230, 4%, 64%)\">// Thực hiện logic login</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:hsl(230, 8%, 24%)\"><span class=\"token plain\">    </span><span class=\"token punctuation\" style=\"color:hsl(119, 34%, 47%)\">}</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:hsl(230, 8%, 24%)\"><span class=\"token plain\"></span><span class=\"token punctuation\" style=\"color:hsl(119, 34%, 47%)\">}</span><br></span></code></pre><div class=\"buttonGroup_6DOT\"><button type=\"button\" aria-label=\"Sao chép mã nguồn\" title=\"Sao chép\" class=\"clean-btn\"><span class=\"copyButtonIcons_FhaS\" aria-hidden=\"true\"><svg viewBox=\"0 0 24 24\" class=\"copyButtonIcon_phi_\"><path fill=\"currentColor\" d=\"M19,21H8V7H19M19,5H8A2,2 0 0,0 6,7V21A2,2 0 0,0 8,23H19A2,2 0 0,0 21,21V7A2,2 0 0,0 19,5M16,1H4A2,2 0 0,0 2,3V17H4V3H16V1Z\"></path></svg><svg viewBox=\"0 0 24 24\" class=\"copyButtonSuccessIcon_FfTR\"><path fill=\"currentColor\" d=\"M21,7L9,19L3.5,13.5L4.91,12.09L9,16.17L19.59,5.59L21,7Z\"></path></svg></span></button></div></div></div>\n<p>Ta đang thiếu các domain class đại diện cho đối tượng tham gia quá trình login. Hãy thêm nó trong <code>Domain.Core</code></p>\n<div class=\"language-typescript codeBlockContainer_APcc theme-code-block\" style=\"--prism-background-color:hsl(230, 1%, 98%);--prism-color:hsl(230, 8%, 24%)\"><div class=\"codeBlockContent_m3Ux\"><pre tabindex=\"0\" class=\"prism-code language-typescript codeBlock_qGQc thin-scrollbar\" style=\"background-color:hsl(230, 1%, 98%);color:hsl(230, 8%, 24%)\"><code class=\"codeBlockLines_p187\"><span class=\"token-line\" style=\"color:hsl(230, 8%, 24%)\"><span class=\"token keyword\" style=\"color:hsl(301, 63%, 40%)\">namespace</span><span class=\"token plain\"> Domain</span><span class=\"token punctuation\" style=\"color:hsl(119, 34%, 47%)\">.</span><span class=\"token plain\">Core</span><span class=\"token punctuation\" style=\"color:hsl(119, 34%, 47%)\">;</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:hsl(230, 8%, 24%)\"><span class=\"token plain\" style=\"display:inline-block\"></span><br></span><span class=\"token-line\" style=\"color:hsl(230, 8%, 24%)\"><span class=\"token plain\"></span><span class=\"token comment\" style=\"color:hsl(230, 4%, 64%)\">// SessionUser.cs</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:hsl(230, 8%, 24%)\"><span class=\"token plain\"></span><span class=\"token keyword\" style=\"color:hsl(301, 63%, 40%)\">public</span><span class=\"token plain\"> </span><span class=\"token keyword\" style=\"color:hsl(301, 63%, 40%)\">class</span><span class=\"token plain\"> </span><span class=\"token class-name\" style=\"color:hsl(35, 99%, 36%)\">SessionUser</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:hsl(230, 8%, 24%)\"><span class=\"token plain\"></span><span class=\"token punctuation\" style=\"color:hsl(119, 34%, 47%)\">{</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:hsl(230, 8%, 24%)\"><span class=\"token plain\">    </span><span class=\"token comment\" style=\"color:hsl(230, 4%, 64%)\">// Định nghĩa Entity cần trả cho client khi đăng nhập thành công</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:hsl(230, 8%, 24%)\"><span class=\"token plain\"></span><span class=\"token punctuation\" style=\"color:hsl(119, 34%, 47%)\">}</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:hsl(230, 8%, 24%)\"><span class=\"token plain\" style=\"display:inline-block\"></span><br></span><span class=\"token-line\" style=\"color:hsl(230, 8%, 24%)\"><span class=\"token plain\"></span><span class=\"token comment\" style=\"color:hsl(230, 4%, 64%)\">// User.cs</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:hsl(230, 8%, 24%)\"><span class=\"token plain\"></span><span class=\"token punctuation\" style=\"color:hsl(119, 34%, 47%)\">[</span><span class=\"token function\" style=\"color:hsl(221, 87%, 60%)\">Table</span><span class=\"token punctuation\" style=\"color:hsl(119, 34%, 47%)\">(</span><span class=\"token string\" style=\"color:hsl(119, 34%, 47%)\">\"Users\"</span><span class=\"token punctuation\" style=\"color:hsl(119, 34%, 47%)\">)</span><span class=\"token punctuation\" style=\"color:hsl(119, 34%, 47%)\">]</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:hsl(230, 8%, 24%)\"><span class=\"token plain\"></span><span class=\"token keyword\" style=\"color:hsl(301, 63%, 40%)\">public</span><span class=\"token plain\"> </span><span class=\"token keyword\" style=\"color:hsl(301, 63%, 40%)\">class</span><span class=\"token plain\"> </span><span class=\"token class-name\" style=\"color:hsl(35, 99%, 36%)\">User</span><span class=\"token plain\"> </span><span class=\"token operator\" style=\"color:hsl(221, 87%, 60%)\">:</span><span class=\"token plain\"> EntityAudit</span><br></span><span class=\"token-line\" style=\"color:hsl(230, 8%, 24%)\"><span class=\"token plain\"></span><span class=\"token punctuation\" style=\"color:hsl(119, 34%, 47%)\">{</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:hsl(230, 8%, 24%)\"><span class=\"token plain\">    </span><span class=\"token comment\" style=\"color:hsl(230, 4%, 64%)\">// Định nghĩa entity user - thứ sẽ được lưu trong Db</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:hsl(230, 8%, 24%)\"><span class=\"token plain\"></span><span class=\"token punctuation\" style=\"color:hsl(119, 34%, 47%)\">}</span><br></span></code></pre><div class=\"buttonGroup_6DOT\"><button type=\"button\" aria-label=\"Sao chép mã nguồn\" title=\"Sao chép\" class=\"clean-btn\"><span class=\"copyButtonIcons_FhaS\" aria-hidden=\"true\"><svg viewBox=\"0 0 24 24\" class=\"copyButtonIcon_phi_\"><path fill=\"currentColor\" d=\"M19,21H8V7H19M19,5H8A2,2 0 0,0 6,7V21A2,2 0 0,0 8,23H19A2,2 0 0,0 21,21V7A2,2 0 0,0 19,5M16,1H4A2,2 0 0,0 2,3V17H4V3H16V1Z\"></path></svg><svg viewBox=\"0 0 24 24\" class=\"copyButtonSuccessIcon_FfTR\"><path fill=\"currentColor\" d=\"M21,7L9,19L3.5,13.5L4.91,12.09L9,16.17L19.59,5.59L21,7Z\"></path></svg></span></button></div></div></div>\n<p>Ta cần một port out tham gia vào quá trình này. Service sẽ cần một bên chịu trách nhiệm lấy thông tin từ DB. Hãy tạo nó trong project <code>Ports.Out</code>:</p>\n<div class=\"language-typescript codeBlockContainer_APcc theme-code-block\" style=\"--prism-background-color:hsl(230, 1%, 98%);--prism-color:hsl(230, 8%, 24%)\"><div class=\"codeBlockContent_m3Ux\"><pre tabindex=\"0\" class=\"prism-code language-typescript codeBlock_qGQc thin-scrollbar\" style=\"background-color:hsl(230, 1%, 98%);color:hsl(230, 8%, 24%)\"><code class=\"codeBlockLines_p187\"><span class=\"token-line\" style=\"color:hsl(230, 8%, 24%)\"><span class=\"token keyword\" style=\"color:hsl(301, 63%, 40%)\">namespace</span><span class=\"token plain\"> Ports</span><span class=\"token punctuation\" style=\"color:hsl(119, 34%, 47%)\">.</span><span class=\"token plain\">Out</span><span class=\"token punctuation\" style=\"color:hsl(119, 34%, 47%)\">;</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:hsl(230, 8%, 24%)\"><span class=\"token plain\" style=\"display:inline-block\"></span><br></span><span class=\"token-line\" style=\"color:hsl(230, 8%, 24%)\"><span class=\"token plain\"></span><span class=\"token keyword\" style=\"color:hsl(301, 63%, 40%)\">public</span><span class=\"token plain\"> </span><span class=\"token keyword\" style=\"color:hsl(301, 63%, 40%)\">interface</span><span class=\"token plain\"> </span><span class=\"token class-name\" style=\"color:hsl(35, 99%, 36%)\">IUserRepository</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:hsl(230, 8%, 24%)\"><span class=\"token plain\"></span><span class=\"token punctuation\" style=\"color:hsl(119, 34%, 47%)\">{</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:hsl(230, 8%, 24%)\"><span class=\"token plain\">    Task</span><span class=\"token operator\" style=\"color:hsl(221, 87%, 60%)\">&lt;</span><span class=\"token plain\">User</span><span class=\"token operator\" style=\"color:hsl(221, 87%, 60%)\">?</span><span class=\"token operator\" style=\"color:hsl(221, 87%, 60%)\">&gt;</span><span class=\"token plain\"> </span><span class=\"token function\" style=\"color:hsl(221, 87%, 60%)\">FindByPhoneOrEmail</span><span class=\"token punctuation\" style=\"color:hsl(119, 34%, 47%)\">(</span><span class=\"token builtin\" style=\"color:hsl(119, 34%, 47%)\">string</span><span class=\"token plain\"> phoneOrEmail</span><span class=\"token punctuation\" style=\"color:hsl(119, 34%, 47%)\">,</span><span class=\"token plain\"> bool includePassword </span><span class=\"token operator\" style=\"color:hsl(221, 87%, 60%)\">=</span><span class=\"token plain\"> </span><span class=\"token boolean\" style=\"color:hsl(35, 99%, 36%)\">false</span><span class=\"token punctuation\" style=\"color:hsl(119, 34%, 47%)\">)</span><span class=\"token punctuation\" style=\"color:hsl(119, 34%, 47%)\">;</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:hsl(230, 8%, 24%)\"><span class=\"token plain\"></span><span class=\"token punctuation\" style=\"color:hsl(119, 34%, 47%)\">}</span><br></span></code></pre><div class=\"buttonGroup_6DOT\"><button type=\"button\" aria-label=\"Sao chép mã nguồn\" title=\"Sao chép\" class=\"clean-btn\"><span class=\"copyButtonIcons_FhaS\" aria-hidden=\"true\"><svg viewBox=\"0 0 24 24\" class=\"copyButtonIcon_phi_\"><path fill=\"currentColor\" d=\"M19,21H8V7H19M19,5H8A2,2 0 0,0 6,7V21A2,2 0 0,0 8,23H19A2,2 0 0,0 21,21V7A2,2 0 0,0 19,5M16,1H4A2,2 0 0,0 2,3V17H4V3H16V1Z\"></path></svg><svg viewBox=\"0 0 24 24\" class=\"copyButtonSuccessIcon_FfTR\"><path fill=\"currentColor\" d=\"M21,7L9,19L3.5,13.5L4.91,12.09L9,16.17L19.59,5.59L21,7Z\"></path></svg></span></button></div></div></div>\n<p>Component cuối cùng tham gia quá trình này là <em>driven adapter</em>. Trong luồng đi của dữ liệu, đây sẽ thành phần chịu trách nhiệm gửi yêu cầu ra hệ thống bên ngoài, trong trường hợp này là triển khai giao tiếp với DB.</p>\n<div class=\"language-typescript codeBlockContainer_APcc theme-code-block\" style=\"--prism-background-color:hsl(230, 1%, 98%);--prism-color:hsl(230, 8%, 24%)\"><div class=\"codeBlockContent_m3Ux\"><pre tabindex=\"0\" class=\"prism-code language-typescript codeBlock_qGQc thin-scrollbar\" style=\"background-color:hsl(230, 1%, 98%);color:hsl(230, 8%, 24%)\"><code class=\"codeBlockLines_p187\"><span class=\"token-line\" style=\"color:hsl(230, 8%, 24%)\"><span class=\"token plain\">using Ports</span><span class=\"token punctuation\" style=\"color:hsl(119, 34%, 47%)\">.</span><span class=\"token plain\">Out</span><span class=\"token punctuation\" style=\"color:hsl(119, 34%, 47%)\">;</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:hsl(230, 8%, 24%)\"><span class=\"token plain\"></span><span class=\"token keyword\" style=\"color:hsl(301, 63%, 40%)\">namespace</span><span class=\"token plain\"> Adapter</span><span class=\"token punctuation\" style=\"color:hsl(119, 34%, 47%)\">.</span><span class=\"token plain\">Database</span><span class=\"token punctuation\" style=\"color:hsl(119, 34%, 47%)\">.</span><span class=\"token plain\">Postgres</span><span class=\"token punctuation\" style=\"color:hsl(119, 34%, 47%)\">;</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:hsl(230, 8%, 24%)\"><span class=\"token plain\" style=\"display:inline-block\"></span><br></span><span class=\"token-line\" style=\"color:hsl(230, 8%, 24%)\"><span class=\"token plain\"></span><span class=\"token keyword\" style=\"color:hsl(301, 63%, 40%)\">public</span><span class=\"token plain\"> </span><span class=\"token keyword\" style=\"color:hsl(301, 63%, 40%)\">class</span><span class=\"token plain\"> </span><span class=\"token class-name\" style=\"color:hsl(35, 99%, 36%)\">UserRepository</span><span class=\"token punctuation\" style=\"color:hsl(119, 34%, 47%)\">(</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:hsl(230, 8%, 24%)\"><span class=\"token plain\">    PostgresRootDbContext context</span><br></span><span class=\"token-line\" style=\"color:hsl(230, 8%, 24%)\"><span class=\"token plain\"></span><span class=\"token punctuation\" style=\"color:hsl(119, 34%, 47%)\">)</span><span class=\"token plain\"> </span><span class=\"token operator\" style=\"color:hsl(221, 87%, 60%)\">:</span><span class=\"token plain\"> </span><span class=\"token generic-function function\" style=\"color:hsl(221, 87%, 60%)\">Repository</span><span class=\"token generic-function generic class-name operator\" style=\"color:hsl(221, 87%, 60%)\">&lt;</span><span class=\"token generic-function generic class-name\" style=\"color:hsl(35, 99%, 36%)\">DomainModel</span><span class=\"token generic-function generic class-name punctuation\" style=\"color:hsl(119, 34%, 47%)\">.</span><span class=\"token generic-function generic class-name\" style=\"color:hsl(35, 99%, 36%)\">User</span><span class=\"token generic-function generic class-name operator\" style=\"color:hsl(221, 87%, 60%)\">&gt;</span><span class=\"token punctuation\" style=\"color:hsl(119, 34%, 47%)\">(</span><span class=\"token plain\">context</span><span class=\"token punctuation\" style=\"color:hsl(119, 34%, 47%)\">)</span><span class=\"token punctuation\" style=\"color:hsl(119, 34%, 47%)\">,</span><span class=\"token plain\"> IUserRepository</span><br></span><span class=\"token-line\" style=\"color:hsl(230, 8%, 24%)\"><span class=\"token plain\"></span><span class=\"token punctuation\" style=\"color:hsl(119, 34%, 47%)\">{</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:hsl(230, 8%, 24%)\"><span class=\"token plain\">    </span><span class=\"token keyword\" style=\"color:hsl(301, 63%, 40%)\">public</span><span class=\"token plain\"> Task</span><span class=\"token operator\" style=\"color:hsl(221, 87%, 60%)\">&lt;</span><span class=\"token plain\">DomainModel</span><span class=\"token punctuation\" style=\"color:hsl(119, 34%, 47%)\">.</span><span class=\"token plain\">User</span><span class=\"token operator\" style=\"color:hsl(221, 87%, 60%)\">?</span><span class=\"token operator\" style=\"color:hsl(221, 87%, 60%)\">&gt;</span><span class=\"token plain\"> </span><span class=\"token function\" style=\"color:hsl(221, 87%, 60%)\">FindByPhoneOrEmail</span><span class=\"token punctuation\" style=\"color:hsl(119, 34%, 47%)\">(</span><span class=\"token builtin\" style=\"color:hsl(119, 34%, 47%)\">string</span><span class=\"token plain\"> phoneOrEmail</span><span class=\"token punctuation\" style=\"color:hsl(119, 34%, 47%)\">,</span><span class=\"token plain\"> bool includePassword</span><span class=\"token punctuation\" style=\"color:hsl(119, 34%, 47%)\">)</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:hsl(230, 8%, 24%)\"><span class=\"token plain\">    </span><span class=\"token punctuation\" style=\"color:hsl(119, 34%, 47%)\">{</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:hsl(230, 8%, 24%)\"><span class=\"token plain\">        </span><span class=\"token comment\" style=\"color:hsl(230, 4%, 64%)\">// Kết nối với Db để query dữ liệu</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:hsl(230, 8%, 24%)\"><span class=\"token plain\">    </span><span class=\"token punctuation\" style=\"color:hsl(119, 34%, 47%)\">}</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:hsl(230, 8%, 24%)\"><span class=\"token plain\"></span><span class=\"token punctuation\" style=\"color:hsl(119, 34%, 47%)\">}</span><br></span></code></pre><div class=\"buttonGroup_6DOT\"><button type=\"button\" aria-label=\"Sao chép mã nguồn\" title=\"Sao chép\" class=\"clean-btn\"><span class=\"copyButtonIcons_FhaS\" aria-hidden=\"true\"><svg viewBox=\"0 0 24 24\" class=\"copyButtonIcon_phi_\"><path fill=\"currentColor\" d=\"M19,21H8V7H19M19,5H8A2,2 0 0,0 6,7V21A2,2 0 0,0 8,23H19A2,2 0 0,0 21,21V7A2,2 0 0,0 19,5M16,1H4A2,2 0 0,0 2,3V17H4V3H16V1Z\"></path></svg><svg viewBox=\"0 0 24 24\" class=\"copyButtonSuccessIcon_FfTR\"><path fill=\"currentColor\" d=\"M21,7L9,19L3.5,13.5L4.91,12.09L9,16.17L19.59,5.59L21,7Z\"></path></svg></span></button></div></div></div>\n<p>Giản lược các phần không liên quan, đây là cây thư mục sau khi implement:</p>\n<div class=\"language-bash codeBlockContainer_APcc theme-code-block\" style=\"--prism-background-color:hsl(230, 1%, 98%);--prism-color:hsl(230, 8%, 24%)\"><div class=\"codeBlockContent_m3Ux\"><pre tabindex=\"0\" class=\"prism-code language-bash codeBlock_qGQc thin-scrollbar\" style=\"background-color:hsl(230, 1%, 98%);color:hsl(230, 8%, 24%)\"><code class=\"codeBlockLines_p187\"><span class=\"token-line\" style=\"color:hsl(230, 8%, 24%)\"><span class=\"token builtin class-name\" style=\"color:hsl(35, 99%, 36%)\">.</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:hsl(230, 8%, 24%)\"><span class=\"token plain\">├── Adapter</span><br></span><span class=\"token-line\" style=\"color:hsl(230, 8%, 24%)\"><span class=\"token plain\">│&nbsp;&nbsp; ├── In</span><br></span><span class=\"token-line\" style=\"color:hsl(230, 8%, 24%)\"><span class=\"token plain\">│&nbsp;&nbsp; │&nbsp;&nbsp; ├── APIs</span><br></span><span class=\"token-line\" style=\"color:hsl(230, 8%, 24%)\"><span class=\"token plain\">│&nbsp;&nbsp; │&nbsp;&nbsp;  &nbsp;&nbsp; ├── GraphQL</span><br></span><span class=\"token-line\" style=\"color:hsl(230, 8%, 24%)\"><span class=\"token plain\">│&nbsp;&nbsp; │&nbsp;&nbsp;  &nbsp;&nbsp; └── RestfulAPI</span><br></span><span class=\"token-line\" style=\"color:hsl(230, 8%, 24%)\"><span class=\"token plain\">│&nbsp;&nbsp; │&nbsp;&nbsp;  &nbsp;&nbsp;     └── LoginController.cs</span><br></span><span class=\"token-line\" style=\"color:hsl(230, 8%, 24%)\"><span class=\"token plain\">│&nbsp;&nbsp; └── Out</span><br></span><span class=\"token-line\" style=\"color:hsl(230, 8%, 24%)\"><span class=\"token plain\">│&nbsp;&nbsp;     ├── Databases</span><br></span><span class=\"token-line\" style=\"color:hsl(230, 8%, 24%)\"><span class=\"token plain\">│&nbsp;&nbsp;      &nbsp;&nbsp; ├── Postgres</span><br></span><span class=\"token-line\" style=\"color:hsl(230, 8%, 24%)\"><span class=\"token plain\">│&nbsp;&nbsp;      &nbsp;&nbsp;  &nbsp;&nbsp; └── UserRepository.cs</span><br></span><span class=\"token-line\" style=\"color:hsl(230, 8%, 24%)\"><span class=\"token plain\">│</span><br></span><span class=\"token-line\" style=\"color:hsl(230, 8%, 24%)\"><span class=\"token plain\">├── Application</span><br></span><span class=\"token-line\" style=\"color:hsl(230, 8%, 24%)\"><span class=\"token plain\"> &nbsp;&nbsp; ├── Domain</span><br></span><span class=\"token-line\" style=\"color:hsl(230, 8%, 24%)\"><span class=\"token plain\"> &nbsp;&nbsp; │&nbsp;&nbsp; ├── Domain.Core</span><br></span><span class=\"token-line\" style=\"color:hsl(230, 8%, 24%)\"><span class=\"token plain\"> &nbsp;&nbsp; │&nbsp;&nbsp; │&nbsp;&nbsp; ├── SessionUser.cs</span><br></span><span class=\"token-line\" style=\"color:hsl(230, 8%, 24%)\"><span class=\"token plain\"> &nbsp;&nbsp; │&nbsp;&nbsp; │&nbsp;&nbsp; └── User.cs</span><br></span><span class=\"token-line\" style=\"color:hsl(230, 8%, 24%)\"><span class=\"token plain\"> &nbsp;&nbsp; │&nbsp;&nbsp; ├── Domain.Services</span><br></span><span class=\"token-line\" style=\"color:hsl(230, 8%, 24%)\"><span class=\"token plain\"> &nbsp;&nbsp; │&nbsp;&nbsp;  &nbsp;&nbsp; └── Authenticator.cs</span><br></span><span class=\"token-line\" style=\"color:hsl(230, 8%, 24%)\"><span class=\"token plain\"> &nbsp;&nbsp; │&nbsp;&nbsp;</span><br></span><span class=\"token-line\" style=\"color:hsl(230, 8%, 24%)\"><span class=\"token plain\"> &nbsp;&nbsp; └── Ports</span><br></span><span class=\"token-line\" style=\"color:hsl(230, 8%, 24%)\"><span class=\"token plain\"> &nbsp;&nbsp;     ├── In</span><br></span><span class=\"token-line\" style=\"color:hsl(230, 8%, 24%)\"><span class=\"token plain\"> &nbsp;&nbsp;     │&nbsp;&nbsp; └── IAuthenticator.cs</span><br></span><span class=\"token-line\" style=\"color:hsl(230, 8%, 24%)\"><span class=\"token plain\"> &nbsp;&nbsp;     └── Out</span><br></span><span class=\"token-line\" style=\"color:hsl(230, 8%, 24%)\"><span class=\"token plain\"> &nbsp;&nbsp;         └── IUserRepository.cs</span><br></span></code></pre><div class=\"buttonGroup_6DOT\"><button type=\"button\" aria-label=\"Sao chép mã nguồn\" title=\"Sao chép\" class=\"clean-btn\"><span class=\"copyButtonIcons_FhaS\" aria-hidden=\"true\"><svg viewBox=\"0 0 24 24\" class=\"copyButtonIcon_phi_\"><path fill=\"currentColor\" d=\"M19,21H8V7H19M19,5H8A2,2 0 0,0 6,7V21A2,2 0 0,0 8,23H19A2,2 0 0,0 21,21V7A2,2 0 0,0 19,5M16,1H4A2,2 0 0,0 2,3V17H4V3H16V1Z\"></path></svg><svg viewBox=\"0 0 24 24\" class=\"copyButtonSuccessIcon_FfTR\"><path fill=\"currentColor\" d=\"M21,7L9,19L3.5,13.5L4.91,12.09L9,16.17L19.59,5.59L21,7Z\"></path></svg></span></button></div></div></div>\n<p>Bạn đạt được 4 thứ khi áp dụng kiến trúc trên vào dự án:</p>\n<ol>\n<li class=\"\">Dễ đọc, dễ hình dung luồng đi của dữ liệu</li>\n<li class=\"\">Có sự ràng buộc nhất định giữa các thành phần với nhau hình thành một luồng đi thống nhất. Bạn không thể bỏ qua bất kì bước nào để đi tắt và do đó, kiến trúc code sẽ đồng nhất. Lại thêm một lý do nữa cho việc dễ đọc.</li>\n<li class=\"\">Dễ kiểm thử, bảo trì. Các thành phần được chia nhỏ theo chức vụ do vậy hạn chế tối đa side effects. Việc unit test giờ đây rất dễ dàng vì sự <em>liên kết lỏng lẻo</em> (<a href=\"https://www.techtarget.com/searchnetworking/definition/loose-coupling\" target=\"_blank\" rel=\"noopener noreferrer\">Loose coupling</a>) giữa các phần với nhau.</li>\n<li class=\"\">DDD được thiết kế cho việc dễ mở rộng, điều này cũng đúng với kiến trúc Ports &amp; Adapters. Như một lẽ tất nhiên, khi bạn dễ đọc code, dễ bảo trì thì code sẽ rất dễ dàng để mở rộng.</li>\n</ol>\n<blockquote>\n<p><span style=\"display:inline-flex;gap:0.25rem\"><span></span><a href=\"https://github.com/daoanhthanh/dotnet-core-ports-and-adapter\" target=\"_blank\" rel=\"noopener noreferrer\">Đây là dự án mẫu tôi đã tạo ra</a></span> để giúp bạn hiểu hơn về cách implement P&amp;A trong .NET. Chúc bạn code vui 🥰🥰</p>\n</blockquote>",
            "url": "https://daoanhthanh.vn/blog/ports-and-adapters-architecture-in-net-core",
            "title": "Kiến trúc 'Ports and Adapters' trong .NET Core",
            "summary": "Một cách implement DDD trong .NET Core sử dụng kiến trúc Port & Adapter.",
            "date_modified": "2024-08-20T00:00:00.000Z",
            "author": {
                "name": "Đào Anh Thành",
                "url": "https://github.com/daoanhthanh"
            },
            "tags": [
                ".NET",
                "architecture"
            ]
        },
        {
            "id": "https://daoanhthanh.vn/blog/amber-rat-tien-dung-cho-cicd",
            "content_html": "<p>Tôi yêu bash, nó rất tiện dụng để viết những script nhỏ chạy tự động, đặc biệt là cho CI/CD. Tuy nhiên, thú thực không phải lúc nào tôi cũng nhớ hết syntax của bash, và tôi luôn mong muốn có một công cụ nào đó giúp tôi viết bash script một cách dễ dàng hơn. Cảm ơn trời, Amber đã xuất hiện. Trong bài viết này tôi muốn chia sẻ về Amber và cách nó đã khiến việc lập trình bash trở nên thú vị hơn.</p>\n<!-- -->\n<h2 class=\"anchor anchorTargetHideOnScrollNavbar_Omnb\" id=\"amber-là-gì\">Amber là gì?<a href=\"https://daoanhthanh.vn/blog/amber-rat-tien-dung-cho-cicd#amber-l%C3%A0-g%C3%AC\" class=\"hash-link\" aria-label=\"Chuyển đến tiêu đề\" title=\"Chuyển đến tiêu đề\" translate=\"no\">​</a></h2>\n<p>Chắc chắn không phải tên của vợ ông thuyền trưởng nào đó, Amber được giới thiệu là một ngôn ngữ biên dịch được viết bằng <a href=\"https://www.rust-lang.org/\" target=\"_blank\" rel=\"noopener noreferrer\">rust</a>, giúp biên dịch code sang bash. Theo như giới thiệu trên trang chủ:</p>\n<blockquote><p>Amber là ngôn ngữ lập trình được biên dịch thành Bash Script. Nó được thiết kế với cú pháp hiện\nđại, các tính năng an toàn, typesafe và các chức năng thiết thực mà Bash không thể cung cấp.\n<cite><a href=\"https://docs.amber-lang.com/getting_started/getting_started\" target=\"_blank\" rel=\"noopener noreferrer\">Amber</a></cite></p></blockquote>\n<p>Tóm cái váy lại, <strong>Amber</strong> là một lớp bổ sung typesafe cho <strong>bash script</strong>. Nó giống như trường hợp của <strong>Typescript</strong> và <strong>Javascript</strong> vậy. Bạn có thể tìm hiểu kỹ hơn về syntax của Amber ở <a href=\"https://amber-lang.com/\" target=\"_blank\" rel=\"noopener noreferrer\">trang chủ</a>.</p>\n<h2 class=\"anchor anchorTargetHideOnScrollNavbar_Omnb\" id=\"cài-đặt-amber\">Cài đặt Amber<a href=\"https://daoanhthanh.vn/blog/amber-rat-tien-dung-cho-cicd#c%C3%A0i-%C4%91%E1%BA%B7t-amber\" class=\"hash-link\" aria-label=\"Chuyển đến tiêu đề\" title=\"Chuyển đến tiêu đề\" translate=\"no\">​</a></h2>\n<ul>\n<li class=\"\">\n<p>Với MacOS/Linux, việc này rất đơn giản:</p>\n<div class=\"language-bash codeBlockContainer_APcc theme-code-block\" style=\"--prism-background-color:hsl(230, 1%, 98%);--prism-color:hsl(230, 8%, 24%)\"><div class=\"codeBlockContent_m3Ux\"><pre tabindex=\"0\" class=\"prism-code language-bash codeBlock_qGQc thin-scrollbar\" style=\"background-color:hsl(230, 1%, 98%);color:hsl(230, 8%, 24%)\"><code class=\"codeBlockLines_p187\"><span class=\"token-line\" style=\"color:hsl(230, 8%, 24%)\"><span class=\"token function\" style=\"color:hsl(221, 87%, 60%)\">curl</span><span class=\"token plain\"> </span><span class=\"token parameter variable\" style=\"color:hsl(221, 87%, 60%)\">-s</span><span class=\"token plain\"> </span><span class=\"token string\" style=\"color:hsl(119, 34%, 47%)\">\"https://raw.githubusercontent.com/Ph0enixKM/AmberNative/master/setup/install.sh\"</span><span class=\"token plain\"> </span><span class=\"token operator\" style=\"color:hsl(221, 87%, 60%)\">|</span><span class=\"token plain\"> </span><span class=\"token function\" style=\"color:hsl(221, 87%, 60%)\">bash</span><br></span></code></pre><div class=\"buttonGroup_6DOT\"><button type=\"button\" aria-label=\"Sao chép mã nguồn\" title=\"Sao chép\" class=\"clean-btn\"><span class=\"copyButtonIcons_FhaS\" aria-hidden=\"true\"><svg viewBox=\"0 0 24 24\" class=\"copyButtonIcon_phi_\"><path fill=\"currentColor\" d=\"M19,21H8V7H19M19,5H8A2,2 0 0,0 6,7V21A2,2 0 0,0 8,23H19A2,2 0 0,0 21,21V7A2,2 0 0,0 19,5M16,1H4A2,2 0 0,0 2,3V17H4V3H16V1Z\"></path></svg><svg viewBox=\"0 0 24 24\" class=\"copyButtonSuccessIcon_FfTR\"><path fill=\"currentColor\" d=\"M21,7L9,19L3.5,13.5L4.91,12.09L9,16.17L19.59,5.59L21,7Z\"></path></svg></span></button></div></div></div>\n</li>\n<li class=\"\">\n<p>Còn với Windows?</p>\n<p>Chà, bạn không thể cài trực tiếp trên Win vì lý do rất đơn giản: <strong>Windows không hỗ trợ bash</strong>, nên việc cài Amber trên Win không mang ý nghĩa gì cả. Tuy nhiên, bạn có thể cài trên WSL (Windows Subsystem for Linux) hoặc sử dụng Docker. Có những lưu ý thêm cho việc cài đặt trên win, bạn hãy tham khảo trên <a href=\"https://docs.amber-lang.com/getting_started/installation\" target=\"_blank\" rel=\"noopener noreferrer\">trang chủ</a>.</p>\n</li>\n</ul>\n<h2 class=\"anchor anchorTargetHideOnScrollNavbar_Omnb\" id=\"sử-dụng-amber\">Sử dụng Amber<a href=\"https://daoanhthanh.vn/blog/amber-rat-tien-dung-cho-cicd#s%E1%BB%AD-d%E1%BB%A5ng-amber\" class=\"hash-link\" aria-label=\"Chuyển đến tiêu đề\" title=\"Chuyển đến tiêu đề\" translate=\"no\">​</a></h2>\n<p>Trước tiên, nếu bạn sử dụng VsCode, hãy thêm extension này để làm việc với Amber dễ dàng hơn: <a href=\"https://marketplace.visualstudio.com/items?itemName=Ph0enixKM.amber-language\" target=\"_blank\" rel=\"noopener noreferrer\">Amber Language</a>. Nếu bạn biết các công cụ nào hỗ trợ vim/Sublime/Zed, hãy chia sẻ với tôi nhé.</p>\n<p>Làm quen với Amber khá đơn giản do syntax được dựa trên ECMA script.</p>\n<div class=\"language-javascript codeBlockContainer_APcc theme-code-block\" style=\"--prism-background-color:hsl(230, 1%, 98%);--prism-color:hsl(230, 8%, 24%)\"><div class=\"codeBlockContent_m3Ux\"><pre tabindex=\"0\" class=\"prism-code language-javascript codeBlock_qGQc thin-scrollbar\" style=\"background-color:hsl(230, 1%, 98%);color:hsl(230, 8%, 24%)\"><code class=\"codeBlockLines_p187\"><span class=\"token-line\" style=\"color:hsl(230, 8%, 24%)\"><span class=\"token comment\" style=\"color:hsl(230, 4%, 64%)\">// example.ab</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:hsl(230, 8%, 24%)\"><span class=\"token plain\" style=\"display:inline-block\"></span><br></span><span class=\"token-line\" style=\"color:hsl(230, 8%, 24%)\"><span class=\"token plain\"></span><span class=\"token keyword\" style=\"color:hsl(301, 63%, 40%)\">let</span><span class=\"token plain\"> target </span><span class=\"token operator\" style=\"color:hsl(221, 87%, 60%)\">=</span><span class=\"token plain\"> </span><span class=\"token string\" style=\"color:hsl(119, 34%, 47%)\">\"my friend\"</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:hsl(230, 8%, 24%)\"><span class=\"token plain\" style=\"display:inline-block\"></span><br></span><span class=\"token-line\" style=\"color:hsl(230, 8%, 24%)\"><span class=\"token plain\"></span><span class=\"token keyword\" style=\"color:hsl(301, 63%, 40%)\">let</span><span class=\"token plain\"> message </span><span class=\"token operator\" style=\"color:hsl(221, 87%, 60%)\">=</span><span class=\"token plain\"> </span><span class=\"token string\" style=\"color:hsl(119, 34%, 47%)\">\"Hello, {target}! Want some Shawarma?\"</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:hsl(230, 8%, 24%)\"><span class=\"token plain\"></span><span class=\"token keyword\" style=\"color:hsl(301, 63%, 40%)\">let</span><span class=\"token plain\"> repeated_times </span><span class=\"token operator\" style=\"color:hsl(221, 87%, 60%)\">=</span><span class=\"token plain\"> </span><span class=\"token number\" style=\"color:hsl(35, 99%, 36%)\">3</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:hsl(230, 8%, 24%)\"><span class=\"token plain\" style=\"display:inline-block\"></span><br></span><span class=\"token-line\" style=\"color:hsl(230, 8%, 24%)\"><span class=\"token plain\"></span><span class=\"token comment\" style=\"color:hsl(230, 4%, 64%)\">// In ra câu chào</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:hsl(230, 8%, 24%)\"><span class=\"token plain\">loop i </span><span class=\"token keyword\" style=\"color:hsl(301, 63%, 40%)\">in</span><span class=\"token plain\"> </span><span class=\"token number\" style=\"color:hsl(35, 99%, 36%)\">0.</span><span class=\"token punctuation\" style=\"color:hsl(119, 34%, 47%)\">.</span><span class=\"token property-access\">repeated_times</span><span class=\"token plain\"> </span><span class=\"token punctuation\" style=\"color:hsl(119, 34%, 47%)\">{</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:hsl(230, 8%, 24%)\"><span class=\"token plain\">    echo message</span><br></span><span class=\"token-line\" style=\"color:hsl(230, 8%, 24%)\"><span class=\"token plain\"></span><span class=\"token punctuation\" style=\"color:hsl(119, 34%, 47%)\">}</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:hsl(230, 8%, 24%)\"><span class=\"token plain\" style=\"display:inline-block\"></span><br></span><span class=\"token-line\" style=\"color:hsl(230, 8%, 24%)\"><span class=\"token plain\"></span><span class=\"token keyword\" style=\"color:hsl(301, 63%, 40%)\">let</span><span class=\"token plain\"> age </span><span class=\"token operator\" style=\"color:hsl(221, 87%, 60%)\">=</span><span class=\"token plain\"> </span><span class=\"token number\" style=\"color:hsl(35, 99%, 36%)\">10</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:hsl(230, 8%, 24%)\"><span class=\"token plain\" style=\"display:inline-block\"></span><br></span><span class=\"token-line\" style=\"color:hsl(230, 8%, 24%)\"><span class=\"token plain\"></span><span class=\"token comment\" style=\"color:hsl(230, 4%, 64%)\">// If-else</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:hsl(230, 8%, 24%)\"><span class=\"token plain\"></span><span class=\"token keyword control-flow\" style=\"color:hsl(301, 63%, 40%)\">if</span><span class=\"token plain\"> age </span><span class=\"token operator\" style=\"color:hsl(221, 87%, 60%)\">&lt;</span><span class=\"token plain\"> </span><span class=\"token number\" style=\"color:hsl(35, 99%, 36%)\">18</span><span class=\"token plain\"> </span><span class=\"token punctuation\" style=\"color:hsl(119, 34%, 47%)\">{</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:hsl(230, 8%, 24%)\"><span class=\"token plain\">    echo </span><span class=\"token string\" style=\"color:hsl(119, 34%, 47%)\">\"\\nI want za spicy garlic!\\n\"</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:hsl(230, 8%, 24%)\"><span class=\"token plain\"></span><span class=\"token punctuation\" style=\"color:hsl(119, 34%, 47%)\">}</span><span class=\"token plain\"> </span><span class=\"token keyword control-flow\" style=\"color:hsl(301, 63%, 40%)\">else</span><span class=\"token plain\"> </span><span class=\"token punctuation\" style=\"color:hsl(119, 34%, 47%)\">{</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:hsl(230, 8%, 24%)\"><span class=\"token plain\">    echo </span><span class=\"token string\" style=\"color:hsl(119, 34%, 47%)\">\"\\nI want za regular garlic!\\n\"</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:hsl(230, 8%, 24%)\"><span class=\"token plain\"></span><span class=\"token punctuation\" style=\"color:hsl(119, 34%, 47%)\">}</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:hsl(230, 8%, 24%)\"><span class=\"token plain\" style=\"display:inline-block\"></span><br></span><span class=\"token-line\" style=\"color:hsl(230, 8%, 24%)\"><span class=\"token plain\"></span><span class=\"token comment\" style=\"color:hsl(230, 4%, 64%)\">// array loop</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:hsl(230, 8%, 24%)\"><span class=\"token plain\"></span><span class=\"token keyword\" style=\"color:hsl(301, 63%, 40%)\">let</span><span class=\"token plain\"> fruits </span><span class=\"token operator\" style=\"color:hsl(221, 87%, 60%)\">=</span><span class=\"token plain\"> </span><span class=\"token punctuation\" style=\"color:hsl(119, 34%, 47%)\">[</span><span class=\"token string\" style=\"color:hsl(119, 34%, 47%)\">\"apple\"</span><span class=\"token punctuation\" style=\"color:hsl(119, 34%, 47%)\">,</span><span class=\"token plain\"> </span><span class=\"token string\" style=\"color:hsl(119, 34%, 47%)\">\"banana\"</span><span class=\"token punctuation\" style=\"color:hsl(119, 34%, 47%)\">,</span><span class=\"token plain\"> </span><span class=\"token string\" style=\"color:hsl(119, 34%, 47%)\">\"cherry\"</span><span class=\"token punctuation\" style=\"color:hsl(119, 34%, 47%)\">,</span><span class=\"token plain\"> </span><span class=\"token string\" style=\"color:hsl(119, 34%, 47%)\">\"date\"</span><span class=\"token punctuation\" style=\"color:hsl(119, 34%, 47%)\">]</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:hsl(230, 8%, 24%)\"><span class=\"token plain\">echo </span><span class=\"token string\" style=\"color:hsl(119, 34%, 47%)\">\"My favorite fruits are:\"</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:hsl(230, 8%, 24%)\"><span class=\"token plain\">loop fruit </span><span class=\"token keyword\" style=\"color:hsl(301, 63%, 40%)\">in</span><span class=\"token plain\"> fruits </span><span class=\"token punctuation\" style=\"color:hsl(119, 34%, 47%)\">{</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:hsl(230, 8%, 24%)\"><span class=\"token plain\">    echo fruit</span><br></span><span class=\"token-line\" style=\"color:hsl(230, 8%, 24%)\"><span class=\"token plain\"></span><span class=\"token punctuation\" style=\"color:hsl(119, 34%, 47%)\">}</span><br></span></code></pre><div class=\"buttonGroup_6DOT\"><button type=\"button\" aria-label=\"Sao chép mã nguồn\" title=\"Sao chép\" class=\"clean-btn\"><span class=\"copyButtonIcons_FhaS\" aria-hidden=\"true\"><svg viewBox=\"0 0 24 24\" class=\"copyButtonIcon_phi_\"><path fill=\"currentColor\" d=\"M19,21H8V7H19M19,5H8A2,2 0 0,0 6,7V21A2,2 0 0,0 8,23H19A2,2 0 0,0 21,21V7A2,2 0 0,0 19,5M16,1H4A2,2 0 0,0 2,3V17H4V3H16V1Z\"></path></svg><svg viewBox=\"0 0 24 24\" class=\"copyButtonSuccessIcon_FfTR\"><path fill=\"currentColor\" d=\"M21,7L9,19L3.5,13.5L4.91,12.09L9,16.17L19.59,5.59L21,7Z\"></path></svg></span></button></div></div></div>\n<p>Để chạy script trên, sử dụng lệnh sau:</p>\n<div class=\"language-bash codeBlockContainer_APcc theme-code-block\" style=\"--prism-background-color:hsl(230, 1%, 98%);--prism-color:hsl(230, 8%, 24%)\"><div class=\"codeBlockContent_m3Ux\"><pre tabindex=\"0\" class=\"prism-code language-bash codeBlock_qGQc thin-scrollbar\" style=\"background-color:hsl(230, 1%, 98%);color:hsl(230, 8%, 24%)\"><code class=\"codeBlockLines_p187\"><span class=\"token-line\" style=\"color:hsl(230, 8%, 24%)\"><span class=\"token plain\">amber example.ab</span><br></span></code></pre><div class=\"buttonGroup_6DOT\"><button type=\"button\" aria-label=\"Sao chép mã nguồn\" title=\"Sao chép\" class=\"clean-btn\"><span class=\"copyButtonIcons_FhaS\" aria-hidden=\"true\"><svg viewBox=\"0 0 24 24\" class=\"copyButtonIcon_phi_\"><path fill=\"currentColor\" d=\"M19,21H8V7H19M19,5H8A2,2 0 0,0 6,7V21A2,2 0 0,0 8,23H19A2,2 0 0,0 21,21V7A2,2 0 0,0 19,5M16,1H4A2,2 0 0,0 2,3V17H4V3H16V1Z\"></path></svg><svg viewBox=\"0 0 24 24\" class=\"copyButtonSuccessIcon_FfTR\"><path fill=\"currentColor\" d=\"M21,7L9,19L3.5,13.5L4.91,12.09L9,16.17L19.59,5.59L21,7Z\"></path></svg></span></button></div></div></div>\n<p>Nó sẽ biên dịch code sang bash và chạy nó. Kết quả sẽ như sau:</p>\n<div class=\"language-plaintext codeBlockContainer_APcc theme-code-block\" style=\"--prism-background-color:hsl(230, 1%, 98%);--prism-color:hsl(230, 8%, 24%)\"><div class=\"codeBlockContent_m3Ux\"><pre tabindex=\"0\" class=\"prism-code language-plaintext codeBlock_qGQc thin-scrollbar\" style=\"background-color:hsl(230, 1%, 98%);color:hsl(230, 8%, 24%)\"><code class=\"codeBlockLines_p187\"><span class=\"token-line\" style=\"color:hsl(230, 8%, 24%)\"><span class=\"token plain\">Hello, my friend! Want some Shawarma?</span><br></span><span class=\"token-line\" style=\"color:hsl(230, 8%, 24%)\"><span class=\"token plain\">Hello, my friend! Want some Shawarma?</span><br></span><span class=\"token-line\" style=\"color:hsl(230, 8%, 24%)\"><span class=\"token plain\">Hello, my friend! Want some Shawarma?</span><br></span><span class=\"token-line\" style=\"color:hsl(230, 8%, 24%)\"><span class=\"token plain\" style=\"display:inline-block\"></span><br></span><span class=\"token-line\" style=\"color:hsl(230, 8%, 24%)\"><span class=\"token plain\">I want za spicy garlic!</span><br></span><span class=\"token-line\" style=\"color:hsl(230, 8%, 24%)\"><span class=\"token plain\" style=\"display:inline-block\"></span><br></span><span class=\"token-line\" style=\"color:hsl(230, 8%, 24%)\"><span class=\"token plain\">My favorite fruits are:</span><br></span><span class=\"token-line\" style=\"color:hsl(230, 8%, 24%)\"><span class=\"token plain\">apple</span><br></span><span class=\"token-line\" style=\"color:hsl(230, 8%, 24%)\"><span class=\"token plain\">banana</span><br></span><span class=\"token-line\" style=\"color:hsl(230, 8%, 24%)\"><span class=\"token plain\">cherry</span><br></span><span class=\"token-line\" style=\"color:hsl(230, 8%, 24%)\"><span class=\"token plain\">date</span><br></span></code></pre><div class=\"buttonGroup_6DOT\"><button type=\"button\" aria-label=\"Sao chép mã nguồn\" title=\"Sao chép\" class=\"clean-btn\"><span class=\"copyButtonIcons_FhaS\" aria-hidden=\"true\"><svg viewBox=\"0 0 24 24\" class=\"copyButtonIcon_phi_\"><path fill=\"currentColor\" d=\"M19,21H8V7H19M19,5H8A2,2 0 0,0 6,7V21A2,2 0 0,0 8,23H19A2,2 0 0,0 21,21V7A2,2 0 0,0 19,5M16,1H4A2,2 0 0,0 2,3V17H4V3H16V1Z\"></path></svg><svg viewBox=\"0 0 24 24\" class=\"copyButtonSuccessIcon_FfTR\"><path fill=\"currentColor\" d=\"M21,7L9,19L3.5,13.5L4.91,12.09L9,16.17L19.59,5.59L21,7Z\"></path></svg></span></button></div></div></div>\n<p>Điều tuyệt vời ở đây là amber hỗ trợ compile file sang bash script. Bạn có thể sử dụng output của nó để chạy trên bất kỳ môi trường unix nào.</p>\n<div class=\"language-bash codeBlockContainer_APcc theme-code-block\" style=\"--prism-background-color:hsl(230, 1%, 98%);--prism-color:hsl(230, 8%, 24%)\"><div class=\"codeBlockContent_m3Ux\"><pre tabindex=\"0\" class=\"prism-code language-bash codeBlock_qGQc thin-scrollbar\" style=\"background-color:hsl(230, 1%, 98%);color:hsl(230, 8%, 24%)\"><code class=\"codeBlockLines_p187\"><span class=\"token-line\" style=\"color:hsl(230, 8%, 24%)\"><span class=\"token plain\">amber input.ab output.sh</span><br></span></code></pre><div class=\"buttonGroup_6DOT\"><button type=\"button\" aria-label=\"Sao chép mã nguồn\" title=\"Sao chép\" class=\"clean-btn\"><span class=\"copyButtonIcons_FhaS\" aria-hidden=\"true\"><svg viewBox=\"0 0 24 24\" class=\"copyButtonIcon_phi_\"><path fill=\"currentColor\" d=\"M19,21H8V7H19M19,5H8A2,2 0 0,0 6,7V21A2,2 0 0,0 8,23H19A2,2 0 0,0 21,21V7A2,2 0 0,0 19,5M16,1H4A2,2 0 0,0 2,3V17H4V3H16V1Z\"></path></svg><svg viewBox=\"0 0 24 24\" class=\"copyButtonSuccessIcon_FfTR\"><path fill=\"currentColor\" d=\"M21,7L9,19L3.5,13.5L4.91,12.09L9,16.17L19.59,5.59L21,7Z\"></path></svg></span></button></div></div></div>\n<p>Thử với file ví dụ bên trên (<code>amber example.ab example.sh</code>), đây là file bash script được tạo ra:</p>\n<div class=\"language-bash codeBlockContainer_APcc theme-code-block\" style=\"--prism-background-color:hsl(230, 1%, 98%);--prism-color:hsl(230, 8%, 24%)\"><div class=\"codeBlockContent_m3Ux\"><pre tabindex=\"0\" class=\"prism-code language-bash codeBlock_qGQc thin-scrollbar\" style=\"background-color:hsl(230, 1%, 98%);color:hsl(230, 8%, 24%)\"><code class=\"codeBlockLines_p187\"><span class=\"token-line\" style=\"color:hsl(230, 8%, 24%)\"><span class=\"token assign-left variable\" style=\"color:hsl(221, 87%, 60%)\">__0_target</span><span class=\"token operator\" style=\"color:hsl(221, 87%, 60%)\">=</span><span class=\"token string\" style=\"color:hsl(119, 34%, 47%)\">\"my friend\"</span><span class=\"token punctuation\" style=\"color:hsl(119, 34%, 47%)\">;</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:hsl(230, 8%, 24%)\"><span class=\"token plain\"></span><span class=\"token assign-left variable\" style=\"color:hsl(221, 87%, 60%)\">__1_message</span><span class=\"token operator\" style=\"color:hsl(221, 87%, 60%)\">=</span><span class=\"token string\" style=\"color:hsl(119, 34%, 47%)\">\"Hello, </span><span class=\"token string variable\" style=\"color:hsl(221, 87%, 60%)\">${__0_target}</span><span class=\"token string\" style=\"color:hsl(119, 34%, 47%)\">! Want some Shawarma?\"</span><span class=\"token punctuation\" style=\"color:hsl(119, 34%, 47%)\">;</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:hsl(230, 8%, 24%)\"><span class=\"token plain\"></span><span class=\"token assign-left variable\" style=\"color:hsl(221, 87%, 60%)\">__2_repeated_times</span><span class=\"token operator\" style=\"color:hsl(221, 87%, 60%)\">=</span><span class=\"token number\" style=\"color:hsl(35, 99%, 36%)\">3</span><span class=\"token punctuation\" style=\"color:hsl(119, 34%, 47%)\">;</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:hsl(230, 8%, 24%)\"><span class=\"token plain\" style=\"display:inline-block\"></span><br></span><span class=\"token-line\" style=\"color:hsl(230, 8%, 24%)\"><span class=\"token plain\"></span><span class=\"token keyword\" style=\"color:hsl(301, 63%, 40%)\">for</span><span class=\"token plain\"> </span><span class=\"token for-or-select variable\" style=\"color:hsl(221, 87%, 60%)\">i</span><span class=\"token plain\"> </span><span class=\"token keyword\" style=\"color:hsl(301, 63%, 40%)\">in</span><span class=\"token plain\"> </span><span class=\"token variable\" style=\"color:hsl(221, 87%, 60%)\">$(</span><span class=\"token variable function\" style=\"color:hsl(221, 87%, 60%)\">seq</span><span class=\"token variable\" style=\"color:hsl(221, 87%, 60%)\"> </span><span class=\"token variable number\" style=\"color:hsl(35, 99%, 36%)\">0</span><span class=\"token variable\" style=\"color:hsl(221, 87%, 60%)\"> </span><span class=\"token variable punctuation\" style=\"color:hsl(119, 34%, 47%)\">$(</span><span class=\"token variable\" style=\"color:hsl(221, 87%, 60%)\">echo $</span><span class=\"token variable punctuation\" style=\"color:hsl(119, 34%, 47%)\">{</span><span class=\"token variable\" style=\"color:hsl(221, 87%, 60%)\">__2_repeated_times</span><span class=\"token variable punctuation\" style=\"color:hsl(119, 34%, 47%)\">}</span><span class=\"token variable\" style=\"color:hsl(221, 87%, 60%)\"> </span><span class=\"token variable string\" style=\"color:hsl(119, 34%, 47%)\">'-'</span><span class=\"token variable\" style=\"color:hsl(221, 87%, 60%)\"> </span><span class=\"token variable number\" style=\"color:hsl(35, 99%, 36%)\">1</span><span class=\"token variable\" style=\"color:hsl(221, 87%, 60%)\"> </span><span class=\"token variable operator\" style=\"color:hsl(221, 87%, 60%)\">|</span><span class=\"token variable\" style=\"color:hsl(221, 87%, 60%)\"> </span><span class=\"token variable function\" style=\"color:hsl(221, 87%, 60%)\">bc</span><span class=\"token variable\" style=\"color:hsl(221, 87%, 60%)\"> </span><span class=\"token variable parameter variable\" style=\"color:hsl(221, 87%, 60%)\">-l</span><span class=\"token variable\" style=\"color:hsl(221, 87%, 60%)\"> </span><span class=\"token variable operator\" style=\"color:hsl(221, 87%, 60%)\">|</span><span class=\"token variable\" style=\"color:hsl(221, 87%, 60%)\"> </span><span class=\"token variable function\" style=\"color:hsl(221, 87%, 60%)\">sed</span><span class=\"token variable\" style=\"color:hsl(221, 87%, 60%)\"> </span><span class=\"token variable string\" style=\"color:hsl(119, 34%, 47%)\">'/\\./ s/\\.\\{0,1\\}0\\{1,\\}$//'</span><span class=\"token variable punctuation\" style=\"color:hsl(119, 34%, 47%)\">)</span><span class=\"token variable\" style=\"color:hsl(221, 87%, 60%)\">)</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:hsl(230, 8%, 24%)\"><span class=\"token plain\"></span><span class=\"token keyword\" style=\"color:hsl(301, 63%, 40%)\">do</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:hsl(230, 8%, 24%)\"><span class=\"token plain\">    </span><span class=\"token builtin class-name\" style=\"color:hsl(35, 99%, 36%)\">echo</span><span class=\"token plain\"> </span><span class=\"token string\" style=\"color:hsl(119, 34%, 47%)\">\"</span><span class=\"token string variable\" style=\"color:hsl(221, 87%, 60%)\">${__1_message}</span><span class=\"token string\" style=\"color:hsl(119, 34%, 47%)\">\"</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:hsl(230, 8%, 24%)\"><span class=\"token plain\"></span><span class=\"token keyword\" style=\"color:hsl(301, 63%, 40%)\">done</span><span class=\"token punctuation\" style=\"color:hsl(119, 34%, 47%)\">;</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:hsl(230, 8%, 24%)\"><span class=\"token plain\" style=\"display:inline-block\"></span><br></span><span class=\"token-line\" style=\"color:hsl(230, 8%, 24%)\"><span class=\"token plain\"></span><span class=\"token assign-left variable\" style=\"color:hsl(221, 87%, 60%)\">__3_age</span><span class=\"token operator\" style=\"color:hsl(221, 87%, 60%)\">=</span><span class=\"token number\" style=\"color:hsl(35, 99%, 36%)\">10</span><span class=\"token punctuation\" style=\"color:hsl(119, 34%, 47%)\">;</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:hsl(230, 8%, 24%)\"><span class=\"token plain\" style=\"display:inline-block\"></span><br></span><span class=\"token-line\" style=\"color:hsl(230, 8%, 24%)\"><span class=\"token plain\"></span><span class=\"token keyword\" style=\"color:hsl(301, 63%, 40%)\">if</span><span class=\"token plain\"> </span><span class=\"token punctuation\" style=\"color:hsl(119, 34%, 47%)\">[</span><span class=\"token plain\"> </span><span class=\"token variable\" style=\"color:hsl(221, 87%, 60%)\">$(</span><span class=\"token variable builtin class-name\" style=\"color:hsl(35, 99%, 36%)\">echo</span><span class=\"token variable\" style=\"color:hsl(221, 87%, 60%)\"> $</span><span class=\"token variable punctuation\" style=\"color:hsl(119, 34%, 47%)\">{</span><span class=\"token variable\" style=\"color:hsl(221, 87%, 60%)\">__3_age</span><span class=\"token variable punctuation\" style=\"color:hsl(119, 34%, 47%)\">}</span><span class=\"token variable\" style=\"color:hsl(221, 87%, 60%)\"> </span><span class=\"token variable string\" style=\"color:hsl(119, 34%, 47%)\">'&lt;'</span><span class=\"token variable\" style=\"color:hsl(221, 87%, 60%)\"> </span><span class=\"token variable number\" style=\"color:hsl(35, 99%, 36%)\">18</span><span class=\"token variable\" style=\"color:hsl(221, 87%, 60%)\"> </span><span class=\"token variable operator\" style=\"color:hsl(221, 87%, 60%)\">|</span><span class=\"token variable\" style=\"color:hsl(221, 87%, 60%)\"> </span><span class=\"token variable function\" style=\"color:hsl(221, 87%, 60%)\">bc</span><span class=\"token variable\" style=\"color:hsl(221, 87%, 60%)\"> </span><span class=\"token variable parameter variable\" style=\"color:hsl(221, 87%, 60%)\">-l</span><span class=\"token variable\" style=\"color:hsl(221, 87%, 60%)\"> </span><span class=\"token variable operator\" style=\"color:hsl(221, 87%, 60%)\">|</span><span class=\"token variable\" style=\"color:hsl(221, 87%, 60%)\"> </span><span class=\"token variable function\" style=\"color:hsl(221, 87%, 60%)\">sed</span><span class=\"token variable\" style=\"color:hsl(221, 87%, 60%)\"> </span><span class=\"token variable string\" style=\"color:hsl(119, 34%, 47%)\">'/\\./ s/\\.\\{0,1\\}0\\{1,\\}$//'</span><span class=\"token variable\" style=\"color:hsl(221, 87%, 60%)\">)</span><span class=\"token plain\"> </span><span class=\"token operator\" style=\"color:hsl(221, 87%, 60%)\">!=</span><span class=\"token plain\"> </span><span class=\"token number\" style=\"color:hsl(35, 99%, 36%)\">0</span><span class=\"token plain\"> </span><span class=\"token punctuation\" style=\"color:hsl(119, 34%, 47%)\">]</span><span class=\"token punctuation\" style=\"color:hsl(119, 34%, 47%)\">;</span><span class=\"token plain\"> </span><span class=\"token keyword\" style=\"color:hsl(301, 63%, 40%)\">then</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:hsl(230, 8%, 24%)\"><span class=\"token plain\">    </span><span class=\"token builtin class-name\" style=\"color:hsl(35, 99%, 36%)\">echo</span><span class=\"token plain\"> </span><span class=\"token string\" style=\"color:hsl(119, 34%, 47%)\">\"</span><br></span><span class=\"token-line\" style=\"color:hsl(230, 8%, 24%)\"><span class=\"token string\" style=\"color:hsl(119, 34%, 47%)\">I want za spicy garlic!</span><br></span><span class=\"token-line\" style=\"color:hsl(230, 8%, 24%)\"><span class=\"token string\" style=\"color:hsl(119, 34%, 47%)\">\"</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:hsl(230, 8%, 24%)\"><span class=\"token plain\"></span><span class=\"token keyword\" style=\"color:hsl(301, 63%, 40%)\">else</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:hsl(230, 8%, 24%)\"><span class=\"token plain\">    </span><span class=\"token builtin class-name\" style=\"color:hsl(35, 99%, 36%)\">echo</span><span class=\"token plain\"> </span><span class=\"token string\" style=\"color:hsl(119, 34%, 47%)\">\"</span><br></span><span class=\"token-line\" style=\"color:hsl(230, 8%, 24%)\"><span class=\"token string\" style=\"color:hsl(119, 34%, 47%)\">I want za regular garlic!</span><br></span><span class=\"token-line\" style=\"color:hsl(230, 8%, 24%)\"><span class=\"token string\" style=\"color:hsl(119, 34%, 47%)\">\"</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:hsl(230, 8%, 24%)\"><span class=\"token plain\"></span><span class=\"token keyword\" style=\"color:hsl(301, 63%, 40%)\">fi</span><span class=\"token punctuation\" style=\"color:hsl(119, 34%, 47%)\">;</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:hsl(230, 8%, 24%)\"><span class=\"token plain\" style=\"display:inline-block\"></span><br></span><span class=\"token-line\" style=\"color:hsl(230, 8%, 24%)\"><span class=\"token plain\"></span><span class=\"token assign-left variable\" style=\"color:hsl(221, 87%, 60%)\">__AMBER_ARRAY_0</span><span class=\"token operator\" style=\"color:hsl(221, 87%, 60%)\">=</span><span class=\"token punctuation\" style=\"color:hsl(119, 34%, 47%)\">(</span><span class=\"token string\" style=\"color:hsl(119, 34%, 47%)\">\"apple\"</span><span class=\"token plain\"> </span><span class=\"token string\" style=\"color:hsl(119, 34%, 47%)\">\"banana\"</span><span class=\"token plain\"> </span><span class=\"token string\" style=\"color:hsl(119, 34%, 47%)\">\"cherry\"</span><span class=\"token plain\"> </span><span class=\"token string\" style=\"color:hsl(119, 34%, 47%)\">\"date\"</span><span class=\"token punctuation\" style=\"color:hsl(119, 34%, 47%)\">)</span><span class=\"token punctuation\" style=\"color:hsl(119, 34%, 47%)\">;</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:hsl(230, 8%, 24%)\"><span class=\"token plain\"></span><span class=\"token assign-left variable\" style=\"color:hsl(221, 87%, 60%)\">__4_fruits</span><span class=\"token operator\" style=\"color:hsl(221, 87%, 60%)\">=</span><span class=\"token punctuation\" style=\"color:hsl(119, 34%, 47%)\">(</span><span class=\"token string\" style=\"color:hsl(119, 34%, 47%)\">\"</span><span class=\"token string variable\" style=\"color:hsl(221, 87%, 60%)\">${__AMBER_ARRAY_0</span><span class=\"token string variable punctuation\" style=\"color:hsl(119, 34%, 47%)\">[</span><span class=\"token string variable\" style=\"color:hsl(221, 87%, 60%)\">@</span><span class=\"token string variable punctuation\" style=\"color:hsl(119, 34%, 47%)\">]</span><span class=\"token string variable\" style=\"color:hsl(221, 87%, 60%)\">}</span><span class=\"token string\" style=\"color:hsl(119, 34%, 47%)\">\"</span><span class=\"token punctuation\" style=\"color:hsl(119, 34%, 47%)\">)</span><span class=\"token punctuation\" style=\"color:hsl(119, 34%, 47%)\">;</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:hsl(230, 8%, 24%)\"><span class=\"token plain\" style=\"display:inline-block\"></span><br></span><span class=\"token-line\" style=\"color:hsl(230, 8%, 24%)\"><span class=\"token plain\"></span><span class=\"token builtin class-name\" style=\"color:hsl(35, 99%, 36%)\">echo</span><span class=\"token plain\"> </span><span class=\"token string\" style=\"color:hsl(119, 34%, 47%)\">\"My favorite fruits are:\"</span><span class=\"token punctuation\" style=\"color:hsl(119, 34%, 47%)\">;</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:hsl(230, 8%, 24%)\"><span class=\"token plain\" style=\"display:inline-block\"></span><br></span><span class=\"token-line\" style=\"color:hsl(230, 8%, 24%)\"><span class=\"token plain\"></span><span class=\"token keyword\" style=\"color:hsl(301, 63%, 40%)\">for</span><span class=\"token plain\"> </span><span class=\"token for-or-select variable\" style=\"color:hsl(221, 87%, 60%)\">fruit</span><span class=\"token plain\"> </span><span class=\"token keyword\" style=\"color:hsl(301, 63%, 40%)\">in</span><span class=\"token plain\"> </span><span class=\"token string\" style=\"color:hsl(119, 34%, 47%)\">\"</span><span class=\"token string variable\" style=\"color:hsl(221, 87%, 60%)\">${__4_fruits</span><span class=\"token string variable punctuation\" style=\"color:hsl(119, 34%, 47%)\">[</span><span class=\"token string variable\" style=\"color:hsl(221, 87%, 60%)\">@</span><span class=\"token string variable punctuation\" style=\"color:hsl(119, 34%, 47%)\">]</span><span class=\"token string variable\" style=\"color:hsl(221, 87%, 60%)\">}</span><span class=\"token string\" style=\"color:hsl(119, 34%, 47%)\">\"</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:hsl(230, 8%, 24%)\"><span class=\"token plain\"></span><span class=\"token keyword\" style=\"color:hsl(301, 63%, 40%)\">do</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:hsl(230, 8%, 24%)\"><span class=\"token plain\">    </span><span class=\"token builtin class-name\" style=\"color:hsl(35, 99%, 36%)\">echo</span><span class=\"token plain\"> </span><span class=\"token string\" style=\"color:hsl(119, 34%, 47%)\">\"</span><span class=\"token string variable\" style=\"color:hsl(221, 87%, 60%)\">${fruit}</span><span class=\"token string\" style=\"color:hsl(119, 34%, 47%)\">\"</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:hsl(230, 8%, 24%)\"><span class=\"token plain\"></span><span class=\"token keyword\" style=\"color:hsl(301, 63%, 40%)\">done</span><br></span></code></pre><div class=\"buttonGroup_6DOT\"><button type=\"button\" aria-label=\"Sao chép mã nguồn\" title=\"Sao chép\" class=\"clean-btn\"><span class=\"copyButtonIcons_FhaS\" aria-hidden=\"true\"><svg viewBox=\"0 0 24 24\" class=\"copyButtonIcon_phi_\"><path fill=\"currentColor\" d=\"M19,21H8V7H19M19,5H8A2,2 0 0,0 6,7V21A2,2 0 0,0 8,23H19A2,2 0 0,0 21,21V7A2,2 0 0,0 19,5M16,1H4A2,2 0 0,0 2,3V17H4V3H16V1Z\"></path></svg><svg viewBox=\"0 0 24 24\" class=\"copyButtonSuccessIcon_FfTR\"><path fill=\"currentColor\" d=\"M21,7L9,19L3.5,13.5L4.91,12.09L9,16.17L19.59,5.59L21,7Z\"></path></svg></span></button></div></div></div>\n<p>Bạn có thể chạy trực tiếp file trên vì trong quá trình biên dịch Amber đã tự động gán quyền execute cho nó. Thật tuyệt 🎉🎉!</p>\n<p>Bỏ qua việc đặt tên biến, bạn có thể thấy cú pháp của Amber dễ đọc hiểu hơn so với bash rất nhiều. Điều này giúp bạn viết script nhanh hơn, dễ đọc hơn và dễ bảo trì hơn.</p>\n<p>Tuy nhiên nếu tinh ý bạn có thể thấy Amber xử lý logic loop chưa tốt ở thời điểm hiện tại. Như ví dụ phía trên, Amber đã sử dụng:</p>\n<div class=\"language-bash codeBlockContainer_APcc theme-code-block\" style=\"--prism-background-color:hsl(230, 1%, 98%);--prism-color:hsl(230, 8%, 24%)\"><div class=\"codeBlockContent_m3Ux\"><pre tabindex=\"0\" class=\"prism-code language-bash codeBlock_qGQc thin-scrollbar\" style=\"background-color:hsl(230, 1%, 98%);color:hsl(230, 8%, 24%)\"><code class=\"codeBlockLines_p187\"><span class=\"token-line\" style=\"color:hsl(230, 8%, 24%)\"><span class=\"token keyword\" style=\"color:hsl(301, 63%, 40%)\">if</span><span class=\"token plain\"> </span><span class=\"token punctuation\" style=\"color:hsl(119, 34%, 47%)\">[</span><span class=\"token plain\"> </span><span class=\"token variable\" style=\"color:hsl(221, 87%, 60%)\">$(</span><span class=\"token variable builtin class-name\" style=\"color:hsl(35, 99%, 36%)\">echo</span><span class=\"token variable\" style=\"color:hsl(221, 87%, 60%)\"> $</span><span class=\"token variable punctuation\" style=\"color:hsl(119, 34%, 47%)\">{</span><span class=\"token variable\" style=\"color:hsl(221, 87%, 60%)\">__3_age</span><span class=\"token variable punctuation\" style=\"color:hsl(119, 34%, 47%)\">}</span><span class=\"token variable\" style=\"color:hsl(221, 87%, 60%)\"> </span><span class=\"token variable string\" style=\"color:hsl(119, 34%, 47%)\">'&lt;'</span><span class=\"token variable\" style=\"color:hsl(221, 87%, 60%)\"> </span><span class=\"token variable number\" style=\"color:hsl(35, 99%, 36%)\">18</span><span class=\"token variable\" style=\"color:hsl(221, 87%, 60%)\"> </span><span class=\"token variable operator\" style=\"color:hsl(221, 87%, 60%)\">|</span><span class=\"token variable\" style=\"color:hsl(221, 87%, 60%)\"> </span><span class=\"token variable function\" style=\"color:hsl(221, 87%, 60%)\">bc</span><span class=\"token variable\" style=\"color:hsl(221, 87%, 60%)\"> </span><span class=\"token variable parameter variable\" style=\"color:hsl(221, 87%, 60%)\">-l</span><span class=\"token variable\" style=\"color:hsl(221, 87%, 60%)\"> </span><span class=\"token variable operator\" style=\"color:hsl(221, 87%, 60%)\">|</span><span class=\"token variable\" style=\"color:hsl(221, 87%, 60%)\"> </span><span class=\"token variable function\" style=\"color:hsl(221, 87%, 60%)\">sed</span><span class=\"token variable\" style=\"color:hsl(221, 87%, 60%)\"> </span><span class=\"token variable string\" style=\"color:hsl(119, 34%, 47%)\">'/\\./ s/\\.\\{0,1\\}0\\{1,\\}$//'</span><span class=\"token variable\" style=\"color:hsl(221, 87%, 60%)\">)</span><span class=\"token plain\"> </span><span class=\"token operator\" style=\"color:hsl(221, 87%, 60%)\">!=</span><span class=\"token plain\"> </span><span class=\"token number\" style=\"color:hsl(35, 99%, 36%)\">0</span><span class=\"token plain\"> </span><span class=\"token punctuation\" style=\"color:hsl(119, 34%, 47%)\">]</span><span class=\"token punctuation\" style=\"color:hsl(119, 34%, 47%)\">;</span><span class=\"token plain\"> </span><span class=\"token keyword\" style=\"color:hsl(301, 63%, 40%)\">then</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:hsl(230, 8%, 24%)\"><span class=\"token plain\"></span><span class=\"token comment\" style=\"color:hsl(230, 4%, 64%)\"># execute logic</span><br></span></code></pre><div class=\"buttonGroup_6DOT\"><button type=\"button\" aria-label=\"Sao chép mã nguồn\" title=\"Sao chép\" class=\"clean-btn\"><span class=\"copyButtonIcons_FhaS\" aria-hidden=\"true\"><svg viewBox=\"0 0 24 24\" class=\"copyButtonIcon_phi_\"><path fill=\"currentColor\" d=\"M19,21H8V7H19M19,5H8A2,2 0 0,0 6,7V21A2,2 0 0,0 8,23H19A2,2 0 0,0 21,21V7A2,2 0 0,0 19,5M16,1H4A2,2 0 0,0 2,3V17H4V3H16V1Z\"></path></svg><svg viewBox=\"0 0 24 24\" class=\"copyButtonSuccessIcon_FfTR\"><path fill=\"currentColor\" d=\"M21,7L9,19L3.5,13.5L4.91,12.09L9,16.17L19.59,5.59L21,7Z\"></path></svg></span></button></div></div></div>\n<p>Syntax trên cồng kềnh cộng thêm sử dụng thư viện ngoài (<code>bc</code>). Chắc chắn điều này sẽ chậm và khó đọc hơn so với cách loop truyền thống ở dưới:</p>\n<div class=\"language-bash codeBlockContainer_APcc theme-code-block\" style=\"--prism-background-color:hsl(230, 1%, 98%);--prism-color:hsl(230, 8%, 24%)\"><div class=\"codeBlockContent_m3Ux\"><pre tabindex=\"0\" class=\"prism-code language-bash codeBlock_qGQc thin-scrollbar\" style=\"background-color:hsl(230, 1%, 98%);color:hsl(230, 8%, 24%)\"><code class=\"codeBlockLines_p187\"><span class=\"token-line\" style=\"color:hsl(230, 8%, 24%)\"><span class=\"token keyword\" style=\"color:hsl(301, 63%, 40%)\">for</span><span class=\"token plain\"> </span><span class=\"token variable punctuation\" style=\"color:hsl(119, 34%, 47%)\">((</span><span class=\"token variable\" style=\"color:hsl(221, 87%, 60%)\">i</span><span class=\"token variable operator\" style=\"color:hsl(221, 87%, 60%)\">=</span><span class=\"token variable number\" style=\"color:hsl(35, 99%, 36%)\">0</span><span class=\"token variable punctuation\" style=\"color:hsl(119, 34%, 47%)\">;</span><span class=\"token variable\" style=\"color:hsl(221, 87%, 60%)\"> i </span><span class=\"token variable operator\" style=\"color:hsl(221, 87%, 60%)\">&lt;</span><span class=\"token variable\" style=\"color:hsl(221, 87%, 60%)\"> repeated_times</span><span class=\"token variable punctuation\" style=\"color:hsl(119, 34%, 47%)\">;</span><span class=\"token variable\" style=\"color:hsl(221, 87%, 60%)\"> i</span><span class=\"token variable operator\" style=\"color:hsl(221, 87%, 60%)\">++</span><span class=\"token variable punctuation\" style=\"color:hsl(119, 34%, 47%)\">))</span><span class=\"token punctuation\" style=\"color:hsl(119, 34%, 47%)\">;</span><span class=\"token plain\"> </span><span class=\"token keyword\" style=\"color:hsl(301, 63%, 40%)\">do</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:hsl(230, 8%, 24%)\"><span class=\"token plain\">   </span><span class=\"token comment\" style=\"color:hsl(230, 4%, 64%)\"># execute logic</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:hsl(230, 8%, 24%)\"><span class=\"token plain\"></span><span class=\"token keyword\" style=\"color:hsl(301, 63%, 40%)\">done</span><br></span></code></pre><div class=\"buttonGroup_6DOT\"><button type=\"button\" aria-label=\"Sao chép mã nguồn\" title=\"Sao chép\" class=\"clean-btn\"><span class=\"copyButtonIcons_FhaS\" aria-hidden=\"true\"><svg viewBox=\"0 0 24 24\" class=\"copyButtonIcon_phi_\"><path fill=\"currentColor\" d=\"M19,21H8V7H19M19,5H8A2,2 0 0,0 6,7V21A2,2 0 0,0 8,23H19A2,2 0 0,0 21,21V7A2,2 0 0,0 19,5M16,1H4A2,2 0 0,0 2,3V17H4V3H16V1Z\"></path></svg><svg viewBox=\"0 0 24 24\" class=\"copyButtonSuccessIcon_FfTR\"><path fill=\"currentColor\" d=\"M21,7L9,19L3.5,13.5L4.91,12.09L9,16.17L19.59,5.59L21,7Z\"></path></svg></span></button></div></div></div>\n<p>Do vậy ở thời điểm hiện tại, nếu bạn sử dụng loop trong dự án của mình, bạn cần kiểm tra và viết lại bash script để cải thiện hiệu năng nếu cần thiết.</p>\n<h2 class=\"anchor anchorTargetHideOnScrollNavbar_Omnb\" id=\"tính-năng-nổi-bật\">Tính năng nổi bật<a href=\"https://daoanhthanh.vn/blog/amber-rat-tien-dung-cho-cicd#t%C3%ADnh-n%C4%83ng-n%E1%BB%95i-b%E1%BA%ADt\" class=\"hash-link\" aria-label=\"Chuyển đến tiêu đề\" title=\"Chuyển đến tiêu đề\" translate=\"no\">​</a></h2>\n<h3 class=\"anchor anchorTargetHideOnScrollNavbar_Omnb\" id=\"hỗ-trợ-typesafe\">Hỗ trợ typesafe<a href=\"https://daoanhthanh.vn/blog/amber-rat-tien-dung-cho-cicd#h%E1%BB%97-tr%E1%BB%A3-typesafe\" class=\"hash-link\" aria-label=\"Chuyển đến tiêu đề\" title=\"Chuyển đến tiêu đề\" translate=\"no\">​</a></h3>\n<p>Tôi từng viết một script khá dài để deploy microservices lên k8s, dẫn đến việc không kiểm tra type khi gán giá trị cho biến. Tôi đã gán <code>string</code> cho một biến đáng nhẽ là <code>int</code> và thực hiện tính toán, kết quả là chạy sai và tôi đã mất khá nhiều thời gian để debug xem lỗi nằm ở đoạn nào.</p>\n<p>Ở Amber, bạn không thể làm điều đó. Compiler sẽ báo lỗi khi bạn gán sai kiểu vào biến, ví dụ:</p>\n<div class=\"language-javascript codeBlockContainer_APcc theme-code-block\" style=\"--prism-background-color:hsl(230, 1%, 98%);--prism-color:hsl(230, 8%, 24%)\"><div class=\"codeBlockContent_m3Ux\"><pre tabindex=\"0\" class=\"prism-code language-javascript codeBlock_qGQc thin-scrollbar\" style=\"background-color:hsl(230, 1%, 98%);color:hsl(230, 8%, 24%)\"><code class=\"codeBlockLines_p187\"><span class=\"token-line\" style=\"color:hsl(230, 8%, 24%)\"><span class=\"token comment\" style=\"color:hsl(230, 4%, 64%)\">// test.ab</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:hsl(230, 8%, 24%)\"><span class=\"token plain\" style=\"display:inline-block\"></span><br></span><span class=\"token-line\" style=\"color:hsl(230, 8%, 24%)\"><span class=\"token plain\"></span><span class=\"token keyword\" style=\"color:hsl(301, 63%, 40%)\">let</span><span class=\"token plain\"> int </span><span class=\"token operator\" style=\"color:hsl(221, 87%, 60%)\">=</span><span class=\"token plain\"> </span><span class=\"token number\" style=\"color:hsl(35, 99%, 36%)\">5</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:hsl(230, 8%, 24%)\"><span class=\"token plain\"></span><span class=\"token keyword\" style=\"color:hsl(301, 63%, 40%)\">let</span><span class=\"token plain\"> string </span><span class=\"token operator\" style=\"color:hsl(221, 87%, 60%)\">=</span><span class=\"token plain\"> </span><span class=\"token string\" style=\"color:hsl(119, 34%, 47%)\">\"hi\"</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:hsl(230, 8%, 24%)\"><span class=\"token plain\" style=\"display:inline-block\"></span><br></span><span class=\"token-line\" style=\"color:hsl(230, 8%, 24%)\"><span class=\"token plain\"></span><span class=\"token keyword\" style=\"color:hsl(301, 63%, 40%)\">let</span><span class=\"token plain\"> composed </span><span class=\"token operator\" style=\"color:hsl(221, 87%, 60%)\">=</span><span class=\"token plain\"> int </span><span class=\"token operator\" style=\"color:hsl(221, 87%, 60%)\">+</span><span class=\"token plain\"> string</span><br></span><span class=\"token-line\" style=\"color:hsl(230, 8%, 24%)\"><span class=\"token plain\">echo composed</span><br></span></code></pre><div class=\"buttonGroup_6DOT\"><button type=\"button\" aria-label=\"Sao chép mã nguồn\" title=\"Sao chép\" class=\"clean-btn\"><span class=\"copyButtonIcons_FhaS\" aria-hidden=\"true\"><svg viewBox=\"0 0 24 24\" class=\"copyButtonIcon_phi_\"><path fill=\"currentColor\" d=\"M19,21H8V7H19M19,5H8A2,2 0 0,0 6,7V21A2,2 0 0,0 8,23H19A2,2 0 0,0 21,21V7A2,2 0 0,0 19,5M16,1H4A2,2 0 0,0 2,3V17H4V3H16V1Z\"></path></svg><svg viewBox=\"0 0 24 24\" class=\"copyButtonSuccessIcon_FfTR\"><path fill=\"currentColor\" d=\"M21,7L9,19L3.5,13.5L4.91,12.09L9,16.17L19.59,5.59L21,7Z\"></path></svg></span></button></div></div></div>\n<p>Bạn sẽ gặp lỗi ngay thời điểm compile <code>amber test.ab test.sh</code>:</p>\n<figure><p><img decoding=\"async\" loading=\"lazy\" alt=\"amber compile error\" src=\"https://daoanhthanh.vn/assets/images/image1-cf6f31aed15d2c573105cb4b9dad427f.png\" width=\"610\" height=\"218\" class=\"img_IV9f\">\n</p><figcaption>Lỗi xảy ra khi thực hiện cộng một số với string</figcaption><p></p></figure>\n<p>Điều này sẽ tránh cho bạn khỏi phiền phức về sau khi mà code base mở rộng. Với tất cả tính năng của typesafe, script của bạn sẽ dễ đọc và bảo trì hơn.</p>\n<h3 class=\"anchor anchorTargetHideOnScrollNavbar_Omnb\" id=\"handle-exception-dễ-dàng-hơn\">Handle exception dễ dàng hơn<a href=\"https://daoanhthanh.vn/blog/amber-rat-tien-dung-cho-cicd#handle-exception-d%E1%BB%85-d%C3%A0ng-h%C6%A1n\" class=\"hash-link\" aria-label=\"Chuyển đến tiêu đề\" title=\"Chuyển đến tiêu đề\" translate=\"no\">​</a></h3>\n<p>Hãy thử xem xét trường hợp sau, khi thực hiện một hàm bị trả về lỗi sẽ gọi một hàm khác để xử lý:</p>\n<div class=\"language-bash codeBlockContainer_APcc theme-code-block\" style=\"--prism-background-color:hsl(230, 1%, 98%);--prism-color:hsl(230, 8%, 24%)\"><div class=\"codeBlockContent_m3Ux\"><pre tabindex=\"0\" class=\"prism-code language-bash codeBlock_qGQc thin-scrollbar\" style=\"background-color:hsl(230, 1%, 98%);color:hsl(230, 8%, 24%)\"><code class=\"codeBlockLines_p187\"><span class=\"token-line\" style=\"color:hsl(230, 8%, 24%)\"><span class=\"token shebang important\" style=\"color:hsl(230, 8%, 24%);font-weight:bold\">#!/bin/bash</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:hsl(230, 8%, 24%)\"><span class=\"token plain\" style=\"display:inline-block\"></span><br></span><span class=\"token-line\" style=\"color:hsl(230, 8%, 24%)\"><span class=\"token plain\"></span><span class=\"token assign-left variable\" style=\"color:hsl(221, 87%, 60%)\">message</span><span class=\"token operator\" style=\"color:hsl(221, 87%, 60%)\">=</span><span class=\"token string\" style=\"color:hsl(119, 34%, 47%)\">\"</span><span class=\"token string variable\" style=\"color:hsl(221, 87%, 60%)\">$(</span><span class=\"token string variable\" style=\"color:hsl(221, 87%, 60%)\">command_returns_error</span><span class=\"token string variable\" style=\"color:hsl(221, 87%, 60%)\">)</span><span class=\"token string\" style=\"color:hsl(119, 34%, 47%)\">\"</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:hsl(230, 8%, 24%)\"><span class=\"token plain\" style=\"display:inline-block\"></span><br></span><span class=\"token-line\" style=\"color:hsl(230, 8%, 24%)\"><span class=\"token plain\"></span><span class=\"token comment\" style=\"color:hsl(230, 4%, 64%)\"># 'set -e' nếu muốn dừng chương trình</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:hsl(230, 8%, 24%)\"><span class=\"token plain\" style=\"display:inline-block\"></span><br></span><span class=\"token-line\" style=\"color:hsl(230, 8%, 24%)\"><span class=\"token plain\"></span><span class=\"token keyword\" style=\"color:hsl(301, 63%, 40%)\">if</span><span class=\"token plain\"> </span><span class=\"token punctuation\" style=\"color:hsl(119, 34%, 47%)\">[</span><span class=\"token plain\"> </span><span class=\"token variable\" style=\"color:hsl(221, 87%, 60%)\">$?</span><span class=\"token plain\"> </span><span class=\"token parameter variable\" style=\"color:hsl(221, 87%, 60%)\">-ne</span><span class=\"token plain\"> </span><span class=\"token number\" style=\"color:hsl(35, 99%, 36%)\">0</span><span class=\"token plain\"> </span><span class=\"token punctuation\" style=\"color:hsl(119, 34%, 47%)\">]</span><span class=\"token punctuation\" style=\"color:hsl(119, 34%, 47%)\">;</span><span class=\"token plain\"> </span><span class=\"token keyword\" style=\"color:hsl(301, 63%, 40%)\">then</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:hsl(230, 8%, 24%)\"><span class=\"token plain\">    do_another_command</span><br></span><span class=\"token-line\" style=\"color:hsl(230, 8%, 24%)\"><span class=\"token plain\"></span><span class=\"token keyword\" style=\"color:hsl(301, 63%, 40%)\">fi</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:hsl(230, 8%, 24%)\"><span class=\"token plain\" style=\"display:inline-block\"></span><br></span><span class=\"token-line\" style=\"color:hsl(230, 8%, 24%)\"><span class=\"token plain\"></span><span class=\"token comment\" style=\"color:hsl(230, 4%, 64%)\"># ...</span><br></span></code></pre><div class=\"buttonGroup_6DOT\"><button type=\"button\" aria-label=\"Sao chép mã nguồn\" title=\"Sao chép\" class=\"clean-btn\"><span class=\"copyButtonIcons_FhaS\" aria-hidden=\"true\"><svg viewBox=\"0 0 24 24\" class=\"copyButtonIcon_phi_\"><path fill=\"currentColor\" d=\"M19,21H8V7H19M19,5H8A2,2 0 0,0 6,7V21A2,2 0 0,0 8,23H19A2,2 0 0,0 21,21V7A2,2 0 0,0 19,5M16,1H4A2,2 0 0,0 2,3V17H4V3H16V1Z\"></path></svg><svg viewBox=\"0 0 24 24\" class=\"copyButtonSuccessIcon_FfTR\"><path fill=\"currentColor\" d=\"M21,7L9,19L3.5,13.5L4.91,12.09L9,16.17L19.59,5.59L21,7Z\"></path></svg></span></button></div></div></div>\n<p>Thành thực mà nói, cú pháp trên hơi khó để hiểu cho người maintain. Nếu bạn không biết <code>[ $? -ne 0 ]</code> là gì thì thật khó để hiểu được luồng của script. Cùng xem liệu Amber có xử lý tốt hơn không:</p>\n<div class=\"language-typescript codeBlockContainer_APcc theme-code-block\" style=\"--prism-background-color:hsl(230, 1%, 98%);--prism-color:hsl(230, 8%, 24%)\"><div class=\"codeBlockContent_m3Ux\"><pre tabindex=\"0\" class=\"prism-code language-typescript codeBlock_qGQc thin-scrollbar\" style=\"background-color:hsl(230, 1%, 98%);color:hsl(230, 8%, 24%)\"><code class=\"codeBlockLines_p187\"><span class=\"token-line\" style=\"color:hsl(230, 8%, 24%)\"><span class=\"token function\" style=\"color:hsl(221, 87%, 60%)\">command_returns_error</span><span class=\"token punctuation\" style=\"color:hsl(119, 34%, 47%)\">(</span><span class=\"token punctuation\" style=\"color:hsl(119, 34%, 47%)\">)</span><span class=\"token plain\"> failed </span><span class=\"token punctuation\" style=\"color:hsl(119, 34%, 47%)\">{</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:hsl(230, 8%, 24%)\"><span class=\"token plain\">    </span><span class=\"token function\" style=\"color:hsl(221, 87%, 60%)\">do_another_command</span><span class=\"token punctuation\" style=\"color:hsl(119, 34%, 47%)\">(</span><span class=\"token punctuation\" style=\"color:hsl(119, 34%, 47%)\">)</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:hsl(230, 8%, 24%)\"><span class=\"token plain\"></span><span class=\"token punctuation\" style=\"color:hsl(119, 34%, 47%)\">}</span><br></span></code></pre><div class=\"buttonGroup_6DOT\"><button type=\"button\" aria-label=\"Sao chép mã nguồn\" title=\"Sao chép\" class=\"clean-btn\"><span class=\"copyButtonIcons_FhaS\" aria-hidden=\"true\"><svg viewBox=\"0 0 24 24\" class=\"copyButtonIcon_phi_\"><path fill=\"currentColor\" d=\"M19,21H8V7H19M19,5H8A2,2 0 0,0 6,7V21A2,2 0 0,0 8,23H19A2,2 0 0,0 21,21V7A2,2 0 0,0 19,5M16,1H4A2,2 0 0,0 2,3V17H4V3H16V1Z\"></path></svg><svg viewBox=\"0 0 24 24\" class=\"copyButtonSuccessIcon_FfTR\"><path fill=\"currentColor\" d=\"M21,7L9,19L3.5,13.5L4.91,12.09L9,16.17L19.59,5.59L21,7Z\"></path></svg></span></button></div></div></div>\n<p>Gọn và dễ đọc hơn nhiều</p>",
            "url": "https://daoanhthanh.vn/blog/amber-rat-tien-dung-cho-cicd",
            "title": "Giới thiệu Amber - Rất tiện dụng cho CI/CD",
            "summary": "Giới thiệu về Amber - rất tiện dụng cho CI/CD",
            "date_modified": "2024-08-13T00:00:00.000Z",
            "author": {
                "name": "Đào Anh Thành",
                "url": "https://github.com/daoanhthanh"
            },
            "tags": [
                "amber-lang",
                "ci/cd"
            ]
        },
        {
            "id": "https://daoanhthanh.vn/blog/cache-penetration-and-cache-invalidation",
            "content_html": "<p>Tôi yêu cache và các hệ thống lớn cũng vậy. Nó giảm tải workload cho server do đó tăng tốc quá trình xử lý, do vậy thời gian phản hồi sẽ nhanh hơn. Tuy nhiên điều gì cũng có hai mặt và với cache, đó là tính toàn vẹn dữ liệu. Hãy cùng phân tích nó qua hai vấn đề <code>Cache Penetration</code> (xâm nhập bộ đệm) và <code>Cache invalidation</code> (vô hiệu hoá bộ đệm)</p>",
            "url": "https://daoanhthanh.vn/blog/cache-penetration-and-cache-invalidation",
            "title": "Cache penetration và cache invalidation",
            "summary": "Cache penetration và cache invalidation là hai vấn đề mà bạn cần quan tâm khi sử dụng cache trong ứng dụng của mình.",
            "date_modified": "2024-05-20T00:00:00.000Z",
            "author": {
                "name": "Đào Anh Thành",
                "url": "https://github.com/daoanhthanh"
            },
            "tags": [
                "cache",
                "redis"
            ]
        }
    ]
}