window.pipedriveLeadboosterConfig = { base: 'leadbooster-chat.pipedrive.com', companyId: 11580370, playbookUuid: '22236db1-6d50-40c4-b48f-8b11262155be', version: 2, } ;(function () { var w = Fenster if (w.LeadBooster) { console.warn('LeadBooster existiert bereits') } else { w.LeadBooster = { q: [], on: function (n, h) { this.q.push({ t: 'o', n: n, h: h }) }, trigger: function (n) { this.q.push({ t: 't', n: n }) }, } } })() Optimierung von Code mit Abfrageobjekten - The Codest
Der Codest
  • Über uns
  • Dienstleistungen
    • Software-Entwicklung
      • Frontend-Softwareentwicklung
      • Backend-Softwareentwicklung
    • Staff Augmentation
      • Frontend-Entwickler
      • Backend-Entwickler
      • Daten-Ingenieure
      • Cloud-Ingenieure
      • QS-Ingenieure
      • Andere
    • IT-Beratung
      • Prüfung und Beratung
  • Branchen
    • Fintech & Bankwesen
    • E-commerce
    • Adtech
    • Gesundheitstechnik
    • Herstellung
    • Logistik
    • Automobilindustrie
    • IOT
  • Wert für
    • CEO
    • CTO
    • Delivery Manager
  • Unser Team
  • Fallstudien
  • Gewusst wie
    • Blog
    • Begegnungen
    • Webinare
    • Ressourcen
Karriere Kontakt aufnehmen
  • Über uns
  • Dienstleistungen
    • Software-Entwicklung
      • Frontend-Softwareentwicklung
      • Backend-Softwareentwicklung
    • Staff Augmentation
      • Frontend-Entwickler
      • Backend-Entwickler
      • Daten-Ingenieure
      • Cloud-Ingenieure
      • QS-Ingenieure
      • Andere
    • IT-Beratung
      • Prüfung und Beratung
  • Wert für
    • CEO
    • CTO
    • Delivery Manager
  • Unser Team
  • Fallstudien
  • Gewusst wie
    • Blog
    • Begegnungen
    • Webinare
    • Ressourcen
Karriere Kontakt aufnehmen
Pfeil zurück ZURÜCK
2019-03-08
Software-Entwicklung

Optimierung des Codes mit Query Objects

Der Codest

Tomasz Szkaradek

Architekt für Entwicklung

Es ist sehr wahrscheinlich, dass Sie bei Ihrer Arbeit schon oft mit überladenen Modellen und einer großen Anzahl von Aufrufen in den Controllern zu tun hatten. Basierend auf dem Wissen in der Rails-Umgebung, in diesem Artikel, werde ich eine einfache Lösung für dieses Problem vorschlagen.

Ein sehr wichtiger Aspekt der Rails-Anwendung ist es, die Anzahl der redundanten Abhängigkeiten zu minimieren, weshalb die gesamte Rails-Umgebung in letzter Zeit den Service-Objekt-Ansatz und die Verwendung der PORO-Methode (Pure Old Ruby Object) fördert. Eine Beschreibung, wie man eine solche Lösung einsetzt, finden Sie hier. In diesem Artikel werden wir das Konzept Schritt für Schritt lösen und es an das Problem anpassen.

Problem

In einer hypothetischen Anwendung haben wir es mit einem komplizierten Transaktionssystem zu tun. Unser Modell, das jede Transaktion repräsentiert, hat eine Reihe von Bereichen, die Ihnen helfen, Daten zu erhalten. Das ist eine große Arbeitserleichterung, da sie an einer Stelle zu finden sind. Dies gilt jedoch nicht für lange Zeit. Mit der Entwicklung der Anwendung werden die Projekt wird immer komplizierter. Die Bereiche haben keine einfachen "Wo"-Verweise mehr, es fehlen Daten und wir beginnen, Beziehungen zu laden. Nach einer Weile erinnert es an ein kompliziertes System von Spiegeln. Und, was noch schlimmer ist, wir wissen nicht, wie man ein mehrzeiliges Lambda erstellt!

