thảo luận Vấn đề về separate Repository/Service/Controller

  • Người tạo chủ đề Người tạo chủ đề soledad86
  • Ngày bắt đầu Ngày bắt đầu
Giờ ví dụ như vầy. UserRepo. GetUserById.

  • Case 1: Tôi cần thông tin sau khi oauth chỉ gồm name và email.
  • Case 2: Tôi cần thông tin cho Report cần name và personal id.
  • Case 3: Tôi cần thông tin cho Salary bao gồm name, personal id và salary.
  • Case N:...

Thế là tôi phải viết N cái function để cần gì lấy đó thôi à? Thế anh tối ưu cho tôi trong trường hợp này tôi nên làm thế nào?
có nhiều cách để làm, nhưng rõ ràng lấy full data của table ra là ko tối ưu mà nhỉ?
giả sử bảng nó có lưu 1 trường text dài kiểu html bác cũng lôi ra nghe ko cần thiết lắm nhỉ
 
có nhiều cách để làm, nhưng rõ ràng lấy full data của table ra là ko tối ưu mà nhỉ?
giả sử bảng nó có lưu 1 trường text dài kiểu html bác cũng lôi ra nghe ko cần thiết lắm nhỉ
Có thể thiết kế DB (do ở đây là dev mới) để tránh việc fetch data lên bị overtime do I/O (transfer cost) này mà :D Còn select 1 column hay N column về cơ bản khác biệt không quá lớn :D
Tùy project, các project mới hiện này mình thấy đa số cần phải phải tối ưu về chi phí (ở đây là dev time) product cần nhanh nhất có thể, mấy cái khác tính sau. Như ông thớt đang đề xuất "big change" cho cả cái team dev đó, khi triển khai sẽ kéo lùi tốc độ cả team xuống, Lead nó k chịu là đúng rồi :D (mặc dù arch có thể tốt hơn, nhưng Lead cũng cần giữ Perf nhé, Perf mà đi xuống thì thằng Lead là thằng ra đường đầu tiên :sure: )
 
có nhiều cách để làm, nhưng rõ ràng lấy full data của table ra là ko tối ưu mà nhỉ?
giả sử bảng nó có lưu 1 trường text dài kiểu html bác cũng lôi ra nghe ko cần thiết lắm nhỉ
Thì anh cứ nói tôi một cách nào mà anh cho rằng là tối ưu nhất mà thực tế áp dụng được thì chúc mừng anh đã mở ra chân trời mới cho đám viết orm và xài orm chúng tôi.

Tôi chỉ đoán, chỉ dám đoán thôi nếu sai thì anh chọi gạch nhẹ nhẹ.

Tôi đoán anh chỉ có kiến thức lý thuyết thôi chứ kinh nghiệm và vị trí anh đứng nó chưa đưa anh tới đủ để biết thế nào là tối ưu.

Đây là điều mà khi tôi nói đến tối ưu.

Tối ưu là anh làm sao mà code của anh nó sáng để ít nhất vài chục developer nó cày phá trong đó mà vẫn hạn chế được lỗi.

Tối ưu là code của anh nó phải sạch để mà một thằng developer nó lỡ có phá thì cũng hư một chỗ thôi chứ ko phải ngàn chỗ hư theo chỉ bởi một commit.

Tối ưu là code anh nó chạy 1 request nó tốn A thời gian. Khi nó phải xử lý 1000 request thì mỗi request nó cũng chỉ tốn A thời gian chứ không tăng thêm thành A+ hay A++.

Tối ưu là khi Business Logic nó thay đổi, mở rộng thì developer nó chỉ phải sửa một chỗ chứ ko phải sửa cả trăm chỗ bằng search và replace.

Tối ưu là khi anh thay cả hệ thống database từ local lên cloud hay thậm chí đổi qua thằng database khác thì anh chỉ việc thay cái config hay đổi cái library implementation của repo là xong chứ không phải là ngưng cả hệ thống lại chờ Team Dev.

Khi nào anh có cái mind set như thế, có cái tầm nhìn như thế thì anh sẽ hiểu trả lại full entity từ repo là quá bình thường và thậm chí là tốt cho một hệ thống lớn. Nó chả có gì là không tối ưu cả.
 
Xin lỗi tôi nói thiếu.



Anh không vứt toàn bộ feature vào 1file là tôi mừng cho dev của anh :D

Còn tôi phân tích dựa trên cái ví dụ của anh nhé. Anh cứ tưởng tượng là có N ông dev cùng làm 1 feature/1 file. Nếu dev1 có commit1 thêm func1 vào cuối file, dev2 commit2 func2 cũng cuối file,... devN commitN funcN cũng cuối file. Thế có phải là sẽ có N-1 lần resolve conflicts không?
:cautious:

