Chào bạn, có bao giờ bạn "đau đầu" khi muốn tách bạch rạch ròi các "món" trong ứng dụng như ghi log, thao tác database hay logic nghiệp vụ chưa? Dù bạn có thể "chế biến" một kiến trúc đơn khối (monolithic) truyền thống để làm được một phần, nhưng tin tôi đi, có một cách "cao cấp" hơn nhiều! Nếu tôi nói với bạn rằng có một phong cách kiến trúc được thiết kế để phân tách các "mối quan tâm" này một cách sạch sẽ và hiệu quả thì sao? Vâng, đó chính là Clean Architecture hay còn gọi là "Kiến trúc Sạch"! Hãy cùng tôi khám phá xem tại sao nó lại "chất" đến vậy nhé!Clean Architecture về cơ bản là một kiến trúc đa tầng, thực thi sự phân tách rạch ròi các "mối quan tâm" thông qua các ranh giới cực kỳ nghiêm ngặt. Mỗi tầng có một vai trò và hướng phụ thuộc rõ ràng. Thường thì, chúng ta có bốn tầng chính trong Clean Architecture:<img src='https://truyentranh.letranglan.top/api/v1/proxy?url=https://dev-to-uploads.s3.amazonaws.com/uploads/articles/sj5uwjpxd750pi719pk9.png' alt='Mô hình Clean Architecture với các lớp'>1. Tầng Domain (Vùng lõi/Trái tim):Đây là tầng cốt lõi và nằm trong cùng của kiến trúc. Nó chẳng thèm phụ thuộc vào bất kỳ tầng nào khác đâu nhé! Tầng này chứa đựng "mô hình kinh doanh thuần túy" của ứng dụng bạn, tức là những quy tắc, đối tượng "thuần khiết" nhất của nghiệp vụ. Bạn sẽ không tìm thấy bất kỳ gói NuGet hay thư viện "lung tung" nào ở đây đâu. Nó thường bao gồm: Enum (liệt kê), Model (mô hình dữ liệu), Constant (hằng số) và Value Object (đối tượng giá trị).2. Tầng Application (Điều phối viên):Tầng Application chỉ phụ thuộc vào tầng Domain mà thôi. Nó chứa đựng các "trường hợp sử dụng" (use case) và logic nghiệp vụ, có nhiệm vụ "điều phối" các thao tác bằng cách sử dụng các thực thể (entity) từ tầng Domain. Ở đây, bạn sẽ định nghĩa: Các dịch vụ ứng dụng (chính là các use case), các giao diện (interface) cho kho lưu trữ (repository) hoặc các dịch vụ bên ngoài, và các quy tắc/ràng buộc về kiểm định dữ liệu (validation rules). Ví dụ, logic về cách tạo một yêu cầu mới, hoặc khi nào một quy trình làm việc nên được kích hoạt, đều nằm gọn trong tầng này.3. Tầng Infrastructure (Hậu cần/Dịch vụ tiện ích):Tầng này cung cấp các "hiện thực" cụ thể cho các giao diện đã được định nghĩa ở tầng Application. Nó phụ thuộc vào tầng Application. Nhưng vì Application lại phụ thuộc vào Domain, nên tầng Infrastructure cũng gián tiếp phụ thuộc vào tầng Domain. Các thành phần điển hình ở đây là: Ngữ cảnh cơ sở dữ liệu (ví dụ: AppDbContext trong EF Core), các hiện thực của Repository (cách lưu trữ và truy xuất dữ liệu), các dịch vụ bên ngoài (như gửi email, lưu trữ file), và các cấu hình hay di chuyển dữ liệu (data migrations).4. Tầng Presentation (Mặt tiền/Giao diện):Đây là tầng ngoài cùng của hệ thống. Nó cung cấp giao diện người dùng (UI) hoặc API để các ứng dụng bên ngoài tương tác. Tầng này phụ thuộc vào cả tầng Application và Infrastructure. Nó thường chứa: Các điểm cuối API (ví dụ: Minimal APIs hoặc Controllers trong ASP.NET Core), các "middleware" (những đoạn mã chạy xen kẽ để xử lý yêu cầu, ví dụ: xử lý ngoại lệ toàn cục), và logic xác thực/phân quyền (Authentication & Authorization).Câu hỏi lớn: Tại sao lại chọn Clean Architecture?Hồi xưa, tôi cũng từng băn khoăn liệu Clean Architecture có "hợp cạ" với dự án của chúng tôi không — cụ thể là hiện đại hóa một loạt các ứng dụng "nhà làm". Công ty tôi đang làm có hơn 20 ứng dụng nội bộ, mỗi cái được xây dựng qua thời gian với các framework và phong cách kiến trúc khác nhau. Một số dùng Angular, số khác dùng .NET, và vài cái thì dùng React. Khi hệ thống lớn dần, việc duy trì chuyên môn trên nhiều "ngăn tủ" công nghệ như vậy trở nên cực kỳ khó khăn và kém hiệu quả. Chúng tôi không muốn phải "săn lùng" các lập trình viên có kỹ năng đặc thù cho từng ứng dụng nữa.Thế là, chúng tôi đưa ra một quyết định chiến lược: chuẩn hóa tất cả các ứng dụng mới và đã hiện đại hóa bằng một "ngăn tủ" công nghệ duy nhất — .NET kết hợp với Clean Architecture. Quyết định này mang lại khả năng mở rộng tốt hơn, phân tách nghiệp vụ rõ ràng, và quan trọng nhất là sự **nhất quán** trên tất cả các ứng dụng.Ban đầu, cách tiếp cận này nghe có vẻ "over-engineering" (quá phức tạp hóa) — đặc biệt là với các ứng dụng nhỏ hơn của chúng tôi. Clean Architecture giới thiệu nhiều tầng và cấu trúc hơn, điều này có thể gây cảm giác "nặng nề" khi ứng dụng của bạn chỉ có vài tính năng hoặc lượng người dùng hạn chế (chúng tôi có khoảng 2.000 người dùng). Nhưng nói thật nhé, kiến trúc không chỉ xoay quanh số lượng người dùng đâu — nó còn là về **mức độ phức tạp**.Ngay cả khi một ứng dụng khởi đầu nhỏ bé, logic nghiệp vụ vẫn có thể phát triển "chóng mặt". Với các cấu trúc đơn khối, bạn có nguy cơ "đổ dồn" mọi thứ vào một "tầng nghiệp vụ" khổng lồ theo thời gian. Clean Architecture giúp bạn tránh điều đó bằng cách "cắm cọc" ranh giới ngay từ đầu.Đúng, ban đầu nó có vẻ phức tạp hơn một chút. Các lập trình viên mới có thể cần thời gian để làm quen với cấu trúc. Nhưng một khi họ đã "nhập tâm", họ sẽ cực kỳ trân trọng cách nó giúp việc điều hướng, bảo trì và mở rộng codebase trở nên dễ dàng đến không ngờ — từng tầng một, từng tính năng một.Câu hỏi: Làm thế nào để triển khai?Vậy, làm thế nào để thực sự xây dựng một giải pháp Clean Architecture đây? Điều quan trọng nhất đối với chúng tôi, khi hiện đại hóa hơn 20 ứng dụng nội bộ, là phải thiết lập một **boilerplate** (một bộ khung mẫu) chung — một mẫu có thể tái sử dụng, đóng vai trò là điểm khởi đầu cho mọi ứng dụng chúng tôi sẽ xây dựng sau này.Dưới đây là cái nhìn tổng quan về cấu trúc đó:<img src='https://truyentranh.letranglan.top/api/v1/proxy?url=https://dev-to-uploads.s3.amazonaws.com/uploads/articles/pqmth4j5d9g8coful1cf.png' alt='Cấu trúc mẫu tổng thể của giải pháp Clean Architecture'>Trong bài viết này, tôi sẽ giải thích cấu trúc một cách khái quát. Trong các bài viết sau, tôi sẽ đào sâu vào từng thành phần cụ thể hơn. Bây giờ, hãy bắt đầu bằng cách tạo một solution trống trong VS 2022, sau đó làm theo các bước sau:Các tệp cấu hình cho toàn bộ Solution:Những tệp này áp dụng cài đặt cho toàn bộ solution, chứ không phải chỉ riêng từng project đâu nhé!editorconfig: Chứa các quy tắc định dạng mã. Ví dụ, trong Visual Studio 2022, tệp này có thể buộc các quy ước đặt tên (ví dụ: biến readonly phải bắt đầu bằng _, biến static phải bắt đầu bằng s_). Bạn tạo nó bằng cách: Add → New Item → Editor Config (.NET).Directory.Build.props: Lưu trữ các siêu dữ liệu chung cho tất cả các project (ví dụ: tên, mô tả, URL kho Git). Bạn tạo nó bằng cách: Add → New Item → XML File. Nó sẽ chứa các cài đặt như Title, Authors, Description, TargetFramework, Nullable, ImplicitUsings, v.v., áp dụng đồng bộ cho các project con.Directory.Packages.props: Tập trung các tham chiếu gói NuGet trên toàn solution, giúp bạn tránh tình trạng "thừa thãi" và dễ dàng quản lý phiên bản. Bạn tạo nó bằng cách: Add → New Item → XML File. Nó sẽ khai báo các phiên bản gói NuGet dùng chung như Carter, Microsoft.AspNetCore.OpenApi, Microsoft.EntityFrameworkCore.Design, v.v., đảm bảo tất cả project đều dùng chung một phiên bản.src/Backend: Hậu cần theo Clean ArchitectureThư mục này chứa giải pháp backend dựa trên Clean Architecture.ISWebAppTemplate.Api: <img src='https://truyentranh.letranglan.top/api/v1/proxy?url=https://dev-to-uploads.s3.amazonaws.com/uploads/articles/9zwqouyc8l79hj1ubcwf.png' alt='Cấu trúc tầng Presentation trong template'> Đây là tầng Presentation (tầng ngoài cùng). Một project ASP.NET Core Web API.ISWebAppTemplate.Application: <img src='https://truyentranh.letranglan.top/api/v1/proxy?url=https://dev-to-uploads.s3.amazonaws.com/uploads/articles/65qiidb6nowpabds5hj3.png' alt='Cấu trúc tầng Application trong template'> Tầng Application. Một thư viện class tập trung vào logic nghiệp vụ.ISWebAppTemplate.Infrastructure: <img src='https://truyentranh.letranglan.top/api/v1/proxy?url=https://dev-to-uploads.s3.amazonaws.com/uploads/articles/rxkob73gsbeeiyo3ziry.png' alt='Cấu trúc tầng Infrastructure trong template'> Tầng Infrastructure. Một thư viện class tập trung vào xử lý tương tác cơ sở dữ liệu và các dịch vụ bên ngoài.ISWebAppTemplate.Domain: <img src='https://truyentranh.letranglan.top/api/v1/proxy?url=https://dev-to-uploads.s3.amazonaws.com/uploads/articles/5vyw4w7iz6uwldm7lfu2.png' alt='Cấu trúc tầng Domain trong template'> Tầng Domain (tầng cốt lõi nhất). Chứa các thực thể domain, đối tượng giá trị và các quy tắc cốt lõi.src/Frontend: Giao diện Web BlazorThư mục này chứa ứng dụng frontend. Tôi đã tạo Blazor Web App và chọn Blazor server làm renderer.ISWebAppTemplate.WebUI: <img src='https://truyentranh.letranglan.top/api/v1/proxy?url=https://dev-to-uploads.s3.amazonaws.com/uploads/articles/w0k02k5mh5t9c3tb3dbx.png' alt='Cấu trúc UI trong template'> Một ứng dụng Blazor Server với:Radzen cho các thành phần UI.Xác thực Azure Entra MFA.Tạo Correlation ID để theo dõi các yêu cầu qua các API downstream — cực kỳ hữu ích khi gỡ lỗi và phân tích log./tests: Kiểm thử Unit và Tích hợpChúng tôi tách biệt các bài kiểm thử thành hai tầng rõ ràng:ISWebAppTemplate.Integration.Tests: <img src='https://truyentranh.letranglan.top/api/v1/proxy?url=https://dev-to-uploads.s3.amazonaws.com/uploads/articles/1v76tuh1zcfpbx7bt6q3.png' alt='Cấu trúc kiểm thử tích hợp trong template'>Trọng tâm: Kiểm thử toàn bộ giao dịch và hành vi.Ví dụ: Nếu người yêu cầu thuộc Band X, không cần phê duyệt; ngược lại, phải kích hoạt phê duyệt của quản trị viên. Các bài kiểm thử tích hợp sẽ xác minh logic nghiệp vụ này một cách toàn diện.ISWebAppTemplate.Unit.Tests: <img src='https://truyentranh.letranglan.top/api/v1/proxy?url=https://dev-to-uploads.s3.amazonaws.com/uploads/articles/05kx2dswdshd82dxomrq.png' alt='Cấu trúc kiểm thử Unit trong template'>Trọng tâm: Kiểm định và logic ở cấp độ thực thể.Ví dụ: Tiêu đề yêu cầu phải dài ít nhất 50 ký tự.Sự khác biệt chính: Kiểm thử Unit (Unit tests) xác thực các thành phần cô lập như một trường hoặc một quy tắc. Kiểm thử Tích hợp (Integration tests) xác thực hành vi từ đầu đến cuối và cách áp dụng quy tắc.Kết luận:Tôi biết là bài này có hơi "nặng đô" một chút — đặc biệt là tôi mới chỉ "cào" được bề mặt thôi. Nhưng đừng lo, tôi sẽ đi sâu hơn nữa trong các bài viết sắp tới. Mỗi kỹ thuật và mẫu thiết kế được sử dụng trong dự án này sẽ được tôi "mổ xẻ" riêng biệt để bạn có cái nhìn rõ ràng và thực tế hơn về cách chúng hoạt động trong các kịch bản thực tế.Vậy, tại sao lại chọn Clean Architecture thay vì một kiến trúc đơn khối truyền thống? Tất cả đều quy về việc **phân tách nghiệp vụ**, **khả năng mở rộng** và **khả năng bảo trì lâu dài**. Clean Architecture đã mang lại cho chúng tôi một cấu trúc giúp quản lý logic nghiệp vụ phức tạp trên hơn 20 ứng dụng — điều mà một cấu trúc đơn khối thông thường sẽ trở nên "bất trị".Lưu ý nhỏ: Chúng tôi không sử dụng Docker hay container hóa trong thiết lập này. Vì các ứng dụng của chúng tôi phục vụ khoảng 2.000 người dùng và được lưu trữ trên một máy chủ ổn định tại chỗ (on-premise server), nhu cầu hiệu suất được đáp ứng dễ dàng. Lý do chính để áp dụng Clean Architecture là để quản lý logic nghiệp vụ phức tạp tốt hơn, chứ không phải để đáp ứng quy mô hay tính linh hoạt triển khai.Hẹn gặp lại bạn ở bài viết tiếp theo: "Tạo và Phát hành Custom .NET Project Template và Private NuGet lên GitLab Package Registry"!
Hướng dẫn chi tiết cách tạo ứng dụng sơ đồ tư duy AI-powered bằng cách kết hợp OpenAI và thư viện Blazor Diagram của Syncfusion. Biến ý tưởng thành sơ đồ trực quan tự động từ văn bản.
Khám phá Server-Side Rendering (SSR) tĩnh trong Blazor .NET 9 giúp website tải nhanh "thần tốc" và tối ưu SEO hiệu quả. Tìm hiểu lợi ích và cách triển khai để nâng tầm ứng dụng Blazor của bạn.