Hãy chia sẻ kiến trúc ứng dụng web dựa trên Java!
Có rất nhiều kiến trúc khác nhau cho các ứng dụng web sẽ được triển khai bằng Java. Các câu trả lời cho câu hỏi này có thể phục vụ như một thư viện của các thiết kế ứng dụng web khác nhau với những ưu và nhược điểm của chúng. Trong khi tôi nhận ra rằng các câu trả lời sẽ mang tính chủ quan, chúng ta hãy cố gắng khách quan nhất có thể và thúc đẩy những ưu và nhược điểm chúng ta liệt kê.
Sử dụng mức độ chi tiết bạn thích để mô tả kiến trúc của bạn. Để câu trả lời của bạn có giá trị, ít nhất bạn sẽ phải mô tả các công nghệ và ý tưởng chính được sử dụng trong kiến trúc mà bạn mô tả. Và cuối cùng nhưng không kém phần quan trọng, khi nào chúng ta nên sử dụng kiến trúc của bạn?
Tôi sẽ bắt đầu...
Tổng quan về kiến trúc
Chúng tôi sử dụng kiến trúc 3 tầng dựa trên các tiêu chuẩn mở từ Sun như Java EE, Java Persistence API, Servlet và Java Server Pages.
- Kiên trì
- Kinh doanh
- Trình bày
Các luồng giao tiếp có thể có giữa các lớp được thể hiện bởi:
Persistence <-> Business <-> Presentation
Ví dụ, điều đó có nghĩa là lớp trình bày không bao giờ gọi hoặc thực hiện các hoạt động bền bỉ, nó luôn thực hiện nó thông qua lớp nghiệp vụ. Kiến trúc này có nghĩa là để đáp ứng nhu cầu của một ứng dụng web có tính sẵn sàng cao.
Kiên trì
Thực hiện các thao tác tạo, đọc, cập nhật và xóa ( CRUD ). Trong trường hợp của chúng tôi, chúng tôi đang sử dụng ( API liên tục Java ) JPA và chúng tôi hiện đang sử dụng Hibernate làm nhà cung cấp kiên trì của chúng tôi và sử dụng EntityManager của nó .
Lớp này được chia thành nhiều lớp, trong đó mỗi lớp liên quan đến một loại thực thể nhất định (nghĩa là các thực thể liên quan đến giỏ hàng có thể được xử lý bởi một lớp duy trì duy nhất) và được sử dụng bởi một và chỉ một người quản lý .
Ngoài ra, lớp này cũng lưu trữ các thực thể JPA giống như Account
, ShoppingCart
v.v.
Kinh doanh
Tất cả logic được gắn với chức năng ứng dụng web được đặt trong lớp này. Chức năng này có thể là bắt đầu chuyển tiền cho một khách hàng muốn trả tiền cho một sản phẩm trực tuyến bằng thẻ tín dụng của họ. Nó cũng có thể tạo ra một người dùng mới, xóa một người dùng hoặc tính toán kết quả của một trận chiến trong một trò chơi dựa trên web.
Lớp này được chia thành nhiều lớp và mỗi lớp này được chú thích @Stateless
để trở thành Bean phiên phi trạng thái (SLSB). Mỗi SLSB được gọi là người quản lý và ví dụ, người quản lý có thể là một lớp được chú thích như đã đề cập AccountManager
.
Khi AccountManager
cần thực hiện các thao tác CRUD, nó thực hiện các cuộc gọi thích hợp đến một thể hiện của AccountManagerPersistence
, đó là một lớp trong lớp kiên trì. Một bản phác thảo sơ bộ của hai phương pháp AccountManager
có thể là:
...
public void makeExpiredAccountsInactive() {
AccountManagerPersistence amp = new AccountManagerPersistence(...)
// Calls persistence layer
List<Account> expiredAccounts = amp.getAllExpiredAccounts();
for(Account account : expiredAccounts) {
this.makeAccountInactive(account)
}
}
public void makeAccountInactive(Account account) {
AccountManagerPersistence amp = new AccountManagerPersistence(...)
account.deactivate();
amp.storeUpdatedAccount(account); // Calls persistence layer
}
Chúng tôi sử dụng các giao dịch của người quản lý container để chúng tôi không phải thực hiện phân định giao dịch. Điều cơ bản xảy ra dưới mui xe là chúng tôi bắt đầu một giao dịch khi nhập phương thức SLSB và cam kết nó (hoặc khôi phục lại nó) ngay trước khi thoát khỏi phương thức. Đó là một ví dụ về quy ước về cấu hình, nhưng chúng tôi chưa có nhu cầu gì ngoài mặc định, Bắt buộc.
Dưới đây là cách Hướng dẫn Java EE 5 từ Sun giải thích thuộc tính Giao dịch bắt buộc cho Enterprise JavaBeans (EJB's):
Nếu khách hàng đang chạy trong một giao dịch và gọi phương thức của bean doanh nghiệp, phương thức đó sẽ thực thi trong giao dịch của khách hàng. Nếu khách hàng không được liên kết với một giao dịch, container sẽ bắt đầu một giao dịch mới trước khi chạy phương thức.
Thuộc tính Bắt buộc là thuộc tính giao dịch ngầm cho tất cả các phương thức bean doanh nghiệp đang chạy với phân định giao dịch được quản lý vùng chứa. Bạn thường không đặt thuộc tính Bắt buộc trừ khi bạn cần ghi đè thuộc tính giao dịch khác. Vì các thuộc tính giao dịch là khai báo, bạn có thể dễ dàng thay đổi chúng sau này.
Trình bày
Lớp trình bày của chúng tôi phụ trách ... trình bày! Nó chịu trách nhiệm về giao diện người dùng và hiển thị thông tin cho người dùng bằng cách xây dựng các trang HTML và nhận thông tin nhập của người dùng thông qua các yêu cầu GET và POST. Chúng tôi hiện đang sử dụng kết hợp Trang + Java Server Pages ( JSP ) cũ của Servlet .
Lớp gọi các phương thức trong các trình quản lý của lớp nghiệp vụ để thực hiện các hoạt động do người dùng yêu cầu và nhận thông tin để hiển thị trên trang web. Đôi khi, thông tin nhận được từ lớp doanh nghiệp là các loại ít phức tạp hơn như String
các int
ví dụ và ví dụ, và tại các thời điểm khác, các thực thể JPA .
Ưu và nhược điểm với kiến trúc
Ưu
- Có tất cả mọi thứ liên quan đến một cách cụ thể để thực hiện kiên trì trong lớp này chỉ có nghĩa là chúng ta có thể trao đổi từ việc sử dụng JPA sang một thứ khác, mà không phải viết lại bất cứ điều gì trong lớp kinh doanh.
- Thật dễ dàng để chúng ta hoán đổi lớp trình bày của mình thành một thứ khác, và có khả năng là chúng ta sẽ làm được nếu chúng ta tìm thấy thứ gì đó tốt hơn.
- Để cho EJB container quản lý ranh giới giao dịch là tốt.
- Sử dụng + JPA của Servlet rất dễ dàng (để bắt đầu) và các công nghệ được sử dụng và triển khai rộng rãi trong nhiều máy chủ.
- Sử dụng Java EE được cho là giúp chúng tôi dễ dàng tạo ra một hệ thống có tính sẵn sàng cao với việc cân bằng tải và thất bại . Cả hai chúng tôi cảm thấy rằng chúng tôi phải có.
Nhược điểm
- Sử dụng JPA, bạn có thể lưu trữ các truy vấn thường được sử dụng như các truy vấn được đặt tên bằng cách sử dụng
@NamedQuery
chú thích trên lớp thực thể JPA. Nếu bạn có càng nhiều càng tốt liên quan đến sự kiên trì trong các lớp kiên trì, như trong kiến trúc của chúng tôi, điều này sẽ trải rộng các vị trí mà bạn có thể tìm thấy các truy vấn để bao gồm cả các thực thể JPA. Sẽ khó hơn để tổng quan các hoạt động kiên trì và do đó khó duy trì hơn. - Chúng tôi có các thực thể JPA như là một phần của lớp kiên trì của chúng tôi. Nhưng
Account
vàShoppingCart
, không phải là họ thực sự đối tượng kinh doanh? Nó được thực hiện theo cách này khi bạn phải chạm vào các lớp này và biến chúng thành các thực thể mà JPA biết cách xử lý. - Các thực thể JPA, cũng là các đối tượng kinh doanh của chúng tôi, được tạo như Đối tượng truyền dữ liệu ( DTO ), còn được gọi là Đối tượng giá trị (VO). Điều này dẫn đến một mô hình miền thiếu máu vì các đối tượng kinh doanh không có logic của riêng họ ngoại trừ các phương thức truy cập. Tất cả logic được thực hiện bởi các nhà quản lý của chúng tôi trong lớp kinh doanh, dẫn đến phong cách lập trình thủ tục hơn. Đó không phải là thiết kế hướng đối tượng tốt, nhưng có lẽ đó không phải là vấn đề? (Sau tất cả các hướng đối tượng không phải là mô hình lập trình duy nhất mang lại kết quả.)
- Sử dụng EJB và Java EE giới thiệu một chút phức tạp. Và chúng ta không thể sử dụng Tomcat hoàn toàn (thêm một thùng chứa vi mô EJB không hoàn toàn là Tomcat).
- Có rất nhiều vấn đề khi sử dụng + JPA của Servlet. Sử dụng Google để biết thêm thông tin về những vấn đề này.
- Vì các giao dịch được đóng khi thoát khỏi lớp nghiệp vụ, chúng tôi không thể tải bất kỳ thông tin nào từ các thực thể JPA được cấu hình để được tải từ cơ sở dữ liệu khi cần (sử dụng
fetch=FetchType.LAZY
) từ bên trong lớp trình bày. Nó sẽ kích hoạt một ngoại lệ. Trước khi trả lại một thực thể có chứa các loại trường này, chúng ta phải chắc chắn gọi các getter có liên quan. Một tùy chọn khác là sử dụng Ngôn ngữ truy vấn bền vững Java ( JPQL ) và thực hiện aFETCH JOIN
. Tuy nhiên cả hai tùy chọn này đều hơi cồng kềnh.