Im Folgenden finden Sie ein bereits erweitertes Anwendungsmodell. Die Transaktionen des Zahlungssystems werden in gespeichert. Wie Sie im Beispiel unten sehen können:

Klasse Transaktion  { 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

Das Modell ist eine Sache, aber wenn der Umfang unseres Projekts zunimmt, werden auch die Controller immer größer. Schauen wir uns das folgende Beispiel an:

class TransactionsController < AnwendungsController
  def index
    @transactions = Transaction.for_publishers
                                   .aktiv
                                   .visible
                                   .joins("LEFT JOIN Abhebungen_Einträge ON Abhebungen_Einträge.transaction_id = Transaktionen.id")
                                   .joins("LEFT JOIN Abhebungen ON Abhebungen.id = Abhebungen_Einzelheiten.Abhebungs_id OR
 (abhebungen.id = quelle.ressource_id AND quelle.ressource_type = 'Abhebung')")
                                   .order(:created_at)
                                   .page(params[:page])
                                   .per(params[:page])
    @Transaktionen = apply_filters(@Transaktionen)
  end
end

Hier sehen wir viele Zeilen verketteter Methoden mit zusätzlichen Verknüpfungen, die wir nicht an vielen Stellen durchführen wollen, sondern nur an dieser einen. Die angehängten Daten werden später von der apply_filters-Methode verwendet, die auf der Grundlage der GET-Parameter die entsprechende Datenfilterung vornimmt. Natürlich können wir einige dieser Verweise in den Anwendungsbereich verlagern, aber ist das nicht das Problem, das wir eigentlich zu lösen versuchen?

Lösung

Da wir bereits wissen, dass wir ein Problem haben, müssen wir dieses lösen. Basierend auf dem Hinweis in der Einleitung werden wir hier den PORO-Ansatz verwenden. In diesem konkreten Fall wird dieser Ansatz als Abfrageobjekt bezeichnet, das eine Weiterentwicklung des Konzepts der Serviceobjekte ist.

Legen wir ein neues Verzeichnis mit dem Namen "services" an, das sich im apps-Verzeichnis unseres Projekts befindet. Dort werden wir eine Klasse namens TransactionsQuery.

Klasse TransactionsQuery
end

Als nächsten Schritt müssen wir einen Initialisierer erstellen, in dem ein Standardaufrufpfad für unser Objekt erstellt wird

class TransactionsQuery
  def initialize(bereich = Transaction.all)
    @scope = Bereich
  end
end

Dadurch können wir die Beziehung vom aktiven Datensatz zu unserer Einrichtung übertragen. Jetzt können wir alle unsere Bereiche in die Klasse übertragen, die nur in dem vorgestellten Controller benötigt werden.

class TransactionsQuery
  def initialize(bereich = Transaction.all)
    @scope = Bereich
  end

  privat

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

  def visible(Bereich)
    Bereich.where(sichtbar: wahr)
  end

  def for_publishers(bereich)
    Bereich.select("Transaktionen.*")
         .joins(:Konto)
         .where("konto.eigentümer_typ = 'Herausgeber'")
         .joins("JOIN verleger ON eigentümer_id = verleger.id")
  end
end

Es fehlt noch der wichtigste Teil, nämlich das Sammeln von Daten in einer Zeichenkette und die Veröffentlichung der Schnittstelle. Die Methode, mit der wir alles zusammenfügen, wird "Aufruf" genannt.

Wirklich wichtig ist, dass wir dort die Instanzvariable @scope verwenden, in der sich der Bereich unseres Aufrufs befindet.

Klasse TransactionsQuery
  ...
  def call
    visible(@scope)
        .then(&Methode(:aktiv))
        .then(&Methode(:for_publishers))
        .order(:created_at)
  end

  privat
  ...
end

Die gesamte Klasse stellt sich wie folgt dar:

class TransactionsQuery
  def initialize(bereich = Transaction.all)
    @scope = Bereich
  end

  def Aufruf
    visible(@scope)
        .then(&Methode(:aktiv))
        .then(&Methode(:for_publishers))
        .order(:created_at)
  end

  privat

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

  def visible(Bereich)
    Bereich.where(sichtbar: wahr)
  end

  def for_publishers(bereich)
    Bereich.select("Transaktionen.*")
         .joins(:Konto)
         .where("konto.eigentümer_typ = 'Herausgeber'")
         .joins("JOIN verleger ON eigentümer_id = verleger.id")
  end
end

Nach unserer Bereinigung sieht das Modell deutlich heller aus. Hier konzentrieren wir uns nur noch auf die Datenvalidierung und die Beziehungen zwischen anderen Modellen.

Klasse Transaktion < ActiveRecord::Base
  gehört_zu :Konto
  hat_einen :Abhebungsposten
end

Der Controller hat unsere Lösung bereits implementiert; wir haben alle zusätzlichen Abfragen in eine separate Klasse verschoben. Allerdings bleiben die Aufrufe, die wir nicht im Modell hatten, ein ungelöstes Problem. Nach einigen Änderungen sieht unsere Indexaktion wie folgt aus:

class TransactionsController < AnwendungsController
  def index
    @transactions = TransactionsQuery.new
                                     .call
                                     .joins("LEFT JOIN Abhebung_Einträge ON Abhebung_Einträge.Abrechnungs_Ereignis_id = Transaktionen.id")
                                     .joins("LEFT JOIN Abhebungen ON abhebungen.id = abhebungen_positionen.abhebungs_id OR
 (abhebungen.id = quelle.ressource_id AND quelle.ressource_type = 'abhebung')")
                                     .order(:created_at)
                                     .page(params[:page])
                                     .per(params[:page])
    @Transaktionen = apply_filters(@Transaktionen)
  end
end

Lösung

Bei der Umsetzung guter Praktiken und Konventionen kann es eine gute Idee sein, alle ähnlichen Vorkommen eines bestimmten Problems zu ersetzen. Daher werden wir die SQL-Abfrage von der Indexaktion in das separate Abfrageobjekt verschieben. Wir nennen dies ein TransactionsFilterableQuery Klasse. Der Stil, in dem wir den Unterricht vorbereiten, wird ähnlich sein wie der, der in TransactionsQuery. Als Teil der Code ändert, wird eine intuitivere Aufzeichnung großer SQL-Abfragen unter Verwendung mehrzeiliger Zeichenketten namens heredoc. Die verfügbare Lösung finden Sie unten:

class TransactionsFilterableQuery
  def initialize(scope = Transaction.all)
    @scope = Bereich
  end

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

  privat

  def entnahme(bereich)
    bereich.joins(<<-SQL
      LEFT JOIN Abhebungen ON abhebungen.id = abhebungen_items.abhebungen_id OR
      (entnahmen.id = quelle.ressource_id AND quelle.ressource_type = 'entnahme')
    SQL
  end

  def entnahme_artikel(bereich)
    bereich.joins(<<-SQL
      LEFT JOIN abhebung_positionen ON abhebung_positionen.abrechnung_ereignis_id = vorgänge.id
    SQL
  end
end

Bei Änderungen in der Steuerung reduzieren wir die Masse der Zeilen, indem wir das Abfrageobjekt hinzufügen. Es ist wichtig, dass wir alles außer dem Teil, der für die Paginierung verantwortlich ist, trennen.

class TransactionsController < AnwendungsController
  def index
    @transactions = TransactionsQuery.new.call.then do |scope|
      TransactionsFilterableQuery.new(Geltungsbereich).call
    end.page(params[:page]).per(params[:page])

    @Transaktionen = apply_filters(@Transaktionen)
  end
end

Zusammenfassung

Das Query-Objekt ändert viel an der Vorgehensweise beim Schreiben von SQL-Abfragen. In ActiveRecord ist es sehr einfach, die gesamte Geschäfts- und Datenbanklogik im Modell zu platzieren, da sich alles an einem Ort befindet. Dies ist für kleinere Anwendungen sehr gut geeignet. Mit zunehmender Komplexität des Projekts verlagern wir die Logik an andere Stellen. Mit dem gleichen Abfrageobjekt können Sie Abfragen von Mitgliedern zu einem bestimmten Problem gruppieren.

Dadurch haben wir eine einfache Möglichkeit der späteren Vererbung des Codes und aufgrund der Duck-Typisierung können Sie diese Lösungen auch in anderen Modellen verwenden. Der Nachteil dieser Lösung ist eine größere Menge an Code und eine Fragmentierung der Verantwortung. Ob wir eine solche Herausforderung annehmen wollen oder nicht, hängt jedoch von uns selbst ab und davon, wie sehr wir uns an fetten Modellen stören.

Ähnliche Artikel

Software-Entwicklung

Zukunftssichere Web-Apps bauen: Einblicke vom The Codest-Expertenteam

Entdecken Sie, wie sich The Codest bei der Erstellung skalierbarer, interaktiver Webanwendungen mit Spitzentechnologien auszeichnet, die nahtlose Benutzererfahrungen auf allen Plattformen bieten. Erfahren Sie, wie unsere Expertise die digitale Transformation und...

DAS SCHÖNSTE
Software-Entwicklung

Top 10 Softwareentwicklungsunternehmen in Lettland

Erfahren Sie in unserem neuesten Artikel mehr über die besten Softwareentwicklungsunternehmen Lettlands und ihre innovativen Lösungen. Entdecken Sie, wie diese Technologieführer Ihr Unternehmen voranbringen können.

thecodest
Enterprise & Scaleups Lösungen

Grundlagen der Java-Softwareentwicklung: Ein Leitfaden für erfolgreiches Outsourcing

Entdecken Sie diesen wichtigen Leitfaden zum erfolgreichen Outsourcing der Java-Softwareentwicklung, um die Effizienz zu steigern, auf Fachwissen zuzugreifen und den Projekterfolg mit The Codest voranzutreiben.

thecodest
Software-Entwicklung

Der ultimative Leitfaden für Outsourcing in Polen

Der Anstieg des Outsourcings in Polen wird durch wirtschaftliche, bildungspolitische und technologische Fortschritte angetrieben, die das IT-Wachstum und ein unternehmensfreundliches Klima fördern.

TheCodest
Enterprise & Scaleups Lösungen

Der vollständige Leitfaden für IT-Audit-Tools und -Techniken

IT-Audits gewährleisten sichere, effiziente und gesetzeskonforme Systeme. Erfahren Sie mehr über ihre Bedeutung, indem Sie den vollständigen Artikel lesen.

Der Codest
Jakub Jakubowicz CTO & Mitbegründer

Abonnieren Sie unsere Wissensdatenbank und bleiben Sie auf dem Laufenden über das Fachwissen aus dem IT-Sektor.

    Über uns

    The Codest - Internationales Software-Unternehmen mit technischen Zentren in Polen.

    Vereinigtes Königreich - Hauptsitz

    • Büro 303B, 182-184 High Street North E6 2JA
      London, England

    Polen - Lokale Tech-Hubs

    • Fabryczna Office Park, Aleja
      Pokoju 18, 31-564 Kraków
    • Brain Embassy, Konstruktorska
      11, 02-673 Warszawa, Polen

      Der Codest

    • Startseite
    • Über uns
    • Dienstleistungen
    • Fallstudien
    • Gewusst wie
    • Karriere
    • Wörterbuch

      Dienstleistungen

    • IT-Beratung
    • Software-Entwicklung
    • Backend-Softwareentwicklung
    • Frontend-Softwareentwicklung
    • Staff Augmentation
    • Backend-Entwickler
    • Cloud-Ingenieure
    • Daten-Ingenieure
    • Andere
    • QS-Ingenieure

      Ressourcen

    • Fakten und Mythen über die Zusammenarbeit mit einem externen Softwareentwicklungspartner
    • Aus den USA nach Europa: Warum entscheiden sich amerikanische Start-ups für eine Verlagerung nach Europa?
    • Tech Offshore Development Hubs im Vergleich: Tech Offshore Europa (Polen), ASEAN (Philippinen), Eurasien (Türkei)
    • Was sind die größten Herausforderungen für CTOs und CIOs?
    • Der Codest
    • Der Codest
    • Der Codest
    • Privacy policy
    • Website terms of use

    Urheberrecht © 2025 von The Codest. Alle Rechte vorbehalten.

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