Dao trong mvc là gì?

Bài viết hôm nay khá hay và cũng là một chủ đề quan trọng trong Spring Boot. Cụ thể, chúng ta hãy tìm hiểu xem dữ liệu sẽ thay đổi như thế nào khi đi qua các lớp khác nhau. Và các khái niệm về Entity, Domain model và DTO là gì.

1. Tổng quan Kiến trúc Spring Boot

1.1. Kiến trúc mã nguồn và kiến ​​trúc dữ liệu

Trong các phần trước, chúng ta đã biết rằng mọi ứng dụng Spring Boot đều tuân theo hai mô hình cơ bản:

  • Mẫu MVC
  • Mô hình 3 lớp (3 tầng)

Và vì vậy chúng tôi đã tổng hợp lại một ứng dụng hoàn chỉnh với cấu trúc như sau.

Dao trong mvc là gì?

Sơ đồ trên dùng để tổ chức mã nguồn trong chương trình. Nhờ đó chúng ta chia thành Controller, Service, Repository tương ứng với các lớp. Tuy nhiên, về mặt tổ chức dữ liệu, sơ đồ sẽ trở thành như sau.

Dao trong mvc là gì?

Mô hình này cũng bao gồm 3 lớp, trong đó tên của các lớp được thay đổi thành các thành phần tương ứng trong Spring Boot.

Theo đó, tương ứng với mỗi lớp, dữ liệu sẽ có một dạng khác nhau. Nói cách khác, mỗi lớp chỉ nên xử lý một số loại dữ liệu nhất định. Mỗi loại dữ liệu sẽ có những nhiệm vụ và mục đích khác nhau. Tất nhiên, mã cũng được phân chia tương ứng.

Ví dụ trong sơ đồ là Bộ điều khiển không nên chạm vào biểu mẫu dữ liệu mô hình miền hoặc thực thểchỉ được phép nhận và trả lại DTO.

1.2. Tại sao phải chia nhiều loại dữ liệu?

Do tuân theo nguyên tắc SoC – tách mối quan tâm – tách mối quan tâm trong thiết kế phần mềm. Cụ thể, chúng tôi đã chia nhỏ ứng dụng Spring Boot như sau.

Spring Boot = Lớp trình bày + Lớp dịch vụ + Lớp truy cập dữ liệu

Nó phá vỡ mã nguồn theo SoC. Tuy nhiên, ở cấp độ thấp hơn, SoC thể hiện nguyên tắc đầu tiên của SOLID (Trách nhiệm đơn lẻ), có nghĩa là mỗi lớp chỉ nên thực hiện một nhiệm vụ duy nhất.

Do đó, trước đây dữ liệu chỉ có một dạng nhưng có nhiều lớp, mỗi lớp ứng xử khác nhau với dữ liệu nên dữ liệu đã thực hiện nhiều nhiệm vụ. Điều này vi phạm Trách nhiệm duy nhất, vì vậy chúng tôi cần chia nhỏ nó thành nhiều loại dữ liệu.

Một lý do khác là nếu dữ liệu chỉ có một biểu mẫu, dữ liệu nhạy cảm sẽ bị rò rỉ. Lấy ví dụ chức năng tìm kiếm bạn bè của Facebook, nó chỉ nên trả về dữ liệu với những thông tin cơ bản (ảnh đại diện, tên, …). Nếu chỉ có một kiểu dữ liệu, tất cả thông tin sẽ được trả về. Mặc dù máy khách chỉ hiển thị những thông tin cần thiết nhưng việc trả lại tất cả những thông tin đó có thể bị những kẻ xấu lợi dụng để đánh cắp thông tin nhạy cảm.

Vì vậy, việc tách dữ liệu thành các dạng riêng biệt cũng là một cách để tăng tính bảo mật cho ứng dụng.

2. Các loại dữ liệu

2.1. Hai loại dữ liệu

Theo sơ đồ trên, dữ liệu trong ứng dụng Spring Boot được chia thành 2 loại chính:

  • Công cộng: có nghĩa là trao đổi, chia sẻ với bên ngoài thông qua REST API hoặc giao tiếp với các dịch vụ khác trong microservice. Dữ liệu hiện ở định dạng DTO.
  • Riêng tư: Dữ liệu được ứng dụng sử dụng trong nội bộ và không được thế giới bên ngoài biết. Dữ liệu hiện nằm trong Mô hình miền hoặc Thực thể.

