The Codest
  • O nás
  • Služby
    • Vývoj softwaru
      • Vývoj frontendů
      • Vývoj backendu
    • Staff Augmentation
      • Vývojáři frontendů
      • Vývojáři backendu
      • Datoví inženýři
      • Cloudoví inženýři
      • Inženýři QA
      • Další
    • To Advisory
      • Audit a poradenství
  • Odvětví
    • Fintech a bankovnictví
    • E-commerce
    • Adtech
    • Healthtech
    • Výroba
    • Logistika
    • Automobilový průmysl
    • IOT
  • Hodnota za
    • CEO
    • CTO
    • Manažer dodávek
  • Náš tým
  • Case Studies
  • Vědět jak
    • Blog
    • Setkání
    • Webové semináře
    • Zdroje
Kariéra Spojte se s námi
  • O nás
  • Služby
    • Vývoj softwaru
      • Vývoj frontendů
      • Vývoj backendu
    • Staff Augmentation
      • Vývojáři frontendů
      • Vývojáři backendu
      • Datoví inženýři
      • Cloudoví inženýři
      • Inženýři QA
      • Další
    • To Advisory
      • Audit a poradenství
  • Hodnota za
    • CEO
    • CTO
    • Manažer dodávek
  • Náš tým
  • Case Studies
  • Vědět jak
    • Blog
    • Setkání
    • Webové semináře
    • Zdroje
Kariéra Spojte se s námi
Šipka zpět ZPĚT
2019-03-08
Vývoj softwaru

Optimalizace kódu pomocí objektů dotazů

The Codest

Tomasz Szkaradek

Vývojový architekt

Je dost pravděpodobné, že jste se v práci již mnohokrát setkali s přetíženými modely a velkým počtem volání v řadičích. Na základě znalostí z prostředí Rails vám v tomto článku navrhnu jednoduché řešení tohoto problému.

Velmi důležitým aspektem kolejnice aplikace je minimalizovat počet nadbytečných závislostí, což je důvod, proč se v celém prostředí Rails v poslední době prosazuje přístup založený na objektech služeb a používání metody PORO (Pure Old Ruby Object). Popis, jak takové řešení použít, naleznete zde zde. V tomto článku budeme postupně řešit tento koncept a přizpůsobíme ho danému problému.

Problém

V hypotetické aplikaci máme co do činění s komplikovaným transakčním systémem. Náš model, reprezentující každou transakci, má sadu rozsahů, které pomáhají získávat data. Je to velké usnadnění práce, protože je lze najít na jednom místě. To však netrvá dlouho. S vývojem aplikace se projekt je stále složitější. Rozsahy již nemají jednoduché odkazy "kde", chybí nám data a začínáme načítat vztahy. Po chvíli to připomíná složitý systém zrcadel. A co hůř, neumíme udělat víceřádkovou lambdu!

Níže najdete již rozšířený model aplikace. Transakce platebního systému jsou uloženy v. Jak můžete vidět na příkladu níže:

