thảo luận How to build a [good] Project/Code Structure (long post)

  • Người tạo chủ đề Người tạo chủ đề _Worker_of_MUFC_
  • Ngày bắt đầu Ngày bắt đầu
Mở đầu
Lời đầu tiên mình xin được chia sẻ là mình không có ý định "dạy đời" bất kì ai cả. Tất cả những điều mình đưa ra đều mang tính cá nhân của mình. Mọi gạch đá đều ok hết.
Có 1 thread khá hay về Project/Code Structure (link), tuy có hơi lan man, nhưng mình thấy đây là một vấn đề hay rất đáng để học hỏi. Do vậy nên mình sẽ chia sẻ kinh nghiệm của bản thân về vấn đề này. (Ban đầu mình định post lên Medium kiếm ít tiền, nhưng nghĩ đi nghĩ lại, ném vào đây có nhiều cao nhân ẩn dật hơn).
Các ví dụ mình đưa ra chủ yếu là dựa trên các Java web framework (Spring, Struts,...), tuy nhiên các bạn có thể map sang các framework của các ngôn ngữ khác một cách tương đương được. Về mặt bản chất thì ý tưởng không thay đổi.

Now, let's go

Level 1: Mô hình truyền thống
Mô hình web này chia cấu trúc project/code ra làm 3 phần riêng biệt: Controller, Service, DAO.
Cấu trúc code có thể chia ra như sauL

[name.of.company].form (entity model for UI)
|_____function_A_Form
|_____function_B_Form
|_____...
[name.of.company].formValidator
|_____function_A_FormValidator
|_____function_B_FormValidator
|_____...
[name.of.company].controller
|_____function_A_Controller
|_____function_B_Controller
|_____...
[name.of.company].service
|_____function_A_Service (Là 1 interface)
|_____function_A_ServiceImpl (Là implement của function_A_Service)
|_____function_B_Service (Là 1 interface)
|_____function_B_ServiceImpl (Là implement của function_B_Service)
|_____...
[name.of.company].dao (persistence layer)
|_____function_A_DAO (Là 1 interface)
|_____function_A_DAOImpl (Là implement của function_A_DAO)
|_____function_B_DAO (Là 1 interface)
|_____function_B_DAOImpl (Là implement của function_B_DAO)
|_____...
[name.of.company].entity (entity model for persistence layer)
|_____table_X_Object
|_____table_Y_Object
|_____...

Đây là mô hình tương đối phổ biến. Chỉ cần google example của Spring MVC là sẽ thấy hầu hết các example đều làm theo mô hình này. Việc phân chia code theo cách này tương đối rõ ràng, cấu trúc code cũng hợp lý, dễ quản lý. Nhưng:
---Nhược điểm chí mạng của mô hình này chính là sự chồng chéo nhau giữa các functions. Về mặt ý tưởng thì chúng ta luôn muốn chia các functions một cách độc lập nhất có thể. Nhưng trên thực tế, với các business logic phức tạp luôn có sự overlap lẫn nhau giữa các function --> Điều này sẽ làm cho việc maintain khi project phình to cực kì painful.
Ngoài ra còn có một số nhược điểm khác mà mình sẽ nói ở phần sau.

Level 1+: Mô hình truyền thống cải tiến
Có một điểm mà mình thấy thực sự không cần thiết ở mô hình level 1 chính là sự xuất hiện của các Interfaces. Về mặt idea thì chắc chắn ai cũng hiểu mục đích của việc tạo ra Interfaces để làm gì. Nhưng trên thực tế, mình thấy 99% các trường hợp chẳng cần dùng tới bọn Interfaces này làm gì
--> Mô hình cải tiến sẽ loại bỏ các redundant code.

Level 2: Mô hình của mình
Mô hình này sẽ giải quyết vấn đề chồng chéo code ở mô hình level 1.
Trước giờ hầu hết các projects đều chia nhỏ dưới dạng functions/module, cái này là đúng nhưng chưa đủ. Idea của mình đơn giản chỉ là cải tiến hơn. Mình sẽ chia ra đơn vị nhỏ nhất là UI Screen. Do các Screen là độc lập với nhau nên mình sẽ đảm bảo được tính độc lập giữa các modules.
Thêm một phần nữa, trong mô hình truyền thống, project/code structure được chia theo chức năng --> Việc traverse giữa một đống packages như thế thực sự không vui một chút nào.
Và đây là project/code structure của một số dự án mình áp dụng

