Bài viết được dịch từ: https://scotch.io

Bài hướng dẫn này tuy được viết khá lâu (từ cuối năm 2014), nhưng nội dung của nó vẫn khá hữu ích cho những lập trình viên muốn phát triển các ứng dụng lớn dễ dàng mở rộng và bảo trì ở phía client. Nó không chỉ giới hạn trong AngularJS mà còn có thể áp dụng với nhiều framework khác. Như  tác giả đã viết, hy vọng các bạn sẽ tìm ra một vài điều hữu ích cho mình.

Chúng ta dành nhiều thời gian để viết code. Trong giai đoạn đầu của dự án, cấu trúc thư mục không quá quan trọng và mọi người thường có xu hướng bỏ qua các best practice. Trong thời gian ngắn, điều này cho phép lập trình viên viết code nhanh chóng, nhưng về lâu dài sẽ ảnh hưởng tới việc  bảo trì code. AngularJS vẫn còn tương đối mới và các lập trình viên vẫn đang tìm hiểu cái gì làm việc và cái gì không (Bài này được viết vào cuối năm 2014). Có nhiều cách hay để cấu trúc một ứng dụng và chúng ta sẽ làm theo một vài qui tắc từ các framework trưởng thành có sẵn nhưng cũng thay đổi một số thứ cho phù hợp với Angular.

Trong bài viết này, tôi sẽ đề cập tới các best practice liên quan tới cấu trúc thư mục cho cả ứng dụng AngularJS lớn và nhỏ. Đây có thể là một vấn đề "hot" với một số lập trình viên đang chưa tìm ra một cách "hoàn hảo" để cấu trúc một ứng dụng, tôi sẽ chia sẻ những kinh nghiệm và bài học đã rút ra được từ những dự án mà tôi đã làm việc.

Cấu trúc chuẩn

Đầu tiên, hãy xem cái gì không lên làm. Nhiều bài hướng dẫn về AngularJS có cấu trúc gần như dưới đây:

app/
----- controllers/
---------- mainController.js
---------- otherController.js
----- directives/
---------- mainDirective.js
---------- otherDirective.js
----- services/
---------- userService.js
---------- itemService.js
----- js/
---------- bootstrap.js
---------- jquery.js
----- app.js
views/
----- mainView.html
----- otherView.html
----- index.html

Đây là cấu trúc ứng dụng mà tôi thường thấy. Nhìn qua, nó rất dễ hiểu và rất giống nhiều framework MVC. Chúng ta chia các controller, view, các thư viện mở rộng, ... vào các thư mục riêng.

Vấn đề chính với cấu trúc thư mục này là nó không rõ ràng khi bạn đang làm việc chỉ với một số ít các view và controller. Trong thực tế, hướng tiếp cận này chỉ phù hợp với các bài hướng dẫn mang tính ví dụ hoặc cho các ứng dụng nhỏ. Cấu trúc này rất dễ cho người đọc hình dung và hiểu các khái niệm bạn đang đề cập.

Cách tiếp cận này sẽ không phù hợp, khi  bạn bắt đầu bổ sung thêm chức năng cho ứng dụng. Khi bạn có hơn 10 controller, view và directive, bạn sẽ phải cuộn chuột rất nhiều trong cây thư mục để tìm các file yêu cầu.

Ví dụ, bạn đang xây một blog với Angular. Bạn quyết định rằng bạn sẽ thêm thông tin tác giả dưới mỗi bài viết. Bây giờ, bạn cần tìm directive, controller, các service liên quan và cuối cùng là view trước khi bạn có thể nhìn toàn bộ bức tranh và bắt đầu chỉnh sửa.

Một vài tháng trôi qua, bạn thêm các tính năng bổ sung tới blog và muốn đổi tên một tính năng, lại một lần nữa bạn phải duyệt qua cấu trúc thư mục để tìm các file bị ảnh hưởng, chỉnh sửa chúng, đảm bảo rằng tất cả chúng đồng bộ và sau đó tạo ra các thay đổi.

Một cấu trúc tốt hơn

Hãy xem các best practice và cái bạn nên làm để xây dựng các ứng dụng AngularJS có thể mở rộng và bảo trì cái mà đồng nghiệp của bạn sẽ yêu bạn. Một cấu trúc ứng dụng AngularJS lý tưởng lên được module hóa thành các chức năng thật cụ thể. Chúng ta cũng lên tận dụng lợi thế của các directive AngularJS để chia nhỏ ứng dụng của mình. Hãy xem cấu trúc thư mục dưới đây:

app/
----- shared/   // acts as reusable components or partials of our site
---------- sidebar/
--------------- sidebarDirective.js
--------------- sidebarView.html
---------- article/
--------------- articleDirective.js
--------------- articleView.html
----- components/   // each component is treated as a mini Angular app
---------- home/
--------------- homeController.js
--------------- homeService.js
--------------- homeView.html
---------- blog/
--------------- blogController.js
--------------- blogService.js
--------------- blogView.html
----- app.module.js
----- app.routes.js
assets/
----- img/      // Images and icons for your app
----- css/      // All styles and style related files (SCSS or LESS files)
----- js/       // JavaScript files written for your app that are not for angular
----- libs/     // Third-party libraries such as jQuery, Moment, Underscore, etc.
index.html

Cấu trúc thư mục này khó đọc và khó hiểu hơn so với cấu trúc trước đó. Một lập trình viên mới làm quen với Angular có thể cảm thấy hoàn toàn bế tắc bởi hướng tiếp cận phức tạp này, và đó là lý do tại sao các bài hướng dẫn và ví dụ trong Angular sử dụng cấu trúc thư mục đơn giản hơn như ở phần trước. Hãy đi sâu vào cấu trúc thư mục ở trên và xem xét cụ thể từng phần một.

Index.html

Index.html ở trong thư mục gốc. Index.html là file chính nạp tất cả các thư viện và phần tử Angular.

Thư mục assets

Thư mục assets là một chuẩn. Nó sẽ chứa tất cả các tài sản cần thiết cho ứng dụng của bạn cái không liên quan tới code AngularJS. Có nhiều cách để tổ chức thư mục này nhưng chúng nằm ngoài phạm vi của bài viết này. Ví dụ phía trên là đủ tốt cho hầu hết các ứng dụng.

Thư mục app

Đây là nơi chứa code ứng dụng AngularJS của bạn. Chúng ta có 2 thư mục con ở đây và một vài file tại thư mục gốc (/app). File app.module.js sẽ chứa các thiết lập cho app của bạn, nạp các dependencies ... File app.route.js sẽ chứa tất cả các route và cấu hình route. Cuối cùng là 2 thư mục - components và shared. Hãy xem xét chúng kỹ hơn.

Thư mục components

Thư mục components sẽ chứa các thành phần thật sự cho ứng dụng Angular của bạn. Đó là view, các directive và các service cho các phần cụ thể của site (ví dụ là phần admin, phần tạo gallery, ...). Mỗi trang lên có thư mục con với controller, các service và các file HTML của riêng mình.

Mỗi thành phần ở đây sẽ giống với một ứng dụng MVC thu nhỏ với view, controller, và các service. Nếu thành phần có nhiều view liên quan, một cách hay là chia các file này vào các thư mục con 'views', 'controllers', 'services'.

Điều này có thể giống như cấu trúc thư mục đơn giản hơn đã thấy trong phần trước của bài viết này, chỉ cần chia nhỏ thành nhiều phần. Vì thế bạn có thể hình dung bản chất là nhiều ứng dụng Angular nhỏ bên trong một ứng dụng lớn.

Thư mục shared

Thư mục shared sẽ chứa các tính năng riêng rẽ cái ứng dụng của bạn sẽ có. Các tính năng này là các directive cái bạn sẽ muốn tái sử dụng trên nhiều trang.

Các tính năng chẳng hạn như các bài viết, các bình luận của người dùng, slider và những thứ khác có thể viết thành các directive. Mỗi thành phần ở đây nên có các thư mục của riêng mình, chứa file directive JavaScript và file template HTML.

Trong một vài trường hợp, một directive có thể có các file JavaScript service của riêng mình, và trong trường hợp này cũng nên đặt chúng vào thư mục con này.

Điều này cho phép chúng ta định nghĩa các thành phần cho site của mình giống như slider sẽ được sử dụng ở nhiều trang. Bạn sẽ thường muốn sử dụng các directive vì rằng bạn có thể truyền các tùy chọn để mở rộng nó. Ví dụ:

<!-- user a slider directive to loop over something -->
<slider id="article-slider" ng-repeat="picture in pictures" size="large" type="square">
</slider>

