Ruby on Rails Modularisering med Packwerk Avsnitt I
Människor har svårt att se helheten i ett problem utan att ägna mycket tid och kraft åt det. Detta händer särskilt när man arbetar med stora och komplexa applikationer. ....
Ett vanligt problem när man arbetar med Rails är att bestämma var logiken från våra funktioner ska placeras.
Logiken är ofta placerad i Controllers, Models eller om vi har tur i ett Service Object. Så om vi har Service Objects, varför behöver vi då Use Cases?
Följ med mig i den här artikeln för att upptäcka fördelarna med detta mönster.
Ett användningsfall är en lista över åtgärder eller händelsesteg som typiskt definierar interaktionen mellan en roll och ett system för att uppnå ett mål.
Det är värt att nämna att detta mönster tillämpas på många olika sätt och har alternativa namn. Vi kan hitta det som Interaktörer, Operatörer eller Kommandonmen i den Ruby samhälle vi håller oss till Användningsfall. Alla implementeringar är olika, men har samma syfte: att tillgodose användarens behov av systemet.
Även om vi i vår projekt Vi definierar inte kraven med hjälp av Användningsfalls och UML är detta mönster fortfarande användbart för att strukturera affärslogiken på ett praktiskt sätt.
Vår Användningsfall måste vara det:
Låt oss ta ett exempel med en användare som vill köpa något i vårt system.
modul Användningsfall
modul Köpare
klass Köp
def initialize(köpare:, kundvagn:)
@köpare = köpare
@vagn = vagn
slut
def anrop
retur om inte check_stock
retur om inte create_purchase
meddela slut
privat
attr_läsare :köpare, :kundvagn
def check_stock
Tjänster::CheckStock.call(kundvagn: kundvagn)
slut
def create_purchase
Services::CreatePurchase.call(köpare: köpare, kundvagn: kundvagn).call
end
def notifiera
Services::NotifyBuyer.call(köpare: köpare)
end
slut
slut
slut
Som du kan se i detta kod exempel skapade vi en ny Användningsfall som heter Purchase. Vi definierade bara en offentlig metod samtal. Inuti anropsmetoden hittar vi ganska grundläggande steg för att göra ett köp, och alla steg är definierade som privata metoder. Varje steg anropar ett serviceobjekt, på så sätt kan vår Användningsfall definierar bara stegen för att genomföra ett köp och inte själva logiken. Detta ger oss en tydlig bild av vad som kan göras i vårt system (göra ett köp) och stegen för att uppnå detta.
Nu är vi redo att anropa vår första Användningsfall från en controller.
klass Controller
def köp
UseCases::Köpare::Köp.new(
köpare: purchase_params[:buyer],
cart: köp_parametrar[:cart]
).call
...
slut
...
slut
Ur detta perspektiv är Användningsfall ser ungefär ut som ett Service Object men syftet är annorlunda. Ett serviceobjekt utför en uppgift på låg nivå och interagerar med olika delar av systemet, t.ex. databasen, medan Användningsfall skapar en ny abstraktion på hög nivå och definierar de logiska stegen.
Vår första Användningsfall fungerar men kan bli bättre. Hur kan vi förbättra det? Låt oss använda oss av torr ädelstenar. I det här fallet kommer vi att använda torr-transaktion.
Låt oss först definiera vår basklass.
klass UseCase
include Dry::Transaktion
klass << själv
def anrop(**args)
ny.anrop(**args)
slut
slut
slut
Detta kommer att hjälpa oss att skicka attribut till UseCase-transaktionen och använda dem. Sedan är vi redo att omdefiniera vårt Purchase Use Case.
modul Användningsfall
modul Köpare
klass Köp
def initialize(köpare:, kundvagn:)
@köpare = köpare
@vagn = vagn
slut
def anrop
retur om inte check_stock
retur om inte create_purchase
meddela
avsluta
privat
attr_reader :köpare, :kundvagn
def check_stock
Tjänster::CheckStock.call(kundvagn: kundvagn)
slut
def create_purchase
Services::CreatePurchase.call(köpare: köpare, kundvagn: kundvagn).call
slut
def notify
Services::NotifyBuyer.call(köpare: köpare)
end
slut
slut
slut
Med de nya ändringarna kan vi på ett tydligt sätt se hur våra steg definieras och vi kan hantera resultatet av varje steg med Success() och Failure().
Vi är redo att anropa vårt nya Use Case i styrenheten och förbereda vårt svar beroende på slutresultatet.
klass Controller
def köp
UseCases::Köpare::Köp.new.call(
köpare: purchase_params[:buyer],
cart: köp_parametrar[:cart]
) do |resultat|
resultat.framgång do
...
slut
result.failure do
...
slut
slut
...
slut
...
slut
Det här exemplet skulle kunna förbättras ännu mer med valideringar, men det räcker för att visa hur kraftfullt det här mönstret är.
Låt oss vara ärliga här, den Mönster för användningsfall är ganska enkel och ser ut ungefär som ett Service Object, men den här abstraktionsnivån kan innebära stora förändringar i din applikation.
Tänk dig att en ny utvecklare ansluter sig till projektet och öppnar mappen use_cases, som ett första intryck kommer han att ha en lista över alla funktioner som finns i systemet och efter att ha öppnat ett användningsfall kommer han att se alla nödvändiga steg för den funktionen utan att gå djupt in i logiken. Denna känsla av ordning och kontroll är den största fördelen med det här mönstret.
Ta med detta i din verktygslåda och kanske kommer du att ha nytta av det i framtiden.