[name.of.company].screen.screenA
|_____screen_A_Form
|_____screen_A_FormValidator
|_____screen_A_Controller
|_____screen_A_Service
|_____screen_A_DAO
[name.of.company].screen.screenB
|_____screen_B_Form
|_____screen_B_FormValidator
|_____screen_B_Controller
|_____screen_B_Service
|_____screen_B_DAO
....
[name.of.company].entity (entity model for persistence layer)
|_____table_X_Object
|_____table_Y_Object
|_____...

Việc focus vào một chỗ nhỏ trong project sẽ tốt hơn rất nhiều việc phân chia code chỗ này một tí, chỗ kia một tí (Bác nào dùng vim/nvim chắc chắn sẽ đồng ý với mình về vụ này). Việc manage source control cũng dễ thở hơn nhiều. Các modules (ở đây là screens) chỉ có "dính" với nhau duy nhất ở phần Controller (thì cũng phải có cái gì liên kết chứ)
Nhưng cái gì cũng có mặt không tốt của nó. Mô hình của mình cũng vậy. Nhược điểm của mô hình này của mình chính là việc duplicate code diễn ra tương đối nhiều.

Ví dụ: cùng là 1 câu query SQL, nhưng có thể xuất hiện cả ở screen A lẫn screen B (Để đảm bảo tính độc lập giữa các screens với nhau, logic sẽ được tách riêng là điều đương nhiên)

Trên quan điểm của mình, việc duplicate code này không ảnh hưởng gì tới software quality cả. Và trên thực tế, mình phản đối việc re-use các code có liên quan tới business logic. Bản chất mỗi business đều độc lập với nhau, việc trùng nhau là chuyện bình thường. Khi tách riêng ra, việc maintain sẽ cực kì dễ thở vì không lo ảnh hưởng tới các module khác.

Level 2+: Mô hình cải tiến
Mô hình này được phát triển khi mình gặp một dự án quái thai. KH muốn chuyển đổi từ .NET MVC sang Java, cụ thể là Struts 2. Dự án đang đi được nửa đường, KH đổi ý, ko muốn sang Struts nữa mà sang Spring MVC. Việc chuyển đổi logic từ cũ -> mới -> mới hơn thực sự là đau đầu. Và ý tưởng về việc tập trung lại business logic tại một chỗ ra đời.
Ở mô hình level 2, các bạn có thể thấy tuy tương đối ok, nhưng business logic đang bị phân mảnh tại 2 chỗ: FormValidator và DAO.
FormValidator sẽ thực hiện các heavy validation (việc check từng field xem null/empty,... không tính tiền), nó cũng là một business logic tương đối quan trọng.
DAO: Nghe thì đơn giản chỉ là các câu query, nhưng mỗi cái đều có cái lý của nó. Câu chuyện về DAO thì bản thân mình trải nghiệm kha khá.
Thời kì đầu (trước những năm 2000): hầu hết các cty tập trung business logic ở các câu query (có câu query dài tới 4 trang A4 nếu in ra)--> Do giới hạn về design.
Thời kì sau (trước năm 2015): business logic được move dần sang phần code đễ dễ maintain hơn, tăng tính readablity --> Hầu như chúng ta sẽ gặp các trường hợp này. Phần PL chỉ còn nhiệm vụ CRUD bình thường, rất ít các query phức tạp.
Thời kì hiện đại: Việc xử lý data ở DB bao giờ cũng nhanh hơn so với xử lý trong code. Thế nên các app cần tính toán nhanh thì lại quay về thời kì đầu là chuyển dần các business logic vào trong DB nên các câu query sẽ dần dần phức tạp hơn tương đối nhiều.