Đấy chỉ là mới nói chuyện bị conflict có 1 chỗ đấy nhé. Còn nếu có tầm X chỗ conflicts (càng nhiều người làm cùng 1 file + commit sửa nhiều chỗ trong file thì X càng dễ lớn) thì ông dev nào non có mà nhoè mẹ mắt
:D
=> lại gọi lead hoặc tham khảo với team để resolve => tốn thời gian + risk
Nếu code scripting language mà không phải kiểu typesafe thì conflict lại càng đáng sợ
:(



Resolve conflicts là việc của dev, nhưng take risk lại là việc của anh :D

=> tốt nhất là "không nên để xảy ra việc conflict" hoặc để X thật nhỏ bằng cách chia nhỏ file ra :D
Thực ra tôi ko muốn tranh cãi vấn đề về conflict vì lạc đề so với topic này, conflict hay không ngoài về cấu trúc code nó còn phải liên quan đến vấn đề quản lí chia feature/task cho member cho hợp lí
Cái ông nói thì cũng đúng đấy, nhưng mà ông xem cái ví dụ source code git của tôi đi. Một feature của git:diff dù rất to cũng chả cần đến 2 ông dev làm cùng lúc. Có ông bên trên giải thích vì sao 6k LOC mà ko bị conflict rồi đấy, vì trong 1 thời điểm chỉ có 1 ông dev làm cái đó thôi. Dù có n ông khác trong team thì có làm thì sẽ không làm cùng thời điểm đó mà làm feature khác. Đấy là nói giai đoạn phát triển, còn giai đoạn maintain thì càng nhàn nữa, thỉnh thoảng fix cái bug rồi merge lên thôi, hệ thống khi đã ổn định thì không mấy khi conflict đâu. Và tôi không thấy có gì risk ở đây cả.
p/s: Tôi làm sản phẩm nên tư duy theo hướng phát triển hệ thống cho sản phẩm nhé, còn mấy ông làm outsource cứ vài ba tháng lại có dự án mới thì chắc tư duy sẽ khác!
 
Sửa lần cuối:
Ờ, thảm họa như nào vậy bạn? Nó chỉ thảm họa khi rơi vào tay 1 ông lead kém, không biết dẫn dắt team đi theo 1 thể thống nhất ( mà thực ra khi đã kém thì khi áp pattern vào thì nó sẽ ra 1 đống bùi nhùi, chỉ có đập đi làm lại). Chia function thôi cũng là nghệ thuật đấy!.
Tôi ví dụ đơn giản như là windows 32 API , hay linux kernel API nó toàn là 1 đống function đấy, bạn cũng chê nó là thảm họa đúng không?
Chẳng phải nếu FP rơi vào tay leader kém, áp dụng không đúng cách, thì cũng là thảm họa hay sao? (cần một người lead nghệ thuật để có thể chia function)

Mình cũng có thể nói ngược lại là để leader xịn thì áp dụng OOP pattern nào cũng thành công. Nhưng mà design pattern sinh ra là để giải quyết phần lớn những vấn đề chung thường gặp. Vậy không tránh được sẽ bị thiếu sót những vấn đề cụ thể. Design pattern mà cần một leader tài năng hay một team tài năng để áp dụng thì nó không còn là pattern nữa rồi. Nó sinh ra để cho những người kém tài năng như mình vẫn xài được nè. :)
 
Chẳng phải nếu FP rơi vào tay leader kém, áp dụng không đúng cách, thì cũng là thảm họa hay sao? (cần một người lead nghệ thuật để có thể chia function)

Mình cũng có thể nói ngược lại là để leader xịn thì áp dụng OOP pattern nào cũng thành công. Nhưng mà design pattern sinh ra là để giải quyết phần lớn những vấn đề chung thường gặp. Vậy không tránh được sẽ bị thiếu sót những vấn đề cụ thể. Design pattern mà cần một leader tài năng hay một team tài năng để áp dụng thì nó không còn là pattern nữa rồi. Nó sinh ra để cho những người kém tài năng như mình vẫn xài được nè. :)
Tôi có bảo dùng FP bao giờ đâu nhỉ :D. Cậu đọc nhầm à, tôi chỉ bảo chính vì OOP nó quá nhiều pattern nên team FP nó mới ghét. Bản thân tôi vẫn dùng OOP thôi và tôi cũng ko phải dạng anti pattern. Chính bạn cũng công nhận là có áp pattern hay không nó vẫn thành công đúng không :D. Thế đúng ý mình rồi :3
 
