Ένα συνηθισμένο πρόβλημα κατά την εργασία με το Rails είναι να αποφασίσουμε πού θα τοποθετήσουμε τη λογική των χαρακτηριστικών μας.
Η λογική συχνά τοποθετείται στους Ελεγκτές, στα Μοντέλα ή, αν είμαστε τυχεροί, σε ένα Αντικείμενο Υπηρεσίας. Αφού λοιπόν έχουμε Service Objects τότε γιατί χρειαζόμαστε Use Cases;
Ακολουθήστε με σε αυτό το άρθρο για να ανακαλύψετε τα οφέλη αυτού του μοτίβου.
Περίπτωση χρήσης
Ορισμός
Μια περίπτωση χρήσης είναι ένας κατάλογος ενεργειών ή βημάτων συμβάντων που συνήθως καθορίζουν τις αλληλεπιδράσεις μεταξύ ενός ρόλου και ενός συστήματος για την επίτευξη ενός στόχου.
Αξίζει να αναφερθεί ότι το μοτίβο αυτό εφαρμόζεται με πολλούς διαφορετικούς τρόπους και έχει εναλλακτικές ονομασίες. Μπορούμε να το βρούμε ως Αλληλεπιδρώντες, Χειριστές ή Εντολές, αλλά στο Ruby κοινότητα με την οποία παραμένουμε Περίπτωση χρήσης. Κάθε υλοποίηση είναι διαφορετική, αλλά με τον ίδιο σκοπό: να εξυπηρετεί την περίπτωση χρήσης του συστήματος από τον χρήστη.
Ακόμη και αν στο δικό μας έργο δεν καθορίζουμε τις απαιτήσεις χρησιμοποιώντας Περίπτωση χρήσηςs και της UML, αυτό το πρότυπο εξακολουθεί να είναι χρήσιμο για τη δόμηση της επιχειρησιακής λογικής με πρακτικό τρόπο.
Κανόνες
Το μας Περιπτώσεις χρήσης πρέπει να είναι:
Πλαίσιο που είναι ανεξάρτητο από το πλαίσιο
Αγνωστικό σε βάσεις δεδομένων
Υπεύθυνοι για ένα μόνο πράγμα (καθορίζουν τα βήματα για την επίτευξη του στόχου του χρήστη)
Οφέλη
Αναγνωσιμότητα: Εύκολη ανάγνωση και κατανόηση, καθώς τα βήματα είναι σαφώς καθορισμένα.
Αποσύνδεση: Μετακινήστε τη λογική από τους Ελεγκτές και τα Μοντέλα και δημιουργήστε ένα νέο επίπεδο αφαίρεσης.
Ορατότητα: Η βάση κώδικα αποκαλύπτει τα χαρακτηριστικά που είναι διαθέσιμα στο σύστημα.
Στην πράξη
Ας πάρουμε το παράδειγμα ενός χρήστη που θέλει να αγοράσει κάτι στο σύστημά μας.
ενότητα UseCases
ενότητα Αγοραστής
κλάση Purchase
def initialize(buyer:, cart:)
@buyer = αγοραστής
@cart = cart
end
def call
return unless check_stock
return unless create_purchase
notify end
private
attr_reader :buyer, :cart
def check_stock
Services::CheckStock.call(cart: cart)
end
def create_purchase
Services::CreatePurchase.call(buyer: buyer, cart: cart).call
end
def notify
Services::NotifyBuyer.call(buyer: buyer)
end
end
end
end
Όπως μπορείτε να δείτε σε αυτό το κωδικός παράδειγμα, δημιουργήσαμε ένα νέο Περίπτωση χρήσης που ονομάζεται Αγορά. Ορίσαμε μόνο μία δημόσια μέθοδο κλήση. Στο εσωτερικό της μεθόδου κλήσης, βρίσκουμε αρκετά βασικά βήματα για την πραγματοποίηση μιας αγοράς, και όλα τα βήματα ορίζονται ως ιδιωτικές μέθοδοι. Κάθε βήμα καλεί ένα αντικείμενο υπηρεσίας, με αυτόν τον τρόπο η Περίπτωση χρήσης καθορίζει μόνο τα βήματα για την πραγματοποίηση μιας αγοράς και όχι την ίδια τη λογική. Αυτό μας δίνει μια σαφή εικόνα για το τι μπορεί να γίνει στο σύστημά μας (πραγματοποίηση μιας αγοράς) και τα βήματα για να επιτευχθεί αυτό.
Τώρα είμαστε έτοιμοι να καλέσουμε το πρώτο μας Περίπτωση χρήσης από έναν ελεγκτή.
κλάση Controller
def αγορά
UseCases::Buyer::Purchase.new(
buyer: purchase_params[:buyer],
cart: purchase_params[:cart]
).call
...
end
...
end
Από αυτή την άποψη, η Περίπτωση χρήσης μοιάζει αρκετά με ένα αντικείμενο υπηρεσίας, αλλά ο σκοπός του είναι διαφορετικός. Ένα Service Object επιτελεί μια εργασία χαμηλού επιπέδου και αλληλεπιδρά με διάφορα μέρη του συστήματος, όπως η Βάση Δεδομένων, ενώ το Η περίπτωση χρήσης δημιουργεί μια νέα αφαίρεση υψηλού επιπέδου και ορίζει τα λογικά βήματα.
Βελτιώσεις
Η πρώτη μας Περίπτωση χρήσης λειτουργεί αλλά θα μπορούσε να είναι καλύτερο. Πώς θα μπορούσαμε να το βελτιώσουμε; Ας χρησιμοποιήσουμε το ξηρό πετράδια. Σε αυτή την περίπτωση θα χρησιμοποιήσουμε ξηρή συναλλαγή.
Πρώτα ας ορίσουμε τη βασική μας κλάση.
κλάση UseCase
include Dry::Transaction
class << self
def call(**args)
new.call(**args)
end
end
end
Αυτό θα μας βοηθήσει να περάσουμε χαρακτηριστικά στη συναλλαγή UseCase και να τα χρησιμοποιήσουμε. Στη συνέχεια, είμαστε έτοιμοι να επαναπροσδιορίσουμε την περίπτωση χρήσης Purchase.
ενότητα UseCases
ενότητα Αγοραστής
κλάση Purchase
def initialize(buyer:, cart:)
@buyer = αγοραστής
@cart = cart
end
def call
return unless check_stock
return unless create_purchase
ειδοποίηση
end
private
attr_reader :buyer, :cart
def check_stock
Services::CheckStock.call(cart: cart)
end
def create_purchase
Services::CreatePurchase.call(buyer: buyer, cart: cart).call
end
def notify
Services::NotifyBuyer.call(buyer: buyer)
end
end
end
end
Με τις νέες αλλαγές, μπορούμε να δούμε με σαφή τρόπο πώς ορίζονται τα βήματά μας και μπορούμε να διαχειριστούμε το αποτέλεσμα κάθε βήματος με τις λειτουργίες Success() και Failure().
Είμαστε έτοιμοι να καλέσουμε τη νέα περίπτωση χρήσης στον ελεγκτή και να προετοιμάσουμε την απάντησή μας ανάλογα με το τελικό αποτέλεσμα.
κλάση Controller
def αγορά
UseCases::Buyer::Purchase.new.call(
buyer: purchase_params[:buyer],
cart: purchase_params[:cart]
) do |result|
result.success do
...
end
result.failure do
...
end
end
...
end
...
end
Αυτό το παράδειγμα θα μπορούσε να βελτιωθεί ακόμη περισσότερο με επικυρώσεις, αλλά αυτό είναι αρκετό για να δείξει τη δύναμη αυτού του προτύπου.
Συμπεράσματα
Ας είμαστε ειλικρινείς εδώ, η Μοτίβο περίπτωσης χρήσης είναι αρκετά απλή και μοιάζει πολύ με ένα Service Object, αλλά αυτό το επίπεδο αφαίρεσης μπορεί να κάνει μεγάλη αλλαγή στην εφαρμογή σας.
Φανταστείτε έναν νέο προγραμματιστή που εντάσσεται στο έργο και ανοίγει το φάκελο use_cases, ως πρώτη εντύπωση θα έχει μια λίστα με όλα τα χαρακτηριστικά που είναι διαθέσιμα στο σύστημα και αφού ανοίξει ένα Use Case θα δει όλα τα απαραίτητα βήματα για αυτό το χαρακτηριστικό χωρίς να εμβαθύνει στη λογική. Αυτή η αίσθηση τάξης και ελέγχου είναι το σημαντικότερο πλεονέκτημα αυτού του προτύπου.
Πάρτε το στην εργαλειοθήκη σας και ίσως στο μέλλον το χρησιμοποιήσετε.