Idea của mình cũng không phải mới, mọi người chắc cũng biết. Đó là mình move các phần logic FormValidator vào trong Service (cái này thì ez). Và cái quan trọng nhất là mình move toàn bộ các query từ DAO vào Service. Để làm việc này, mình đã phải tự phát triển một app gần tương tự như jOOQ (thậm chí là ngon hơn). Việc tập trung business logic code vào một chỗ như thế này có vài điểm lợi như sau:
  • Loại bỏ được kha khá các entity/model không cần thiết kiểu POJO như: BO, DO, VO,....(mấy thằng này gần như same same nhau, nhưng vì yêu cầu mà đẻ ra lắm thứ)
  • Tập trung business logic code lại một chỗ thì việc focus, debug dễ hơn nhiều.
  • Bọn devs bớt được kha khá công việc Unit Test (thứ mình cho là bullsh!t và nonsense), UT cũng có ý nghĩa hơn.
Nhược điểm:
  • Gặp dev không quen thì sẽ bị choáng bởi cách phân chia không như truyền thống.
  • Khi có bugs thì đúng là hơi mệt khi debug vì phải trace từ đầu tới cuối (cần dev vừa có exp, vừa phải hiểu business logic cái đang làm)

Level 3: Simplicity is the ultimate sophistication (Đơn giản là đỉnh cao của sự tinh tế)
Trước kia web app thường là server rendering với jsp, thymeleaf,...Công việc làm web sẽ focus tương đối vào cách transit pages và các logic kèm theo. Nhưng từ khi bọn React, Angular,...nó thống trị làng web thì cơ chế phía back-end cũng phải thay đổi theo. Giờ bọn back-end gần như chỉ tương đương lũ repo, cung cấp các RESTful APIs cho lũ front-end làm việc. Nếu áp dụng mô hình cũ thì có rất nhiều cái để đánh cãi chửi nhau (thực tế luôn).

Mình đã cực kì đau đầu vì vụ này. Bàn với một anh Team Lead (một fan cuồng của functional programing) thì lão còn định đập cmn server Spring đi mà thay thế = NodeJS (KH nó lại chả tế sống cả team). Cuối cùng cả 2 cũng đưa ra một giải pháp khá hay. Đó là thay vì phải đau đầu phân chia theo screen thì ném tất cả vào một chỗ. Kết quả là: 1 Controller, 1 Service, 1 DAO.

Nhiều bạn đọc đến đây sẽ chửi tung lên ngay. Nhưng xin đọc hết đã nhé.
Cách phân chia này sẽ đảm bảo được tính độc lập trong code(moẹ, có mỗi một thì có conflict với ai đâu mà chả độc lập), lại khắc phục được vụ duplicate code như trong mô hình level 2.
Nhưng cách làm này nếu áp dụng source control theo cách truyền thống thì bọn TL với PM như tôi chết cmn luôn. Suy nghĩ chán chê cuối cùng tự thấy mình ngu. Tại sao phải bổ dọc (tức là mỗi dev sẽ làm từ A-Z cho 1 function/module từ UI, Controller, Service, DAO) khi mà cách chia của mình đã làm theo từng lớp? Thế là tôi phân chia như sau:
  • 1 team UI làm front-end, team này cử ra 1 thằng chỉ take care thằng Controller. Bọn UI cần cái gì thì báo với thằng làm Controller là xong. Thằng đấy cũng làm nhiệm vụ take care phần service trong UI (React/Angular) luôn.
  • 1 team chỉ chuyên làm back-end Service, tập trung làm những thứ cần thiết và theo yêu cầu của bọn Controller mà thôi. Chỉ duy nhất Team Lead của team này được phép push code vào dev branch. Còn việc merge/resolve/...của chúng nó ntn thì mình ko care.

Kết quả là dự án chạy vèo vèo, mình còn giữ được thành tích là 1 năm liền deliver bug free product cho KH. Như vậy nó cũng tương đối có hiệu quả đấy chứ.

Còn về project/code structure bên front-end thế nào thì còn nhiều cái khá phức tạp, bản thân mình cũng ko có quá nhiều exp về cái này nên ko dám chém mạnh. Nhưng bạn Lý Thành Nhân bên Tiki cũng có 2 bài tương đối hay để cho mọi người tham khảo:
https://nextlint.com/@lythanhnhan27...-ung-dung-react-phan-1-eHHLqvVXxUzFy48phJMspf
https://nextlint.com/@lythanhnhan27...n-hao-cho-react-phan-2-C7k235L64BWzKNQ9Rzt2PU

