Áp dụng kiến trúc trên ứng dụng của ta về cơ bản có các tầng minh họa trong hình 3.7. Các thành phần chính ta xây dựng trong ứng dụng (có màu nhạt) là khối HTML5/AngularJS, RESTful Web Services và Mongo Services. Các thành phần trong khối Mongo Services sẽ cung cấp các dịch vụ cho việc xử lý nghiệp vụ liên quan đến khách hàng, sản phẩm hay đơn đặt hàng và truy cập MongoDB thông qua dịch vụ Mongo được cung cấp bởi thành phần của Amdatu.
Mỗi thành phần là một OSGi bundle, việc chia nhỏ mỗi tầng thành các thành phần sẽ giúp các thành phần ít phụ thuộc vào nhau và có tính linh hoạt hơn. Các thành
phần nhỏ giúp ta dễ dàng hiểu và thay đổi vì trong mỗi thành phần chứa không nhiều mã nguồn.
Hình 3.7: Các tầng trong ứng dụng
Ta không nên ngần ngại tạo thêm các thành phần mới. Khi một dịch vụ thực hiện một số chức năng không liên quan hoặc không liên quan về kỹ thuật, ta nên xem xét tạo một thành phần riêng cho dịch vụ. Các thành phần chính trong ứng dụng của ta được minh họa trong hình 3.8. Ứng dụng được chia thành các tầng và theo chức năng.
Hình 3.8: Các thành phần trong ứng dụng
Các thành phần sẽ giao tiếp với nhau thông qua các dịch vụ do vậy mỗi thành phần sẽ cung cấp một dịch vụ tương ứng với một giao diện. Các dịch vụ Web RESTful cũng là các thành phần OSGi và cung cấp dịch vụ OSGi, nhưng các dịch vụ này có một chút khác biệt là chúng không có giao diện riêng mà được đăng ký thông qua lớp
java.lang.Object bởi vì chúng được gọi thông qua giao diện HTTP. Phóng to thêm chút nữa ta có các thành phần và giao tiếp giữa chúng như minh họa trong hình 3.9.
Hình 3.9: Các thành phần và giao tiếp giữa chúng
Việc phụ thuộc vòng luôn luôn phải được để ý và ngăn ngừa. Trong một ứng dụng lớn, thường có một số dịch vụ thường gọi là “core service”, chúng được nhiều dịch vụ khác sử dụng. Việc sử dụng lại rất hữu ích, đây cũng là một trong những mục đích thiết kế mà ta hướng tới. Để các dịch vụ có thể sử dụng lại, chúng nên chỉ làm một công việc trong dịch vụ đó. Ví dụ, Customer Service trong ứng dụng chỉ xử lý thông tin về khách hàng. Nó được sử dụng trong cả Login Service và Order Service. Dịch vụ Indexing và Search sử dụng dịch vụ Product để cung cấp chức năng tìm kiếm Full Text.
Thiết kế bundle
Trong hầu hết các bundle, sẽ có các dịch vụ OSGi. Dịch vụ OSGi là cốt lõi trong kiến trúc của ta, và ta sẽ tạo nhiều dịch vụ trong ứng dụng. Trong một ứng dụng lớn thường có hàng trăm dịch vụ. Điều này hoàn toàn bình thường và không ảnh hưởng gì đến tính động của các dịch vụ. Ta càng tách biệt các dịch vụ về chức năng thì càng giảm được sự phụ thuộc do đó tăng khả năng linh hoạt.
Một số dịch vụ không có giao diện Java. Ví dụ, một JAX-RS Resource [21] (dịch vụ Web RESTful) nó được gọi thông qua giao diện HTTP. Do nó không được gọi bởi các lớp Java khác nên ta không cần định nghĩa giao diện Java cho riêng nó. Các dịch vụ khác như các dịch vụ truy cập cơ sở dữ liệu nên có một giao diện được định nghĩa tường minh, rõ ràng.
Ta nên luôn luôn đảm bảo các dịch vụ chỉ làm việc trong một phạm vi kỹ thuật đơn nhất. Vấn đề thường gặp phải ví dụ như một dịch vụ ban đầu hoạt động như một dịch vụ truy cập dữ liệu, sau một thời gian phát triển nó lại bao gồm thêm nhiều nghiệp vụ không liên quan đến truy cập dữ liệu. Khi đó, ta nên chỉnh lại mã nguồn và tách ra thành các dịch vụ riêng. Nếu không các dịch vụ sẽ trở nên lớn và cồng kềnh làm mất tính thành phần và khó hiểu cũng như bảo trì.
Thông thường ta nên tách các giao diện thành các API bundle riêng để tách biệt với phần cài đặt của nó. Điều này giúp ích khi một số cài đặt thường xuyên phải thay đổi. Một số bundle cơ bản trong ứng dụng:
Các bundle liên quan đến giao diện chỉ chứa các dữ liệu tĩnh như các trang html, javascript, css, ảnh. Những dữ liệu tĩnh dùng chung như thực đơn, ảnh ta đưa ra một bundle riêng. Ví dụ với dữ liệu dùng chung cho trang quản trị ta tạo một bundle riêng gọi là chipshop.admin.general.ui. Cấu trúc của bundle trong BND project như hình 3.10. Cấu trúc của bundle sau khi biên dịch như hình 3.11.
Để các tài nguyên tĩnh trong bundle có thể được truy cập từ các bundle khác thông trong file MANIFEST.MF ta cần phải khai báo các header sau:
Ta chú ý header sau: X-Web-Resource: /admin/ui. Header này cho phép các tài nguyên tĩnh trong thư mục ui trong bundle sẽ được truy cập thông qua đường dẫn /admin/ui từ các bundle khác. Các bundle cho giao diện người dùng như chipshop.frontend.ui dành
Manifest-Version: 1.0 Bnd-LastModified: 1414425100776 Bundle-ManifestVersion: 2 Bundle-Name: chipshop.admin.general.ui Bundle-SymbolicName: chipshop.admin.general.ui Bundle-Version: 0 Include-Resource: admin/ui=static Private-Package: admin.ui.css,admin.ui.img,admin.ui,admin.ui.js,ad min.ui.js.lib,admin.ui.partials Tool: Bnd-2.4.0.201409260647 X-Web-Resource: /admin/ui X-Web-Resource-Version: 1.0
cho trang frontend, chipshop.admin.orders.ui dành trong quản lý đơn hàng trong trang quản trị hay chipshop.admin.products.ui dành cho quản lý sản phẩm cũng có cấu trúc tương tự.
Hình 3.10: Cấu trúc bundle chipshop.admin.general.ui trong Bnd project
Hình 3.11: Cấu trúc bundle chipshop.admin.general.ui sau khi build
Để cho phép khách hàng xem và chọn lựa các sản phẩm ta cần tạo các component cung cấp các dịch vụ liên quan đến sản phẩm tương ứng trong các bundle như như hình 3.12.
- Bundle chipshop.products.api bao gồm một giao diện ProductService và lớp Product. Giao diện và lớp được sử dụng chung trong các bundle khác liên quan đến sản phẩm. Giao diện ProductService cung cấp một số phương thức như sau:
- Bundle chipshop.products.mongo cung cấp phần cài đặt cho giao diện ProductService và đăng ký dịch vụ dưới giao diện ProductService. Dịch vụ này xử lý các nghiệp vụ liên quan đến sản phẩm. Nó sử dụng dịch vụ MongoDB được cung cấp bởi một thành phần Amdatu để truy cập dữ liệu về sản phẩm từ MongoDB.
Hình 3.12: Tương tác giữa các bundle liên quan đến sản phẩm thông qua dịch vụ - Bundle chipshop.products.rest cung cấp các dịch vụ Web RESTful. Nó tiếp nhận các yêu cầu từ frontend được gửi bằng AngulaJS. Từ yêu cầu nhận được nó gọi đến ProductService của chipshop.products.mongo bundle để xử lý.
Để xử lý các đơn hàng ta có các bundle liên quan đến đơn hàng như hình 3.13.
- Bundle chipshop.orders.mongo cài đặt OrderService trong bundle chipshop.orders.api và đăng ký dịch vụ dưới giao diện này. Dịch vụ này xử lý các nghiệp vụ liên quan đến đơn hàng. Nó sử dụng dịch vụ MongoDB của Amdatu để truy cập MongoDB.
- Bundle chipshop.orders.rest cung cấp các dịch vụ Web RESTful cho chức năng xử lý đơn hàng. Nó tiếp nhận yêu cầu từ frontend và gọi đến dịch vụ OrderService được cung cấp bởi bundle chipshop.orders.mongo để xử lý đơn hàng.
public interface ProductService {
List<Product> listProductsInCategory(String
category);
List<Product> listProducts(); List<String> listCategories(); Product getProductById(String id);
void saveProduct(Product product);
void removeProduct(String id);
Hình 3.13: Các bundle xử lý đơn hàng
- Bundle chipshop.orders.api cung cấp giao diện OrderService, lớp Order và một số lớp khác để lưu các thông tin về đơn hàng:
Tương tự đối với xử lý liên quan đến khách hàng ta có các bundle sau:
- Bundle chipshop.customer.api bao gồm giao diện CustomerService, lớp Customer và một số lớp sử dụng chung cho các bundle xử lý các công việc liên quan đến khách hàng. Giao diện CustomerService:
- Bundle chipshop.customers.mongo cài đặt giao diện CustomerService và đăng ký dịch vụ với giao diện này. Nó xử lý các nghiệp vụ liên quan đến khách hàng đồng thời truy cập dữ liệu khách hàng từ MongoDB thông qua dịch vụ MongoDB của Amdatu.
public interface OrderService {
List<Order> listOrders();
List<Order> listOrdersForCustomer(String email); Order getOrderById(String id);
void placeOrder(Order order);
void updateOrderStatus(String orderId,
OrderEventType status); }
public interface CustomerService {
Customer getCustomerByEmail(String email);
Customer getCustomerByEmailAndPassword(String
email, String password);
void saveCustomer(Customer customer); }
- Bundle chipshop.customer.login.rest cung cấp dịch vụ RESTful cho phép khách hàng đăng nhập và đăng xuất, truy vấn thông tin tài khoản của khách hàng. Bundle này sử dụng hai dịch vụ là CustomerService từ bundle chipshop.customers.mongo và TokenProvider từ thành phần của Amdatu. TokenProvider cho phép tạo ra token tương ứng với thông tin khách hàng và kiểm tra chúng giúp xác thực thông tin đăng nhập. - Bundle chipshop.customer.rest tiếp nhận yêu cầu đăng ký khách hàng từ frontend và sử dụng CustomerService để lưu thông tin khách hàng vào MongoDB.
Với chương trình khuyến mại ta có bundle chipshop.discounts.api cung cấp giao diện DiscountService:
- Bundle chipshop.discounts.mongo cài đặt giao diện DiscountService, sử dụng dịch vụ Configuration Admin có sẵn trong thành phần của container để đọc các thông tin khuyến mại từ file cấu hình ví dụ như giảm giá bao nhiêu phần trăm sau đó lấy thông tin sản phẩm thông qua dịch vụ MongoDB của Amdatu và tính toán đưa ra các thông tin khuyến mại cho sản phẩm.
- Bundle chipshop.discounts.rest tiếp nhận yêu cầu truy cập thông tin khuyến mại từ Web frontend và sử dụng dịch vụ DiscountService để lấy thông tin về khuyến mại. Toàn bộ các bundle của ứng dụng sau khi biên dịch như trong hình 3.14:
public interface DiscountService {
List<Discount> getDiscounts(); }
Hình 3.14: Các bundle của toàn bộ ứng dụng