Đấy là anh nghĩ vậy thôi :) Tại sao anh không nghĩ đến việc sẽ có một repo khác handle những câu query kiểu liên quan đến cả 2 bảng user và order nhỉ. Ví dụ như UserOrdersJoinedRepo chẳng hạn (ví dụ nên bỏ qua tính đúng đắn của tên class đi nhé :)). Vẫn đảm bảo"repo xử lý table", vừa đảm bảo luôn cả single responsibility nhé :).
Còn cái cách bố trí câu query theo "lập luận logic" của anh nó có thuật ngữ gọi là "circular dependency" đấy :D

Đã nói ở trên nhé :)

Hic, bạn thực sự có vấn đề trong việc tư duy logic.

Ngay từ đầu bạn chủ thớt đã cố gắng đưa vấn đề về việc đơn giản hoá, tránh việc 1 Service sẽ sinh ra nhiều repo. Giờ mới chỉ có 1 câu query join mà bạn sinh ra tới 3 repo class để handle. Thử tưởng tượng với business logic phức tạp thì vục mặt vào đống repo kia có mà chết à?
 
Hic, bạn thực sự có vấn đề trong việc tư duy logic.

Ngay từ đầu bạn chủ thớt đã cố gắng đưa vấn đề về việc đơn giản hoá, tránh việc 1 Service sẽ sinh ra nhiều repo. Giờ mới chỉ có 1 câu query join mà bạn sinh ra tới 3 repo class để handle. Thử tưởng tượng với business logic phức tạp thì vục mặt vào đống repo kia có mà chết à?
vậy cách giải quyết Vấn đề này ntn nhỉ?
 
Đang làm project có kiểu kiến trúc thế này. Core là 1 module riêng. Code có package repo, service, application. Api đẩy qua module k. Các util dùng chung thì có 1 module riềng. Giao tiếp với nhau qua interface.
 
Đấy là anh nghĩ vậy thôi :) Tại sao anh không nghĩ đến việc sẽ có một repo khác handle những câu query kiểu liên quan đến cả 2 bảng user và order nhỉ. Ví dụ như UserOrdersJoinedRepo chẳng hạn (ví dụ nên bỏ qua tính đúng đắn của tên class đi nhé :)). Vẫn đảm bảo"repo xử lý table", vừa đảm bảo luôn cả single responsibility nhé :).
Còn cái cách bố trí câu query theo "lập luận logic" của anh nó có thuật ngữ gọi là "circular dependency" đấy :D

Đã nói ở trên nhé :)
Mình cũng vẫn áp dụng theo cách bác này. Cần tách thêm repo ra để hanlde mấy cái linh tinh đó. Thực ra thì chả có thiết kế kiểu nào đảm bảo hết được các nguyên lý cả. Kiểu gì cũng phải nghĩ cách mà chít chọt.

via theNEXTvoz for iPhone
 
Lý do: Repo thường là trả về full entity mà controller nó chỉ cần một phần trong đó thôi nên cần có service để bỏ bớt hoặc format lại dữ liệu. Cho nên thưc tế controller gọi thẳng repo là cực hiếm.
Hay thật. "Select star" mà cũng thao thao về tối ưu.
Tối ưu là khi anh thay cả hệ thống database từ local lên cloud hay thậm chí đổi qua thằng database khác thì anh chỉ việc thay cái config hay đổi cái library implementation của repo là xong chứ không phải là ngưng cả hệ thống lại chờ Team Dev.
Lý thuyết OOP, ORM, design pattern tốt thật.
 
bên mình đang có một project nodejs backend. Bạn leader bảo viết tất cả api vào trong 1 controller duy nhất. Chạy được 4 tuần rồi có 1k LOC. Vài tháng nữa k biết có lên được 6k như bạn bên trên k, Lần nào code xong PR cũng reslove conflict cái controller này ghét vãi.
 
con dự án công ty em cũng chia layer giống thím thớt đề cập, controller thì gọi tới service, service thì gọi các repo, thậm chí gọi cả service khác (chính em khi code thêm feature đã bắt trước inject thêm service và dính circular dependency
4hx1KJn.gif
)
Tuy nhiên có một phần em thấy rất khó hiểu, đó là việc xử lý logic lại đấy cho thằng repo và controller là chủ yếu, những file controller hay repo 6k loc là chuyện rất bình thường, service layer thì đa số chỉ return repo.
Controller thì phải xử lý nhiều logic để tạo viewbag và viewmodel, còn tất cả các businesse logic đều xử lý trong Repo, đa phần là phải dùng những câu query join đến 4 5 tables (10 thằng thì có 7 thằng phải dùng query lên tới cả trang, còn lại là crud).
Em thắc mắc là nếu như business logic cần dùng đến nhiều query như thế, theo lý thuyết bác trên có nói thì repos chỉ crud và trả về full entity, thì chắc chắn code trong service sẽ bị phình cực to (thay vì chỏ dùng 1 câu query khoảng 50 loc thì phải viết 1 service 200 loc để đáp ứng nhu cầu tương tự). Như vậy có phải code bên em không clean, hay là do trên thực tế project phực tạp việc apply pattern 1 cách clean là việc khó khá thi ạ
epl6fhW.png
 