Nào, giờ có gạch đá gì thì mình xin chịu tất nhé. Lưu ý: gạch đá thì phải có lý luận và dẫn chứng cụ thể nhé. Thanks for reading
 
mình cũng đang làm theo cách 2 của bạn những function core hoặc common thì tách thành project riêng rồi import qua thôi còn lại chia mỗi business thành từng module riêng biệt vừa muốn host service nào thì host mà vẫn đảm bảo đc tất cả các microservice nằm chung project ko cần phai tách riêng project chi cho mệt

via theNEXTvoz for iPhone
 
Bạn định nghĩa Screen của bạn là gì vậy? Một component như Teaser, Banner, Cluster Box hay một Screen là một Page gồm nhiều component?
 
xin quote tạm thím hường để anh em tham khảo

Về vấn đề của chủ thớt: chủ thớt đề xuất setup project theo mô hình 3 lớp như vậy xét về mặt kỹ thuật là hợp lý. Team leader và những người trong team chủ thớt phản đối mà ko nêu được lý do chính đáng (ví dụ scope project nhỏ, ko cần mở rộng và reuse, deadline hạn hẹp, team members thiếu kinh nghiệm..v..v..) thì rất đáng trách và làm nhiều người ko phục. Cái này có thể ko phải những người đó dốt (mặc dù có thể dốt thật) mà do cố chấp, ko muốn bị 1 thằng nhóc 9x dạy đời.

Lời khuyên cho chủ thớt: mình chỉ gợi ý, quyền quyết định là của thằng tech lead. Nó ko nghe thì thôi, vì chung quy người chịu trách nhiệm là nó, là PM, Team Leader. Nếu cái đề xuất của mình nó ko nghe theo mà ko ảnh hưởng gì lớn tới công việc của mình, như trường hợp này design dở nhưng lại code khoẻ hơn, kiểu mì ăn liền thì thôi chấp nhận làm theo. Lâu dài ko học hỏi đc gì hay thì kiếm cty khác.

Về vụ kiến trúc dự án nên là 3 lớp hay 2 lớp:
Một dự án bất kỳ Web, Windows, Mobile thường sẽ như vầy:
UI (Web, Win, Mobile) hoặc REST API -> Business Service -> Data Access Service (persistence layer).

Business Service là phần core nên 90% các dự án là sẽ có. Còn Data Access Service là để CRUD vào database, XML file, cloud storage... hay cái quái gì khác ko cần biết. Mục đích của nó là tách phần logic đọc ghi data với phần business service để có thể Unit Test business service bằng cách mock thằng Data Access Service, hoặc thay đổi database (ví dụ thay thế implementation của layer này từ MySQL sang cloud storage, hoặc XML...). Data Access Service có thể có hoặc không thì tuỳ vào scope dự án.

Dự án nhỏ, chỉ đơn thuần CRUD, ko có nhu cầu đổi database (thực tế khá hiếm) như dự án của chủ thớt thì nên đi theo cái này: UI -> Business Service. Lý do: các dự án CRUD thì phần core business logic rất ít, nếu đẻ ra Data Access Service thì đa số phần logic thật sự nằm ở tầng này vì làm CRUD và phải đảm bảo tính Unit of Work của nó (update 2 tables cùng 1 transaction) như vậy business service chỉ có nhiệm vụ pass qua lại DAS và UI thì chi bằng implement thẳng nó vào business service luôn, chấp nhận fix thằng database vì nó hiếm thay đổi mà. Tất nhiên làm vậy thì chỉ có thể Unit Test thằng UI (controller), còn business service thì integration test nhưng cũng nên như vậy vì CRUD nên làm integration test.

Về Repository và ORM:
Tôi ko gọi thằng này là Repository mà gọi là Data Access Service như bên trên. Hiện tại có ORM nên tôi sẽ ko tạo ra Repository tương ứng cho từng table mà sử dụng trực tiếp hibernate session, dbContext, dapper... trong phần implementation của Data Access Service luôn. Làm như vậy tận dụng được hết khả năng của ORM framework vì nó support tận răng rồi (CRUD, mapping, transaction) . Trường hợp duy nhất cần Repository là khi có ý định thay đổi luôn cả ORM framework như vậy phải abstract luôn cả thằng này (cực hiếm) trong khi hiện tại các ORM framework đã support thay đổi nhiều database luôn rồi.

