Phân biệt các loại service trong ứng dụng của bạn
Ở bài viết trước mình đã từng giới thiệu Service Object là gì và cách sử dụng chúng, bài dưới đây sẽ thể hiện tổng quan hơn việc service là gì và các loại service mà chúng ta có. Không chỉ riêng về service object trong rails. Đôi khi trong model bạn đi qua một điều gì đó mà nó không thực sự là ...
Ở bài viết trước mình đã từng giới thiệu Service Object là gì và cách sử dụng chúng, bài dưới đây sẽ thể hiện tổng quan hơn việc service là gì và các loại service mà chúng ta có. Không chỉ riêng về service object trong rails.
Đôi khi trong model bạn đi qua một điều gì đó mà nó không thực sự là như vậy. Những hoạt động đó không hoàn toàn thuộc về một đối tượng đó được gọi là service. Service layer thường sống trong một lớp service riêng biệt, nó nằm giữa controllers và models.
Thiết kế ứng dụng với services và service layer là phổ biến trong cộng đồng Java J2EE/Spring. Tuy nhiên nó không phổ biến trong thế giới Rails. Điều này có phải do cộng đồng Ruby cho rằng Java phức tạp hay là do sự gia tăng của HTTP dựa vào JSON API làm cho kiến trúc này trở nên lỗi thời? Để trả lời những câu hỏi này, chúng ta hãy đi sâu hơn về services để thấy được lợi ích của chúng.
Service là gì?
Trong Domain-Driven Design, Evans định nghĩa một service là một hoạt động được cung cấp như một giao diện đứng một mình trong model. Nói một cách khác, service là một action có thể tương tác không phải là thing. Và thay vì forcing hoạt động vào một đối tượng đã tồn tại, chúng ta nên đóng gói chúng trong những service với trạng thái riêng biệt.
Không phải lúc nào chúng ta cũng cần phải rõ ràng những logic tạo thành một service. Và tách service ra càng nhỏ càng tốt và cần phân loại đúng service mà chúng ta sử dụng, để làm được điều đó chúng ta sẽ đi đến các định nghĩa phân loại services như sau:
Các loại services
Evans đã định nghĩa 3 loại service như sau:
- Application
- Domain
- Infrastructure
Application Services
Một application service là một service cung cấp các hoạt động không liên quan đến infrastructure (cơ sở hạ tầng của phần mềm), nó sẽ không liên quan đến việc thảo luận về các miền model bên ngoài phần mềm, tức là trong môi trường tự nhiên của nó.
Ví dụ, export các transactions của một account dưới dạng file CSV trong ứng dụng banking là một application service vì nó được export dưới dạng một file, CSV, nó không có ý nghĩa trong miền của banking (Có nghĩa không liên quan gì đến lĩnh vực banking).
class AccountCSVExporter def self.to_csv(account) CSV.generate do |csv| account.transactions.each do |transaction| csv << [transaction.amount, transaction.created_on] end end end end
Application service cải thiện sự gắn kết của các model bằng cách ngăn chặn các thực thi chi tiết vào đó.
Domain Services
Một domain service là kịch bản cho một use-case có liên quan đến nhiều đối tượng. Forcing logic này vào một object là sự không khéo léo, vì các use-cases thường liên quan đến các quy tắc, trách nhiệm bên ngoài và không phải là của một đối tượng duy nhất.
Tiếp tục với ví dụ liên quan đến banking, chuyển khoản ngân hàng sẽ là một ví dụ về domain service. Chuyển tiền nó không hoàn toàn liên quan đến account object hay một phần của customer object, vì vậy nó sẽ trở thành một dịch vụ độc lập.
class FundsTransferService def self.transfer(from, to, amount) Account.transaction do from.debit amount to.credit amount end end end
Một ví dụ khác về domain service là tìm kiếm trên cả một trang web với nhiều models khác nhau. Do nó được tìm kiếm trên những model khác nhau nên nó không còn phù hợp với khái niệm trong bất kì model cụ thể nào cả. Thay vào đó chúng ta cần phải có một dịch vụ tìm kiếm riêng biệt cho việc này.
class SearchService def self.search(query) Sunspot.search(Document, Email, Appointment) do fulltext query end end end
Infrastructure Services
Một infrastructure service đóng gói các truy cập vào một hệ thống bên ngoài. Các infrastructure services thường được sử dụng bới application và domain service. Như là gửi mail, gọi API từ bên ngoài ....
E-mailling và message queuing là 2 ví dụ về infrastructure services.
class AccountEmailService def self.overdrawn_account(account) email = AccountMailer.overdrawn_account account email.deliver end end class MessagingService def self.overdrawn_account_sms(account) Resque.enqueue SmsJob, "Account #{account.account_number} overdrawn!" end end
Kết luận
Từ các định nghĩa trên có thể thấy rằng chúng ta hầy như là đang sử dụng 2 loại service trong ứng dụng Ruby là Domain service và Infrastructure service. Việc xác định đúng được loại service sẽ giúp chúng ta dễ dàng quản lý và tái sử dụng một cách tối ưu nhất mã code của mình.