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
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.

Nếu có 1 module mà anh chỉ cần thay đổi config là có thể thay đổi được cách hoạt động của nó, thì module đó là blackbox và anh là end user. Anh không thể extend được cái module đó. Như vậy mà gọi là SOLID?
https://www.brandonsmith.ninja/blog/libraries-not-frameworks
Mindset của người ta như này mà anh. Sao anh lại chỉ focus vào mỗi cái thay config thế kia. Tôi thấy cái mindset đấy nó giống kiểu abstract thinking và nếu áp dụng được cái đấy vào code thì quá là flexible và extendable còn gì nữa anh. Người ta nói thế tôi chả thấy chỗ nào không ổn cả?
 
^
đúng như anh nói. dự án crud thì 90% logic là data access.
vậy tại sao không dùng begin .. commit mà lại cần Unit Of Work? tại sao không dùng sql mà lại dùng linq?
ORM bắt anh phải map 1-1 giữa c# object và table, hay người ta gọi là database driven design.
 
Mindset của người ta như này mà anh. Sao anh lại chỉ focus vào mỗi cái thay config thế kia. Tôi thấy cái mindset đấy nó giống kiểu abstract thinking và nếu áp dụng được cái đấy vào code thì quá là flexible và extendable còn gì nữa anh. Người ta nói thế tôi chả thấy chỗ nào không ổn cả?
Vì cái abstract thinking của anh mà rất nhiều thư viện vô dụng (nhưng rất nhiều người dùng) được viết như AutoMapper, FluentValidation.
 
^
đúng như anh nói. dự án crud thì 90% logic là data access.
vậy tại sao không dùng begin .. commit mà lại cần Unit Of Work? tại sao không dùng sql mà lại dùng linq?
ORM bắt anh phải map 1-1 giữa c# object và table, hay người ta gọi là database driven design.

Vì cái nào tiện hơn thì mình dùng thôi. Những cái a nói là các implementation của thằng Data Access Service. Còn thằng Business Service nhìn xuống nó có quan tâm là bên dưới xài cái gì đâu.

via theNEXTvoz for iPhone
 
Vì cái nào tiện hơn thì mình dùng thôi. Những cái a nói là các implementation của thằng Data Access Service. Còn thằng Business Service nhìn xuống nó có quan tâm là bên dưới xài cái gì đâu.

via theNEXTvoz for iPhone
ừ nhưng anh không viết sql thì anh ko được sử dụng features của DBMS cung cấp, và anh phải học api của EntityFramework, NHibernate
 
ừ nhưng anh không viết sql thì anh ko được sử dụng features của DBMS cung cấp, và anh phải học api của EntityFramework, NHibernate

Khi nào gặp dự án mà gần trăm tables. Mổi tables chừng 50, 100 columns a ngồi làm CURD bằng cách viết SQL đi rồi thấy tại sao người ta đẻ ra ORM. Tất nhiên ORM cũng có hạn chế khi update batch data nhiều dòng, join nhiều tables. Bởi vậy mới đẻ ra thằng Dapper là để dung hoà cả 2 đó.