Về vụ conflict source:
Nguyên tắc rất đơn giản: 1 người chỉ được code trên 1 file, phân chia làm sao để tránh xài chung thì thì ko có conflict: Ví dụ phân front end/back end thì từ controller, html, css, js là anh front end làm, code business service, data access service, SQL script là anh backend làm. Còn ko thì phân theo Use Case, module.

@phongkyanh: được chưa thím :shame:

Về vụ circular reference:
"Cách tốt nhất để giải quyết một vấn đề phát sinh là đừng để phát sinh cái vấn đề đó".
Nói chung là tách 2 service ra thành 3 cái, hoặc gộp 2 service thành 1 cái nếu có quá nhiều phần chung. Làm sao làm để ko có circular reference. Còn nếu bắt buộc thì dùng property injection thay cho constructor injection, nhưng tôi prefer cách ko nên để có circular reference. Ai chửi tui ngu ko giải quyết được đi né tránh vấn đề thì tui chịu. :shame:

Về vụ Join các Repository:
Cái này theo tôi nói ở post trước là dùng thẳng ORM framework trong implementation của Data Access Service (DAS) để tận dụng hết features của ORM framework.
https://voz.vn/t/van-de-ve-separate-repository-service-controller.183643/post-5706814

Còn vẫn muốn dùng Repository map 1-1 với table thì phải abstract và wrap các method của ORM như Join, Query, Order, Create, Insert, Update, Delete, Transaction operations... nói chung là đi wrap gần như tất cả các method của ORM. Thường người ta sẽ làm nó trong 1 base class là GenericRepository<T> như thế này rồi gọi method Join giữa 2 Repo như link dưới:
https://entityframework.net/knowled...generic-repository-pattern---entity-framework
C#:
using (MyDbContext ctx = new MyDbContext())
{
  var studentRep = new Repository<Student>(ctx);
  var standardRep = new Repository<Standard>(ctx);
  var studentToStandard = studentRep.GetAll().Join(standardRep.GetAll(),
                        student => student.StandardRefId,
                        standard => standard.StandardId,
                        (stud, stand) => new { Student=stud, Standard=stand }).ToList();
}

Nhưng tôi ko prefer cách phía trên lắm vì thật sự ko cần thiết, trừ trường hợp thay đổi ORM thôi. Cái vụ Repository pattern này thường hay bị lạm dụng vì hàng loạt ví dụ về nó, thậm chí trên web của Microsoft cũng có 1 bài về Unit of Work và Repository pattern như bài này:
https://docs.microsoft.com/en-us/as...f-work-patterns-in-an-asp-net-mvc-application

Cái design này có 1 bất cập là tác giả sử dụng unitOfWork chỉ như một thằng manager quản lý các Repo và wrap các method của ORM lại, như vậy thì vô tình xem persistence layer xài ORM rồi. Điển hình là đây, các code này trong Controller, sử dụng unitOfWork rồi gọi cả Save, Dispose...
C#:
// ...
unitOfWork.CourseRepository.Insert(course);
unitOfWork.Save();
//...
unitOfWork.CourseRepository.Delete(id);
unitOfWork.Save();
// ...
unitOfWork.Dispose();
Làm như cách trên thì dự án này fix luôn là tầng persistence layer là xài ORM rồi. Nếu như persistence layer là gọi 3rd API của cloud storage thì 2 hàm unitOfWork.Save(); unitOfWork.Dispose(); lại bị dư thừa, lúc đó phải implement lại và để trống? Theo tôi thì nó nên là Data Access Service và độc lập với persistence layer bên dưới, trong implementation của courseDAS.Insert() và courseDAS.Delete(id) thì xài trực tiếp ORM hoặc gọi 3rd API hay gì thì tuỳ vào persistence layer của mình.
C#:
// ...
courseDAS.Insert(course);
//...
courseDAS.Delete(id);

Bonus thêm cái link bàn về Repository và ORM ở đây:
https://www.thereformedprogrammer.net/is-the-repository-pattern-useful-with-entity-framework-core/
 