Bây giờ slider này có thể truy cập từ mọi nơi trên site của chúng ta mà không cần phải viết lại. Chúng ta chỉ cần thay đổi một nơi, thư mục shared và nó sẽ cập nhật trên toàn bộ site.

Best Practice (Cho ứng dụng cực lớn)

Nếu bạn đang phát triển một ứng dụng AngularJS thực sự lớn, bạn sẽ muốn module hóa hơn nữa ứng dụng của mình. Đây là một vài cách mà bạn có thể áp dụng.

Module hóa header và footer

Một cách hay là tạo ra một thư mục core trong thư mục components, và sau đó là một thư mục con cho HeaderFooter và mọi thành phần bổ sung khác cái sẽ chia sẻ trên nhiều trang.

Module hóa các route

Trong cấu trúc phía trên chúng ta không làm điều này, nhưng một cách tốt cho mọi ứng dụng lớn là chia các route thành các file riêng biệt. Ví dụ bạn có thể thêm một file blogRoutes.js trong thư mục /views/blog/ và chỉ bao gồm các route liên quan tới blog chẳng hạn như /blog/:slug, /blog/:slug/edit, /blog/tags/:tag, ...

Đừng quên minify

Nếu bạn quyết định xây dựng ứng dụng AngulaJS của mình theo cách module hóa, hãy chắc chắn nối và minify code của bạn trước khi phát hành bản production. Có nhiều công cụ chẳng hạn như Grunt và Gulp sẽ giúp bạn làm điều này - vì thế đừng ngại chia nhỏ code đến mức bạn cần.

Bạn có thể không muốn chỉ có một file .js khổng lồ cho toàn bộ ứng dụng của mình, mà có thể nối các file ứng dụng của bạn thành một vài file như sau:

  • app.js (dành cho khởi tạo, cấu hình và điều hướng ứng dụng)
  • services.js (dành cho tất cả các dịch vụ)

Điều này có lợi ích là giảm thời gian khởi tạo ứng dụng của bạn.

Nếu bạn cần một vài cách để minify, bạn có thể tham khảo bài hướng dẫn này: Khai báo các Module AngularJS để minify

Đặt tên một cách thống nhất

Đây chỉ là một mẹo thông thường, nhưng giúp bạn không bị đau đầu trong tương lai, khi viết các thành phần (components) và bạn có nhiều file trong một thành phần (component), cố gắng đặt tên chúng theo một mẫu thống nhất. Ví dụ: blogView.html, blogServices.js, blogController.js.

Lợi ích của cách tiếp cận module hóa

Ví dụ ở phía trên cho thấy một hướng tiếp cận module hóa để xây dựng ứng dụng AngularJS. Những lợi ích từ hướng tiếp cận này bao gồm:

Dễ bảo trì code

Hướng tiếp cận ở trên sẽ chia ứng dụng của bạn thành các khối logic và bạn sẽ dễ dàng có thể xác định và chỉnh sửa code.

Dễ mở rộng

Code của bạn sẽ dễ dàng mở rộng hơn. Thêm các directive và các trang sẽ không làm phình to các thư mục có sẵn. Các lập trình viên mới cũng sẽ dễ dàng tham gia hơn khi cấu trúc được giải thích rõ ràng. Ngoài ra, với hướng tiếp cận này, bạn có thể thêm vào hoặc loại ra các tính năng trong ứng dụng của mình một cách dễ dàng để kiểm thử các chức năng mới hoặc gỡ bỏ. 

Debug

Debug code của bạn sẽ dễ dàng hơn với hướng tiếp cận module hóa. Nó sẽ dễ dàng hơn để tìm ra các phần code bị lỗi và sửa chúng.

Kiểm thử

Viết các script và kiểm thử ứng dụng sẽ dễ dàng hơn nhiều so với hướng tiếp cận không module hóa.

Kết luận

Bài viết này đề cập tới một và best practice liên quan tới cấu trúc một ứng dụng AngularJS. Rất dễ từ chối các best practice để tiếp kiện thời gian lúc đầu. Tất cả chúng ta có xu hướng chỉ muốn bắt đầu viết code. Thỉnh thoảng niềm đam mê này có thể làm đau chúng ta, sau một thời gian dài khi ứng dụng của chúng ta phát triển và trở lên phổ biến và sau đó chúng ta cần viết lại hoặc thậm chí là bảo trì những đoạn code tồi. Tôi hy vọng bài viết này sẽ đem lại một vài thứ hữu ích cho bạn.