Các kiểu dữ liệu có thể có nhiều tên gọi khác nhau, nhưng nhìn chung, chúng vẫn thuộc hai phần giống nhau như trên. Do đó, khi áp dụng vào kiến ​​trúc Spring Boot, chúng ta sẽ xem xét kiểu dữ liệu nào phù hợp với lớp nào (mục 2.2).

Từ 2 loại public và private trên, chúng ta có 3 loại dữ liệu:

  • DTO (Đối tượng truyền dữ liệu): là các lớp đóng gói dữ liệu để truyền giữa máy khách – máy chủ hoặc giữa các dịch vụ trong microservices. Mục đích của việc tạo DTO là để giảm lượng thông tin không cần thiết cần được chuyển, đồng thời cũng tăng tính bảo mật.
  • Mô hình miền: là các lớp đại diện cho các miền, được hiểu là các đối tượng nghiệp vụ như Client, Report, Department, … chẳng hạn. Trong ứng dụng thực, các lớp biểu diễn kết quả tính toán, các lớp làm tham số đầu vào cho dịch vụ tính toán, … được coi là mô hình miền.
  • Thực thể: Cũng thế mô hình miền nhưng tương ứng với bảng trong DB, có thể được ánh xạ tới DB. Lưu ý rằng chỉ các thực thể mới có thể biểu diễn dữ liệu trong DB.

Các kiểu dữ liệu có hậu tố tương ứng, ngoại trừ thực thể. Ví dụ: thực thể Người dùng không có hậu tố, nếu là mô hình miền thì đó là Mô hình người dùng hoặc với DTO, thì đó là UserDto, … cũng có.

2.2. Nguyên tắc chọn dữ liệu tương ứng với lớp

Chà, tôi thậm chí không biết phải gọi nó là gì. Nói tóm lại, mỗi lớp trong mô hình 3 lớp sẽ xử lý, nhận và trả về dữ liệu của một số kiểu nhất định.

Áp dụng cho mô hình 3 lớp trong sơ đồ, chúng ta có thể rút ra nguyên tắc thiết kế chung:

  • Các lớp web: chỉ nên được xử lý DTOcó nghĩa là Bộ điều khiển chỉ nên nhận và trả lại dữ liệu dưới dạng DTO.
  • Lớp dịch vụ: nhận vào DTO (từ bộ điều khiển được gửi đến) hoặc Mô hình miền (từ các dịch vụ nội bộ khác). Dữ liệu được xử lý (có thể tương tác với DB), cuối cùng Dịch vụ trả về lớp Web dưới dạng DTO.
  • Các lớp kho lưu trữ: chỉ hoạt động trên Thực thểvì nó là đối tượng thích hợp, có thể được ánh xạ tới DB.

Đối với các thành phần Spring Boot khác không thuộc bất kỳ lớp nào, hãy:

  • Kho lưu trữ tùy chỉnh: Đây là một lớp không đi qua kho lưu trữ mà thao tác trực tiếp với cơ sở dữ liệu. Do đó, lớp này được coi là Dịch vụ.

2.3. Lập bản đồ mô hình

Khi dữ liệu đi qua các lớp khác nhau, nó sẽ biến đổi thành các dạng khác nhau. Ví dụ, DTO từ bộ điều khiển đi vào dịch vụ, nó sẽ được ánh xạ tới mô hình miền hoặc thực thể, và sau đó khi vào Kho lưu trữ, nó phải trở thành Thực thể. Và ngược lại cũng đúng.

Việc chuyển đổi giữa các kiểu dữ liệu, ví dụ DTO thành Thực thể, DTO thành mô hình miền, mô hình miền thành thực thể hoặc ngược lại, được gọi là ánh xạ mô hình.

Việc thực hiện ánh xạ mô hình thường là sử dụng thư viện như ModelMapper (cách sử dụng sẽ có trong bài sau). Tuy nhiên, đơn giản nhất là viết mã sao chép thuần túy như sau.

@Getter
công cộng lớp UserDto { Sợi dây Tên; Sợi dây tuổi tác; công cộng vô hiệu loadFromEntity(Người sử dụng thực thể) { đây.Tên = thực thể.getName(); đây.tuổi tác = thực thể.getAge(); }
}
@Getter
công cộng lớp Người sử dụng { Sợi dây Tên; Sợi dây tuổi tác; Sợi dây Người mình thích; công cộng vô hiệu loadFromDto(UserDto dto) { đây.Tên = dto.getName(); đây.tuổi tác = dto.getAge(); }
}