Đơn giản chỉ là 1 page thôi (cái này là jsp, thymeleaf,...not React/Angular/Vue,... nhé)
Ví dụ mình có một cái Countries-Table và mình có 2 pages.

  • Một page về thắng cảnh của các nước. Ví dụ có cái combo box để người dùng select country rồi liệt kê thắng cảnh của country đó.
  • Một page về khách sạn của các nước. Hoạt động như trên, user select country rồi liệt kê các khách sạn.

Hỏi: Vậy là bạn tạo 2 screen như ở trên và tạo 2 cái DAO để cùng lấy danh sách countries ra?
 
Ví dụ mình có một cái Countries-Table và mình có 2 pages.

  • Một page về thắng cảnh của các nước. Ví dụ có cái combo box để người dùng select country rồi liệt kê thắng cảnh của country đó.
  • Một page về khách sạn của các nước. Hoạt động như trên, user select country rồi liệt kê các khách sạn.

Hỏi: Vậy là bạn tạo 2 screen như ở trên và tạo 2 cái DAO để cùng lấy danh sách countries ra?
Về mặt design thì sẽ không ai dở hơi đi lấy list country riêng lẻ cả. Cách lấy ra sẽ là 1 Map dạng <countryName - List of hotels> và <countryName - List of landscapes> --> Nó sẽ là 2 câu queries khác nhau.

Còn nếu bạn thích design kiểu của bạn thì ok, không sao cả, mình hiểu ý của bạn. Nhưng ngay trong post của mình, mình cũng đã chỉ rõ nhược điểm của mô hình của mình.

Nhưng cái gì cũng có mặt không tốt của nó. Mô hình của mình cũng vậy. Nhược điểm của mô hình này của mình chính là việc duplicate code diễn ra tương đối nhiều.

Ví dụ: cùng là 1 câu query SQL, nhưng có thể xuất hiện cả ở screen A lẫn screen B (Để đảm bảo tính độc lập giữa các screens với nhau, logic sẽ được tách riêng là điều đương nhiên)

Trên quan điểm của mình, việc duplicate code này không ảnh hưởng gì tới software quality cả. Và trên thực tế, mình phản đối việc re-use các code có liên quan tới business logic. Bản chất mỗi business đều độc lập với nhau, việc trùng nhau là chuyện bình thường. Khi tách riêng ra, việc maintain sẽ cực kì dễ thở vì không lo ảnh hưởng tới các module khác.
 
Chia theo screen nhưng chỉ chia view và controller, service với dao phải để gom vào 1 chỗ vì tái sử dụng đc.
 
@_Worker_of_MUFC_: Mình hỏi để mình hiểu rõ cách design của bạn để học hỏi thôi.

Mình chưa nói gì bạn cả. Đừng quá nhạy cảm như thế. Bạn viết ra để chia sẻ kiến thức mà chưa gì đã "xù lông" lên thế rồi. Ai dám hỏi tiếp?

Còn muốn chọc ngoáy thì mình ngoáy liền đây.
Việc map thì bạn nghĩ thử xem bạn có 200 countries. Mỗi country có vài chục thắng cảnh và hàng ngàn hotels.

Ý bạn nói mình dở hơi nên load toàn bộ list countries rồi lazy load khi người dùng chọn country?

Ý bạn là load liền một lúc 200 countries với mấy ngàn hotels xuống client thì nó mới đúng?
 
Sửa lần cuối:
name.of.project.interfaceA.
...batch
...vo
...dao
...helper
...utils
...controller
[...pcf (page config)
...enhancement
...typekey
...ext]
 
Về mặt design thì sẽ không ai dở hơi đi lấy list country riêng lẻ cả. Cách lấy ra sẽ là 1 Map dạng <countryName - List of hotels> và <countryName - List of landscapes> --> Nó sẽ là 2 câu queries khác nhau.

Còn nếu bạn thích design kiểu của bạn thì ok, không sao cả, mình hiểu ý của bạn. Nhưng ngay trong post của mình, mình cũng đã chỉ rõ nhược điểm của mô hình của mình.
Thôi ô ơi map cái gì, giờ tôi muốn country rồi province
Giả sử screen A và screen B đều dùng Controller X (giả sử - GET /user) thì làm tnao?