třída Transaction  { where(visible: true) }

  scope(:active, lambda do
    joins(<<-SQL
      LEFT OUTER JOIN source ON transactions.source_id = source.id
      AND source.accepted_at IS NOT NULL
    SQL
  end)
end

Model je jedna věc, ale s rostoucím rozsahem našeho projektu začínají bobtnat i kontrolory. Podívejme se na příklad níže:

třída TransactionsController < ApplicationController
  def index
    @transactions = Transaction.for_publishers
                                   .active
                                   .visible
                                   .joins("LEFT JOIN withdrawal_items ON withdrawal_items.transaction_id = transactions.id")
                                   .joins("LEFT JOIN withdrawals ON withdrawalals.id = withdrawal_items.withdrawal_id OR
 (withdrawals.id = source.resource_id AND source.resource_type = 'Withdrawal')")
                                   .order(:created_at)
                                   .page(params[:page])
                                   .per(params[:page])
    @transactions = apply_filters(@transactions)
  konec
end

Zde vidíme mnoho řádků zřetězených metod spolu s dalšími spoji, které nechceme provádět na mnoha místech, pouze na tomto konkrétním. Připojená data později použije metoda apply_filters, která na základě parametrů GET přidá příslušné filtrování dat. Samozřejmě můžeme některé z těchto odkazů přenést do oboru, ale není to vlastně problém, který se snažíme vyřešit?

Řešení

Protože již víme o problému, který máme, musíme ho vyřešit. Na základě odkazu v úvodu zde použijeme přístup PORO. Přesně v tomto případě se tento přístup nazývá dotazovací objekt, který je rozvinutím konceptu servisních objektů.

Vytvořme nový adresář s názvem "services", který se nachází v adresáři apps našeho projektu. V něm vytvoříme třídu s názvem TransactionsQuery.

třída TransactionsQuery
konec

V dalším kroku musíme vytvořit inicializátor, ve kterém bude vytvořena výchozí cesta volání pro náš objekt.

třída TransactionsQuery
  def initialize(scope = Transaction.all)
    @scope = scope
  end
end

Díky tomu budeme moci přenést vztah z aktivního záznamu do našeho zařízení. Nyní můžeme do třídy přenést všechny naše obory, které jsou potřebné pouze v prezentovaném kontroléru.

třída TransactionsQuery
  def initialize(scope = Transaction.all)
    @scope = scope
  end

  private

  def active(scope)
    scope.joins(<<-SQL
      LEFT OUTER JOIN source ON transactions.source_id = source.id
      AND source.accepted_at IS NOT NULL
    SQL
  end

  def visible(scope)
    scope.where(visible: true)
  end

  def for_publishers(scope)
    scope.select("transactions.*")
         .joins(:account)
         .where("accounts.owner_type = 'Publisher'")
         .joins("JOIN publishers ON owner_id = publishers.id")
  konec
konec

Stále nám chybí ta nejdůležitější část, tj. shromáždění dat do jednoho řetězce a zveřejnění rozhraní. Metoda, ve které vše slepíme dohromady, se bude jmenovat "volání".

Důležité je, že zde použijeme instanční proměnnou @scope, ve které se nachází obor našeho volání.

třída TransactionsQuery
  ...
  def call
    visible(@scope)
        .then(&method(:active))
        .then(&method(:for_publishers))
        .order(:created_at)
  end

  private
  ...
end

Celá třída se prezentuje takto:

třída TransactionsQuery
  def initialize(scope = Transaction.all)
    @scope = scope
  end

  def call
    visible(@scope)
        .then(&method(:active))
        .then(&method(:for_publishers))
        .order(:created_at)
  end

  private

  def active(scope)
    scope.joins(<<-SQL
      LEFT OUTER JOIN source ON transactions.source_id = source.id
      AND source.accepted_at IS NOT NULL
    SQL
  end

  def visible(scope)
    scope.where(visible: true)
  end

  def for_publishers(scope)
    scope.select("transactions.*")
         .joins(:account)
         .where("accounts.owner_type = 'Publisher'")
         .joins("JOIN publishers ON owner_id = publishers.id")
  konec
konec

Po našem vyčištění vypadá model rozhodně lehčí. V něm se zaměříme pouze na validaci dat a vztahy mezi ostatními modely.

třída Transaction < ActiveRecord::Base
  belongs_to :account
  has_one :withdrawal_item
end

Kontrolér již implementoval naše řešení; všechny další dotazy jsme přesunuli do samostatné třídy. Nevyřešeným problémem však zůstávají volání, která jsme v modelu neměli. Po několika změnách vypadá naše akce indexu takto:

třída TransactionsController < ApplicationController
  def index
    @transactions = TransactionsQuery.new
                                     .call
                                     .joins("LEFT JOIN withdrawal_items ON withdrawal_items.accounting_event_id = transactions.id")
                                     .joins("LEFT JOIN withdrawals ON withdrawalals.id = withdrawal_items.withdrawal_id OR
 (withdrawals.id = source.resource_id AND source.resource_type = 'Withdrawal')")
                                     .order(:created_at)
                                     .page(params[:page])
                                     .per(params[:page])
    @transactions = apply_filters(@transactions)
  konec
end

Řešení

V případě zavádění osvědčených postupů a konvencí může být dobrým nápadem nahradit všechny podobné výskyty daného problému. Proto přesuneme SQL dotaz z indexové akce do samostatného objektu dotazu. Ten budeme nazývat TransactionsFilterableQuery třída. Sloh, kterým třídu připravujeme, bude podobný tomu, který je prezentován v přednášce TransactionsQuery. V rámci kód změny, intuitivnější záznam velkých dotazů SQL bude propašován pomocí víceřádkových řetězců znaků tzv. heredoc. Dostupné řešení najdete níže:

třída TransactionsFilterableQuery
  def initialize(scope = Transaction.all)
    @scope = scope
  end

  def call
    withdrawal(@scope).then(&method(:withdrawal_items))
  end

  private

  def stažení(scope)
    scope.joins(<<-SQL
      LEFT JOIN withdrawals ON withdrawals.id = withdrawal_items.withdrawal_id NEBO
      (withdrawals.id = source.resource_id AND source.resource_type = 'Withdrawal')
    SQL
  konec

  def withdrawal_items(scope)
    scope.joins(<<-SQL
      LEFT JOIN withdrawal_items ON withdrawal_items.accounting_event_id = transactions.id
    SQL
  end
end

V případě změn v řadiči zmenšíme množství řádků přidáním objektu dotazu. Důležité je, abychom oddělili vše kromě části zodpovědné za stránkování.

třída TransactionsController < ApplicationController
  def index
    @transactions = TransactionsQuery.new.call.then do |scope|
      TransactionsFilterableQuery.new(scope).call
    end.page(params[:page]).per(params[:page])

    @transactions = apply_filters(@transactions)
  end
konec

Souhrn

Objekt dotazu hodně mění přístup k psaní dotazů SQL. V ActiveRecordu je velmi snadné umístit veškerou obchodní a databázovou logiku do modelu, protože vše je na jednom místě. To bude docela dobře fungovat u menších aplikací. S rostoucí složitostí projektu nastavíme logiku na jiná místa. Stejný objekt dotazu umožňuje seskupovat členské dotazy do určitého problému.

Díky tomu máme snadnou možnost pozdějšího dědění kódu a díky kachnímu typování lze tato řešení použít i v jiných modelech. Nevýhodou tohoto řešení je větší množství kódu a roztříštěnost odpovědnosti. Nicméně to, zda se do takové výzvy chceme pustit, nebo ne, záleží na nás a jak moc nám vadí tlusté modelky.

Související články

Ilustrace zdravotnické aplikace pro chytré telefony s ikonou srdce a rostoucím zdravotním grafem, označená logem The Codest, která představuje digitální zdraví a řešení HealthTech.
Vývoj softwaru

Softwarové vybavení pro zdravotnictví: a případy použití

Nástroje, na které se dnes zdravotnické organizace spoléhají, se v ničem nepodobají papírovým kartám z doby před desítkami let. zdravotnický software dnes podporuje zdravotnické systémy, péči o pacienty a moderní poskytování zdravotní péče v klinických a...

NEJKRÁSNĚJŠÍ
Abstraktní ilustrace klesajícího sloupcového grafu se stoupající šipkou a zlatou mincí symbolizující efektivitu nákladů nebo úspory. V levém horním rohu se zobrazuje logo The Codest se sloganem "In Code We Trust" na světle šedém pozadí.
Vývoj softwaru

Jak rozšířit tým vývojářů bez ztráty kvality produktu

Zvětšujete svůj vývojový tým? Zjistěte, jak růst, aniž byste museli obětovat kvalitu produktu. Tento průvodce se zabývá příznaky, že je čas na škálování, strukturou týmu, najímáním zaměstnanců, vedením a nástroji - a také tím, jak může The Codest...

NEJKRÁSNĚJŠÍ
Vývoj softwaru

Vytváření webových aplikací odolných vůči budoucnosti: postřehy týmu odborníků The Codest

Zjistěte, jak společnost The Codest vyniká při vytváření škálovatelných, interaktivních webových aplikací pomocí nejmodernějších technologií, které poskytují bezproblémové uživatelské prostředí na všech platformách. Zjistěte, jak naše odborné znalosti podporují digitální transformaci a obchodní...

NEJKRÁSNĚJŠÍ
Vývoj softwaru

10 nejlepších lotyšských společností zabývajících se vývojem softwaru

V našem nejnovějším článku se dozvíte o nejlepších lotyšských společnostech zabývajících se vývojem softwaru a jejich inovativních řešeních. Zjistěte, jak mohou tito technologičtí lídři pomoci pozvednout vaše podnikání.

thecodest
Podniková a škálovací řešení

Základy vývoje softwaru v jazyce Java: A Guide to Outsourcing Successfully

Prozkoumejte tuto základní příručku o úspěšném vývoji softwaru outsourcing Java, abyste zvýšili efektivitu, získali přístup k odborným znalostem a dosáhli úspěchu projektu s The Codest.

thecodest

Přihlaste se k odběru naší znalostní databáze a získejte aktuální informace o odborných znalostech z oblasti IT.

    O nás

    The Codest - Mezinárodní společnost zabývající se vývojem softwaru s technologickými centry v Polsku.

    Spojené království - ústředí

    • Kancelář 303B, 182-184 High Street North E6 2JA
      Londýn, Anglie

    Polsko - Místní technologická centra

    • Kancelářský park Fabryczna, Aleja
      Pokoju 18, 31-564 Krakov
    • Brain Embassy, Konstruktorska
      11, 02-673 Varšava, Polsko

      The Codest

    • Home
    • O nás
    • Služby
    • Case Studies
    • Vědět jak
    • Kariéra
    • Slovník

      Služby

    • To Advisory
    • Vývoj softwaru
    • Vývoj backendu
    • Vývoj frontendů
    • Staff Augmentation
    • Vývojáři backendu
    • Cloudoví inženýři
    • Datoví inženýři
    • Další
    • Inženýři QA

      Zdroje

    • Fakta a mýty o spolupráci s externím partnerem pro vývoj softwaru
    • Z USA do Evropy: Proč se americké startupy rozhodly přesídlit do Evropy?
    • Srovnání technických vývojových center v zahraničí: Tech Offshore Evropa (Polsko), ASEAN (Filipíny), Eurasie (Turecko)
    • Jaké jsou hlavní výzvy CTO a CIO?
    • The Codest
    • The Codest
    • The Codest
    • Privacy policy
    • Website terms of use

    Copyright © 2026 by The Codest. Všechna práva vyhrazena.

    cs_CZCzech
    en_USEnglish de_DEGerman sv_SESwedish da_DKDanish nb_NONorwegian fiFinnish fr_FRFrench pl_PLPolish arArabic it_ITItalian jaJapanese es_ESSpanish nl_NLDutch etEstonian elGreek pt_PTPortuguese cs_CZCzech