Đoạn mã trên khi sử dụng sẽ như thế này.

Người sử dụng người sử dụng = Mới Người sử dụng();
người sử dụng.loadFromDto(userDto); Người sử dụng người sử dụng = userService.getUser(tên tài khoản);
userDto userDto = Mới UserDto();
userDto.loadFromEntity(người sử dụng);
trở về userDto;

Một cách đơn giản hơn là thay vì viết phương thức sao chép, hãy sao chép nó trong hàm tạo. Do đó, mã chuyển đổi sẽ ngắn hơn.

Người sử dụng người sử dụng = Mới Người sử dụng(userDto); UserDto userDto = Mới UserDto(người sử dụng);

3. Thực tế như thế nào?

Khi áp dụng vào thực tế, có rất nhiều tình huống khác nhau xảy ra. Đừng đơn giản làm theo mẫu sau.

Bộ điều khiển nhận DTO> Dịch vụ chuyển DTO thành mô hình hoặc thực thể, sau đó xử lý> Kho lưu trữ nhận Thực thể để đưa vào DB

Kho lưu trữ được đưa từ DB đến Thực thể> Dịch vụ và sau đó vào DTO> Bộ điều khiển và trả về DTO

Nhưng có những trường hợp khác như:

  • Bộ điều khiển không chấp nhận DTO nhưng chấp nhận các tham số nguyên thủy như int, float, …
  • Nhận DTO danh sách
  • Trả về DTO danh sách

Do đó, trong thực tế người ta có thể thay đổi để phù hợp với dự án.

Ví dụ tiêu chuẩn là Dịch vụ sẽ thực hiện ánh xạ tới DTO và ngược lại, bộ điều khiển sẽ chỉ nhận DTO. Nhưng đôi khi để giảm tải cho dịch vụ, việc ánh xạ này sẽ được thực hiện bởi bộ điều khiển (tuy nhiên, bộ điều khiển có thể bị cồng kềnh, trong khi nó nên được giữ mỏng – càng ít mã càng tốt).

Nhưng dù theo cách nào thì quy tắc chung là ánh xạ luôn được thực hiện ở rìa của mã. Nghĩa là, nếu ánh xạ nằm trong dịch vụ, các phép biến đổi phải luôn ở đầu hoặc cuối phương thức khi chúng được xử lý.

Ngoài ra, để giảm mã boilerplate, chúng tôi thường giảm độ nghiêm ngặt một chút nếu không cần thiết. Ví dụ:

  • Đôi khi không cần mô hình miền, Dịch vụ có thể được chuyển thẳng DTO Pháo đài thực thể.
  • Dịch vụ cũng có thể trở lại Thực thể hoặc Người mẫu, nếu chúng quá đơn giản và không chứa thông tin nhạy cảm. Lúc này không cần DTO mà controller trả về Entity hoặc Model luôn để tránh nhầm lẫn (mặc dù vì nó trái quy định khi public 2 thằng này nên cân nhắc).

Có nhiều ý kiến ​​tranh cãi về việc sử dụng DTO như một mẫu chống. Cá nhân mình không thấy vậy, đôi khi DTO vẫn khá hữu ích, có thể tùy chỉnh để phù hợp và hiệu quả hơn.

Bài đăng này đủ dài. Nói thật đây là bài viết mà mình dành nhiều thời gian nhất, vì phải tiếp xúc nhiều về kiến ​​trúc. Mới hôm qua, tôi thậm chí còn lôi dự án cũ ra để cấu trúc lại nó cho phù hợp, để hiểu rõ hơn về kiến ​​trúc mà tôi sắp trình bày và những tác dụng phụ có thể xảy ra.

Bài viết được tham khảo tại nguồn https://www.petrikainulainen.net/software-development/design/undilities-spring-web-application-architecture-the-classic-way/ mà mình thấy hay nhất. Trong link trên cũng có phần kết luận và bình luận, bạn có thể đọc thêm.

Đừng quên nếu bạn cảm thấy bài viết hay và hữu ích thì hãy upvote và quay clip để tạo động lực cho mình nhé. Tạm biệt

Dao trong mvc là gì?

Bài viết được chia sẻ bởi biquyet.com

Leave a Reply

Your email address will not be published.