via theNEXTvoz for iPhone
Nói như anh là ngược nhé, bản chất khi gọi request là người dùng tương tác với controller trước rồi mới render đến view. K bao giờ có chuyện 1 view 2 controller.
Trường hợp a nói giả sử màn admin cần list user thì sẽ gọi vào userservice lấy, đéo ai gọi usercontroller.
 
@_Worker_of_MUFC_: Mình hỏi để mình hiểu rõ cách design của bạn để học hỏi thôi.

Mình chưa nói gì bạn cả. Đừng quá nhạy cảm như thế. Bạn viết ra để chia sẻ kiến thức mà chưa gì đã "xù lông" lên thế rồi. Ai dám hỏi tiếp?

Còn muốn chọc ngoáy thì mình ngoáy liền đây.
Việc map thì bạn nghĩ thử xem bạn có 200 countries. Mỗi country có vài chục thắng cảnh và hàng ngàn hotels.

Ý bạn nói mình dở hơi nên load toàn bộ list countries rồi lazy load khi người dùng chọn country?

Ý bạn là load liền một lúc 200 countries với mấy ngàn hotels xuống client thì nó mới đúng?
Mình trả lời bạn một cách rất bình thường, nhưng bạn mới là người bị nhột.
Thực sự mình không thích sự hời hợt cũng như thiếu suy nghĩ của bạn, càng không thích cách bạn tranh luận một cách mất não như ở thread trước.
Vậy mình xin bạn vui lòng đừng off-topic như ở thread trước được không?
 
Cách mình chia cũng giống bác thread. có điều cái web controller mình để riêng.
com.a.web.FeatrueA
Controller A.1,
Controller A.2,
com.a.components.FeatrueA
ServiceA
RepoA
DtoA
com.a.Entities.FeatrueA

Rule là các component goi nhau thông qua service hết. Nhưng nhiều khi bi break rule. và bị cyclelic
Những bên tớ db quan hệ phức tạp, nó join chục table là với nhau, data hiện thị như là 1 cái report vậy đó. cho nên native SQL nhiều :(.
Conflict thì ít. do tụi nó độc lập nhau.
Ah chủ thread thử dùng sonargrapth để đo chỉ số maintain thử. Đây là bên mình. Cycyclie critical đã sửa hết, còn lại warning chưa sửa được nữa
1607232292590.png
 
team mình đi theo clean architecture, và phải đảm bảo mỗi function viết ra điều phải viết unittest được :D, đơn giản vậy thôi
 
mấy ông Java / DotNet thấy cứ thích khổ dâm với đống architect / pattern / clean code nhỉ. Năm 2020 rồi, hãy dùng react/vue/angular để phía BE giản tiện lại. Giờ mà còn server-side rendering = JSP với Thymeleaf nữa

Vd như phía API quan tâm éo gì tới screen Login, screen Register, screen User List 1, screen User List 2.... BE cứ viết API "GET /api/users" rồi phía FE tự xử thôi.

Code duplicate là chuyện bình thường, thậm chí tui còn khuyến khích khi đang code 1 feature mới nữa. Để duplicated code đó, sau 1-2 sprint mới có đủ tầm nhìn mà refactor.
 
Mình trả lời bạn một cách rất bình thường, nhưng bạn mới là người bị nhột.
Thực sự mình không thích sự hời hợt cũng như thiếu suy nghĩ của bạn, càng không thích cách bạn tranh luận một cách mất não như ở thread trước.
Vậy mình xin bạn vui lòng đừng off-topic như ở thread trước được không?
Ủa thread trước là thread nào? Có luôn hả? Thiệt tình tui còn không biết ông là ai luôn. Tui mà nhớ tên ông trong đầu thì ông nằm trong danh sách ignore rồi. Hay chắc dùng clone acc nào đó???
Cả cái post toàn công kích cá nhân.
Cười ỉa. Ai thèm. Gớm. Bye.
 
Sửa lần cuối:

Thống kê chủ đề

Ngày tạo
_Worker_of_MUFC_,
Người trả lời cuối
darkrose,
Trả lời
46
Lượt xem
10.444
Quay lại
Lên đầu trang