con dự án công ty em cũng chia layer giống thím thớt đề cập, controller thì gọi tới service, service thì gọi các repo, thậm chí gọi cả service khác (chính em khi code thêm feature đã bắt trước inject thêm service và dính circular dependency
4hx1KJn.gif
)
Tuy nhiên có một phần em thấy rất khó hiểu, đó là việc xử lý logic lại đấy cho thằng repo và controller là chủ yếu, những file controller hay repo 6k loc là chuyện rất bình thường, service layer thì đa số chỉ return repo.
Controller thì phải xử lý nhiều logic để tạo viewbag và viewmodel, còn tất cả các businesse logic đều xử lý trong Repo, đa phần là phải dùng những câu query join đến 4 5 tables (10 thằng thì có 7 thằng phải dùng query lên tới cả trang, còn lại là crud).
Em thắc mắc là nếu như business logic cần dùng đến nhiều query như thế, theo lý thuyết bác trên có nói thì repos chỉ crud và trả về full entity, thì chắc chắn code trong service sẽ bị phình cực to (thay vì chỏ dùng 1 câu query khoảng 50 loc thì phải viết 1 service 200 loc để đáp ứng nhu cầu tương tự). Như vậy có phải code bên em không clean, hay là do trên thực tế project phực tạp việc apply pattern 1 cách clean là việc khó khá thi ạ
epl6fhW.png
Cái này cũng khó đánh giá, phải hỏi người thiết kế ban đầu tại sao họ lại làm vậy!?

Nhưng mà biz logic nhét trong controller và repository nó làm sai lệch vai trò của 2 thằng đó.

Controller chỉ nên đóng vai trò là phương tiện giao tiếp với application/service layer. Vì trong thực tế ngoài HTTP Controller thì ứng dụng có thể dùng protocol khác như AMQP, gRPC, Thrift...

Repository chỉ nên đóng vai trò cầu nối giữa domain layer và persistence layer. Nó sẽ persist trạng thái của model xuống data storage.

Application/Service layer thì là cổng vào của ứng dụng. Nó handle use-cases, cross-domain biz logic, coordinate/glue các thành phần của ứng dụng với nhau từ domain, infrastructure...

Service gọi lẫn nhau cũng phổ biến nhưng nên hạn chế, vì gây phụ thuộc lòng vòng, xui sửa phát là sửa dây chuyền vỡ mồm.
 
Service gọi Service khác theo mình là hợp lý hơn việc nhét nhiều Repo vào Service vì nó sẽ làm cho việc sở hữu data bị chồng chéo. Bù lại phần Interface của Service phải được thiết kế tốt để tránh việc phải thay đổi để đáp ứng thay đổi của Business Logic.
Tổ chức theo kiểu này thì sau này chuyển sang microservices cũng dễ dàng hơn, chỉ cần implement một Service khác theo interface có sẵn nhưng bên dưới sẽ gọi Service bên ngoài.
 
Service gọi Service khác theo mình là hợp lý hơn việc nhét nhiều Repo vào Service vì nó sẽ làm cho việc sở hữu data bị chồng chéo. Bù lại phần Interface của Service phải được thiết kế tốt để tránh việc phải thay đổi để đáp ứng thay đổi của Business Logic.
Tổ chức theo kiểu này thì sau này chuyển sang microservices cũng dễ dàng hơn, chỉ cần implement một Service khác theo interface có sẵn nhưng bên dưới sẽ gọi Service bên ngoài.
để service gọi service thì tổ chức làm sao để tránh việc 2 services inject lẫn nhau hả bác, chắc là phải chia nhỏ service ra nhỉ
 
Các anh toàn đề xuất bỏ service, theo tôi bỏ repo là hợp ní, model service là quá đủ.
Ai phản bác cho xin lí do cái repo càn tồn tại là gì.
 

Thống kê chủ đề

Ngày tạo
soledad86,
Người trả lời cuối
freedom.9,
Trả lời
217
Lượt xem
33.247
Quay lại
Lên đầu trang