Modularizace Ruby on Rails pomocí Packwerk Episode I
Pro lidi je obtížné vidět celkový obraz problému, aniž by mu věnovali mnoho času a úsilí. To se stává zejména při práci s rozsáhlými a složitými aplikacemi.....
Častým problémem při práci s Rails je rozhodnutí, kam umístit logiku našich funkcí.
Logika je často umístěna v kontrolérech, modelech nebo, pokud máme štěstí, v objektu služby. Pokud tedy máme objekty služeb, proč potřebujeme případy užití?
V tomto článku se dozvíte, jaké jsou výhody tohoto vzoru.
Případ užití je seznam akcí nebo kroků událostí, které obvykle definují interakce mezi rolí a systémem za účelem dosažení cíle.
Stojí za zmínku, že tento vzor se používá mnoha různými způsoby a má alternativní názvy. Můžeme se s ním setkat jako Interaktory, Provozovatelé nebo Příkazy, ale v Ruby komunita, které se držíme Případ použití. Každá implementace je jiná, ale má stejný účel: sloužit uživateli k použití systému.
I když v našem projekt nedefinujeme požadavky pomocí Případ použitía UML je tento vzor stále užitečný pro praktické strukturování obchodní logiky.
Naše Případy použití musí být:
Vezměme si příklad uživatele, který si chce v našem systému něco koupit.
modul UseCases
modul Kupující
třída Purchase
def initialize(kupující:, košík:)
@buyer = 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
Jak můžete vidět v tomto kód jsme vytvořili nový Případ použití s názvem Nákup. Definovali jsme pouze jednu veřejnou metodu zavolejte na. Uvnitř metody volání nalezneme zcela základní kroky k provedení nákupu a všechny kroky jsou definovány jako soukromé metody. Každý krok je voláním objektu služby, čímž se naše Případ použití definuje pouze kroky k provedení nákupu, nikoliv samotnou logiku. To dává nás jasnou představu o tom, co lze v našem systému udělat (provést nákup), a kroky, jak toho dosáhnout.
Nyní jsme připraveni zavolat první Případ použití z řídicí jednotky.
třída Controller
def nákup
UseCases::Buyer::Purchase.new(
buyer: purchase_params[:buyer],
cart: purchase_params[:cart]
).call
...
konec
...
konec
Z tohoto pohledu je Případ použití vypadá podobně jako objekt služby, ale jeho účel je jiný. Objekt služby plní nízkoúrovňové úkoly a komunikuje s různými částmi systému, jako je například databáze, zatímco objekt služby Případ použití vytváří novou abstrakci na vysoké úrovni a definuje logické kroky.
Naše první Případ použití funguje, ale mohl by být lepší. Jak bychom ji mohli vylepšit? Využijme suché skvosty. V tomto případě použijeme suchá transakce.
Nejprve definujme naši základní třídu.
třída UseCase
include Dry::Transaction
class << self
def call(**args)
new.call(**args)
end
end
end
To nám pomůže předávat atributy transakci UseCase a používat je. Poté jsme připraveni znovu definovat náš případ užití Purchase.
modul UseCases
modul Kupující
třída Purchase
def initialize(kupující:, košík:)
@buyer = buyer
@cart = cart
end
def call
return unless check_stock
return unless create_purchase
oznámit
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
Díky novým změnám vidíme přehledně, jak jsou naše kroky definovány, a můžeme spravovat výsledek každého kroku pomocí Success() a Failure().
Jsme připraveni zavolat náš nový případ užití v kontroléru a připravit odpověď v závislosti na konečném výsledku.
třída Controller
def nákup
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
...
konec
Tento příklad by se dal ještě vylepšit pomocí validací, ale pro ukázku síly tohoto vzoru to stačí.
Buďme upřímní. Vzor případu použití je poměrně jednoduchý a vypadá podobně jako objekt služby, ale tato úroveň abstrakce může v aplikaci přinést velké změny.
Představte si, že nový vývojář, který se připojí k projektu a otevře složku use_cases, bude mít na první dojem seznam všech funkcí dostupných v systému a po otevření jednoho případu užití uvidí všechny potřebné kroky pro danou funkci, aniž by musel pronikat do logiky. Tento pocit pořádku a kontroly je hlavní výhodou tohoto vzoru.
Vezměte si ji do své sbírky nástrojů a možná ji v budoucnu dobře využijete.