via theNEXTvoz for iPhone
 
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:
Anh lói rất giống tôi nghĩ. Kể cả đoạn
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ì tôi thấy giờ các công ty chạy theo thị trường nhiều quá, chỉ thích mì ăn liền thôi. Release source code xong là phủi tay. Nên cố gắng tìm công ty nào tập trung vào cả code quality nữa thì mới học hỏi được nhiều thứ.... Buồn cái là để tìm được thì hơi khó :(

Về vụ conflict source:
Tôi biết cái nguyên tắc đấy. Cho nên tôi mới bảo không nên viết cả một feature trong 1 class kể cả có tách hàm chuẩn chỉnh đi chăng nữa, vì nó dễ vi phạm cái nguyên tắc này và dẫn đến resolve conflict mất thời gian (và risk nữa). Cơ mà anh @quandaso anh ấy cứ bảo không sao nên tôi mới nói qua nói lại thôi.
 
Khi nào gặp dự án mà gần trăm tables. Mổi tables chừng 50, 100 columns a ngồi làm CURD bằng cách viết SQL đi rồi thấy tại sao người ta đẻ ra ORM. Tất nhiên ORM cũng có hạn chế khi update batch data nhiều dòng, join nhiều tables. Bởi vậy mới đẻ ra thằng Dapper là để dung hoà cả 2 đó.

via theNEXTvoz for iPhone
đừng nói với tôi gần trăm tables, mỗi table 50, 100 columns mà anh viết bằng linq dễ hơn sql nhé. dùng dapper là a đang viết sql nhé. tôi đang nói orm thay sql bằng linq kìa
 
đừng nói với tôi gần trăm tables, mỗi table 50, 100 columns mà anh viết bằng linq dễ hơn sql nhé. dùng dapper là a đang viết sql nhé. tôi đang nói orm thay sql bằng linq kìa

Thật ra ORM để làm CRUD, đỡ tốn công sức vụ mapping thôi. Những câu query phức tạp, join nhiều table, ví dụ hàm Search, reports thì tôi viết stored procedure, views ở database. Nói chung cũng rất it khi viết linq trên code. A xài ORM nhưng đâu ai cấm anh viết stored procedure đâu.
 
Thật ra ORM để làm CRUD, đỡ tốn công sức vụ mapping thôi. Những câu query phức tạp, join nhiều table, ví dụ hàm Search, reports thì tôi viết stored procedure, views ở database. Nói chung cũng rất it khi viết linq trên code. A xài ORM nhưng đâu ai cấm anh viết stored procedure đâu.
Thế thì tôi đề xuất anh bỏ hẳn EntityFramework hay NHibernate, để dev của anh không dùng table để thiết kế domain model nữa - giống "best practices" của contoso university. Và, project của anh không có 2 cách làm cho cùng 1 việc. Nó chỉ có nhược điểm là hơi mất công khi insert, update thôi.
 
Thế thì tôi đề xuất anh bỏ hẳn EntityFramework hay NHibernate, để dev của anh không dùng table để thiết kế domain model nữa - giống "best practices" của contoso university. Và, project của anh không có 2 cách làm cho cùng 1 việc. Nó chỉ có nhược điểm là hơi mất công khi insert, update thôi.

Cái này thi tuỳ dự án mà chọn cách implementation của Data Access Service dùng cái nào. Dự án a thấy ko dùng ORM ok thì ok thôi. Nó đâu liên quan domain driven hay database driven đâu.

Phần domain ở Business Service nó chỉ quan tâm các interface là IDataAccessService thôi. Nó ko cần biết anh dùng SQL, EntityFramework hay NHibernate bên dưới.

Nói luôn vụ switch database bằng config là được nhé:
Ví dụ interface IDataAccessService có các implementation là EFDataAccessService, NhDataAccessService, AdoDataAccessService, CloudDataAccessService, XmlDataAccessService. Anh switch qua lại các module này bằng config là được nhé.
 
Vì cái abstract thinking của anh mà rất nhiều thư viện vô dụng (nhưng rất nhiều người dùng) được viết như AutoMapper, FluentValidation.
Ô anh nói buồn cười vậy. Tôi đang nói cái abstract thinking nó hay, vì nó seperate các layers hay các modules với nhau, tiện cho việc thay đổi (anh muốn thay cục nào thì thay cục đấy, nó không (hoặc ít) ảnh hưởng đến các cục khác) hoặc việc sửa chữa (anh abstract tốt mà reuse cũng tốt thì chỉ cần sửa một chỗ nó sẽ ăn nhiều chỗ khác). Còn anh lại lấy luôn nhiều thư viện vô dụng để chỉ trích một principle hay một concept thì lập luận của anh thiếu thuyết phục quá. Không có võ công không tốt, chỉ có người dùng nó không tốt thôi :D

ừ nhưng anh không viết sql thì anh ko được sử dụng features của DBMS cung cấp, và anh phải học api của EntityFramework, NHibernate
Tôi chưa thấy cái framework nào về orm mà không cho anh viết native sql cả. Nếu anh thích anh có thể viết native sql được chứ? Cần gì phải cứng nhắc là bắt buộc dùng api của framework vậy
 
Cái này thi tuỳ dự án mà chọn cách implementation của Data Access Service dùng cái nào. Dự án a thấy ko dùng ORM ok thì ok thôi. Nó đâu liên quan domain driven hay database driven đâu.

Phần domain ở Business Service nó chỉ quan tâm các interface là IDataAccessService thôi. Nó ko cần biết anh dùng SQL, EntityFramework hay NHibernate bên dưới.

Nói luôn vụ switch database bằng config là được nhé:
Ví dụ interface IDataAccessService có các implementation là EFDataAccessService, NhDataAccessService, AdoDataAccessService, CloudDataAccessService, XmlDataAccessService. Anh switch qua lại các module này bằng config là được nhé.
Anh kia có vẻ không nắm được chữ L trong SOLID với cả loose coupling lắm thì phải :(
 
@lam vung lau lam: Tôi là định ignore anh luôn rồi nhưng đang đi ị buồn ko có gì làm nên chém gió với anh chút.

Thôi bây giờ anh giải quyết cho tôi cái bài toán mà tôi nêu ở trên đi. GetUserById cho N trường hợp.

Mỗi trường hợp có thể cần số lượng fields khác nhau.

Anh chỉ cho tôi một cách làm mà tôi có thể áp dụng vào dự án khoảng 20 Dev. Business Logic có thể thay đổi (Không fix). Database cũng có thể thay đổi luôn (chắc chắn).

Tôi cám ơn anh trước. Tôi đoán chỉ là đoán thôi nha. Chắc anh làm .nét rồi. Thế thì cũng may là ngay thế mạnh của tôi. Thế anh nhé.
 
tôi hiểu là OOP các anh thích abstraction. tôi cũng hiểu các anh code c# 10 năm nên cứng (lì) lắm rồi.
nên dừng tranh luận ở đây nhé.
 
vụ gọi giữa các service với nhau cũng khoai phết nhỉ, dễ bị gọi lồng nhau lỗi ngay
 
Anh @lam vung lau lam đã out topic rồi làm tôi không được học thêm cái gì mới.

Bàn về con người.
Ở trên các bạn nói abstract thinking hay abstraction thì đúng chuyên môn và hàn lâm quá rồi. Tôi thì chỉ có một cái giải thích thôi đó là vì tôi lười. Tôi lười phải chạy theo các thay đổi của tụi bên Business và cả theo chef tổng nên tôi phải làm sao mà khi có những thay đổi diễn ra, tôi phải không bị stress khi sửa các thay đổi đó vào.

Sẽ có những thời khắc mà các quyết định nó hoàn toàn mang tính "chính trị" chứ chẳng phải gì về công nghệ cái con mịa gì cả. Ví dụ đang local chạy tốt việc gì phải lên cloud, đang SQL tốt việc gì phải qua NoSQL. Đó là vì nỗi sợ của những người không hiểu. Họ sợ bị tụt hậu, họ sợ bị bỏ lỡ. Mà những người đó toàn là CEO, Chủ tịch quản trị, CIO thôi, toàn dân Politics cả chứ nền tảng technical được bao nhiêu so với dân chuyên môn. Nhưng đó là cách thế giới vận hành và phát triển.

Bàn về tech.

1.
Giờ ai nói và về select field nhanh hơn với select star thì thôi mấy anh tự làm cái test đi. Mấy anh lựa cái bảng nào nhiều dòng nhất mà mấy anh có làm cái SQL "Select * from table where id =" với cái "Select id from table where id=" xem coi nó lệch bao nhiêu ms. Đấy là phần SQL.

Rồi về phần mapping giờ mấy anh tự code một cái function map giữa 2 object A và B (copy giá trị 1-1). Một cái map full 20 properties một cái map 1 property. Rồi mấy anh đo xem coi nó lệch bao nhiêu ms.

Sau khi làm xong mấy anh báo lại xem mấy anh tối ưu được bao nhiêu ms.

Tôi báo kết quả luôn là 0ms. Tức là về performance các anh lợi được là 0ms.
Nhưng cũng phải nói các anh tiết kiệm được vài kb bộ nhớ trong quá trình map đấy.

Rồi giờ các anh đứng trước quyết định là mấy anh làm spaghetti code để tiết kiệm vài kb bộ nhớ trong vài giây thì có đáng không?

2. Rồi thứ 2 là về Raw SQL.
- Các anh nói các anh thích viết raw sql. It's ok. Đó là sở thích của các anh. Nhưng đứng ở vị trí của tôi trước dự án 20-30 dev. Các anh kêu tôi dùng raw sql á? Một là các anh bị điên. Hai là các anh bị khùng. Ai sẽ quản lý nổi cái đống raw sql đó? Convention, SQL Function, SQL Injection... ai sẽ validate? Rồi khi thay đổi Business Logic dùng cột A thay cột B thì ai sẽ ngồi search và replace rồi test lại là mọi thứ đều đúng?

- Rồi các anh gào lên là cần Raw SQL để tạo report cho nó performance. Tôi đồng ý với các anh luôn. Nhưng quan trọng là các anh đặt cái Raw SQL đó ở đâu? Các anh sẽ tự hào quăng luôn câu SQL đó vào trong code của các anh. Và tôi chúc các anh maintain nó vui vẻ cả phần đời còn lại của các anh.
Với tôi, quy tắc quan trọng nhất khi dùng ORM đó là khi anh không thể dùng ORM để viết một câu SQL anh muốn thì câu SQL đó phải nằm ở Database Server chứ không phải ở code của anh. Bởi vì, rõ ràng câu SQL đó nó gắn với một loại Database nhất định chứ không còn abstract đủ để dùng ORM nữa. Khi để câu SQL đó trong code tức là anh đã tự bắn vào "chim" mình, sau này khi có thay đổi thì anh ăn đạn đừng kêu trách ai.

Thế thôi. Đó là ý của tôi. Tầm nhìn nó phụ thuộc vào vị trí các anh đứng. Luôn nhớ là như vậy.
 
Sửa lần cuối:
1.
Giờ ai nói và về select field nhanh hơn với select star thì thôi mấy anh tự làm cái test đi. Mấy anh lựa cái bảng nào nhiều dòng nhất mà mấy anh có làm cái SQL "Select * from table where id =" với cái "Select id from table where id=" xem coi nó lệch bao nhiêu ms. Đấy là phần SQL.

Rồi về phần mapping giờ mấy anh tự code một cái function map giữa 2 object A và B (copy giá trị 1-1). Một cái map full 20 properties một cái map 1 property. Rồi mấy anh đo xem coi nó lệch bao nhiêu ms.

Sau khi làm xong mấy anh báo lại xem mấy anh tối ưu được bao nhiêu ms.

Tôi báo kết quả luôn là 0ms. Tức là về performance các anh lợi được là 0ms.
Nhưng cũng phải nói các anh tiết kiệm được vài kb bộ nhớ trong quá trình map đấy.

Rồi giờ các anh đứng trước quyết định là mấy anh làm spaghetti code để tiết kiệm vài kb bộ nhớ trong vài giây thì có đáng không?
Tôi ko anti ORM ( tôi thấy nó tiện vl với các tác vụ CURD ). Nhìn cũng đẹp hơn raw sql query.

Nhưng ý 1 của anh thì đúng là của dân cuồng tín mù quáng 1 công nghệ mà ra =)).
A chỉ biết case đơn giản mà anh từng gặp phải nên éo biết select * vs select 1 vài column cần thiết nó khác biệt lớn ntn.
Tối về tôi viết cái test: SELECT * và SELECT 1 field cần thiết cho anh xem lệch 0ms cái đầu anh.
 
Tối về tôi viết cái test: SELECT * và SELECT 1 field cần thiết cho anh xem lệch 0ms cái đầu anh.
Cám ơn anh đã cho tôi ánh sáng. Sẵn anh cho tôi biết cái table của anh như thế nào, số dòng và số cột trong đó và loại Database anh dùng (MySQL, SQL, MongoDb...) để chúng ta rõ ràng hơn là tôi "ngu" hay thằng thiết kế SQL mà anh có "ngu" nhé.

Anh hãy đập vào mặt tôi một cái trường hợp mà tôi chỉ câm nín nghe tiếng anh chửi thôi.

Bét rì ga.
 
Sửa lần cuối:
Dù project to hay bé thì vẫn nên theo chuẩn structure , theo mình dùng như thớt là chuẩn rồi dễ maintain . Cẩn thận thêm thì viết thêm 1 tầng manager nữa quản lý những service có cùng business .
 

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.250
Quay lại
Lên đầu trang