ActiveRecord versus Ecto deel twee

Dit is het tweede deel van de serie "ActiveRecord vs. Ecto", waarin Batman en Batgirl vechten om databases te doorzoeken en we appels met peren vergelijken.

Na bestudering van databaseschema's en migraties in deel 1 van ActiveRecord versus Ecto, gaat dit bericht in op hoe zowel ActiveRecord als Ecto ontwikkelaars in staat stellen de database te doorzoeken, en hoe zowel ActiveRecord als Ecto met elkaar vergelijken wanneer ze met dezelfde vereisten omgaan. Onderweg zullen we ook de identiteit van Batgirl uit 1989–2011 ontdekken.

Seed data

Laten we beginnen! Op basis van de databasestructuur die is gedefinieerd in de eerste post van deze serie, neemt u aan dat de gebruikers en de factuurtabellen de volgende gegevens hebben opgeslagen:

gebruikers

* Het veld create_at van ActiveRecord heet standaard insert_at in Ecto.

facturen

* Het veld Created_at van ActiveRecord heet standaard insert_at in Ecto.

Bij zoekopdrachten die via dit bericht worden uitgevoerd, wordt ervan uitgegaan dat de bovenstaande gegevens in de database zijn opgeslagen, dus houd deze informatie in gedachten tijdens het lezen.

Zoek item met behulp van de primaire sleutel

Laten we beginnen met het ophalen van een record uit de database met behulp van de primaire sleutel.

ActiveRecord

irb (main): 001: 0> User.find (1) User Load (0.4ms) SELECT "gebruikers". * FROM "gebruikers" WAAR "gebruikers". "id" = $ 1 LIMIT $ 2 [["id", 1 ], ["LIMIT", 1]] => # 

ecto

iex (3)> Repo.get (Gebruiker, 1)
[debug] QUERY OK source = "users" db = 5.2ms decode = 2.5ms wachtrij = 0.1ms
SELECT u0. "Id", u0. "Volledige naam", u0. "Email", u0. "Insert_at", u0. "Updated_at" VAN "gebruikers" ALS u0 WAAR (u0. "Id" = $ 1) [1]
% Financex.Accounts.User {
  __meta__: # Ecto.Schema.Metadata <: geladen, "gebruikers">,
  e-mail: "bette@kane.test",
  volledige_naam: "Bette Kane",
  id: 1,
  insert_at: ~ N [2018-01-01 10: 01: 00.000000],
  facturen: # Ecto.Association.NotLoaded ,
  updated_at: ~ N [2018-01-01 10: 01: 00.000000]
}

Vergelijking

Beide gevallen zijn vrij gelijkaardig. ActiveRecord vertrouwt op de methode van de zoekklasse van de klasse Gebruikersmodel. Het betekent dat elke ActiveRecord-kindklasse een eigen vindmethode bevat.

Ecto gebruikt een andere aanpak, waarbij het vertrouwt op het Repository-concept als bemiddelaar tussen de kaartlaag en het domein. Bij het gebruik van Ecto weet de gebruikersmodule niet hoe hij zichzelf kan vinden. Een dergelijke verantwoordelijkheid is aanwezig in de Repo-module, die deze kan toewijzen aan de onderliggende datastore, in ons geval Postgres.

Bij het vergelijken van de SQL-query zelf, kunnen we een paar verschillen zien:

  • ActiveRecord laadt alle velden (gebruikers. *), Terwijl Ecto alleen de velden laadt die in de schemadefinitie worden vermeld.
  • ActiveRecord bevat een LIMIT 1 voor de zoekopdracht, terwijl Ecto dat niet doet.

Alle items ophalen

Laten we een stap verder gaan en alle gebruikers uit de database laden.

ActiveRecord

irb (main): 001: 0> User.all User Load (0,5ms) SELECT "users". * FROM "users" LIMIT $ 1 [["LIMIT", 11]] => # , # , # , # ]>

ecto

iex (4)> Repo.all (Gebruiker)
[debug] QUERY OK source = "users" db = 2.8ms decode = 0.2ms wachtrij = 0.2ms
SELECT u0. "Id", u0. "Volledige naam", u0. "Email", u0. "Insert_at", u0. "Updated_at" VAN "gebruikers" ALS u0 []
[
  % Financex.Accounts.User {
    __meta__: # Ecto.Schema.Metadata <: geladen, "gebruikers">,
    e-mail: "bette@kane.test",
    volledige_naam: "Bette Kane",
    id: 1,
    insert_at: ~ N [2018-01-01 10: 01: 00.000000],
    facturen: # Ecto.Association.NotLoaded ,
    updated_at: ~ N [2018-01-01 10: 01: 00.000000]
  },
  % Financex.Accounts.User {
    __meta__: # Ecto.Schema.Metadata <: geladen, "gebruikers">,
    e-mail: "barbara@gordon.test",
    volledige_naam: "Barbara Gordon",
    id: 2,
    insert_at: ~ N [2018-01-02 10: 02: 00.000000],
    facturen: # Ecto.Association.NotLoaded ,
    updated_at: ~ N [2018-01-02 10: 02: 00.000000]
  },
  % Financex.Accounts.User {
    __meta__: # Ecto.Schema.Metadata <: geladen, "gebruikers">,
    e-mail: "cassandra@cain.test",
    volledige_naam: "Cassandra Cain",
    id: 3,
    insert_at: ~ N [2018-01-03 10: 03: 00.000000],
    facturen: # Ecto.Association.NotLoaded ,
    updated_at: ~ N [03-01-2018 10: 03: 00.000000]
  },
  % Financex.Accounts.User {
    __meta__: # Ecto.Schema.Metadata <: geladen, "gebruikers">,
    e-mail: "stephanie@brown.test",
    volledige_naam: "Stephanie Brown",
    id: 4,
    insert_at: ~ N [2018-01-04 10: 04: 00.000000],
    facturen: # Ecto.Association.NotLoaded ,
    updated_at: ~ N [2018-01-04 10: 04: 00.000000]
  }
]

Vergelijking

Het volgt exact hetzelfde patroon als het vorige gedeelte. ActiveRecord gebruikt de all class-methode en Ecto vertrouwt op het repository-patroon om de records te laden.

Er zijn weer enkele verschillen in de SQL-query's:

Vragen met voorwaarden

Het is zeer onwaarschijnlijk dat we alle records uit een tabel moeten halen. Een veel voorkomende behoefte is het gebruik van voorwaarden om de geretourneerde gegevens uit te filteren.

Laten we dat voorbeeld gebruiken om een ​​lijst weer te geven van alle facturen die nog moeten worden betaald (WAAR IS betaald NULL).

ActiveRecord

irb (main): 024: 0> Invoice.where (betaald_at: nihil) Factuurbelasting (18,2 ms) SELECTEER "facturen". * UIT "facturen" WAAR "facturen". "betaald_at" IS NULL LIMIET $ 1 [["LIMIT" , 11]] => # , # ]>

ecto

iex (19)> waar (factuur, [i], is_nil (i.paid_at)) |> Repo.all ()
[debug] QUERY OK source = "invoices" db = 20.2ms
SELECT i0. "Id", i0. "Payment_method", i0. "Paid_at", i0. "User_id", i0. "Insert_at", i0. "Updated_at" VAN "facturen" ALS i0 WAAR (i0. "Betaald_at" IS NUL) []
[
  % Financex.Accounts.Invoice {
    __meta__: # Ecto.Schema.Metadata <: geladen, "facturen">,
    id: 3,
    insert_at: ~ N [2018-01-04 08: 00: 00.000000],
    paid_at: nihil,
    payment_method: nihil,
    updated_at: ~ N [2018-01-04 08: 00: 00.000000],
    gebruiker: # Ecto.Association.NotLoaded ,
    user_id: 3
  },
  % Financex.Accounts.Invoice {
    __meta__: # Ecto.Schema.Metadata <: geladen, "facturen">,
    id: 4,
    insert_at: ~ N [2018-01-04 08: 00: 00.000000],
    paid_at: nihil,
    payment_method: nihil,
    updated_at: ~ N [2018-01-04 08: 00: 00.000000],
    gebruiker: # Ecto.Association.NotLoaded ,
    user_id: 4
  }
]

Vergelijking

In beide voorbeelden wordt het trefwoord Where gebruikt, wat een verbinding is met de SQL WHERE-component. Hoewel de gegenereerde SQL-query's erg op elkaar lijken, heeft de manier waarop beide tools daar komen een aantal belangrijke verschillen.

ActiveRecord transformeert het argument pay_at: nil automatisch naar de betaald_at IS NULL SQL-instructie. Om met Ecto tot dezelfde output te komen, moeten ontwikkelaars explicieter zijn over hun intentie door de is_nil () aan te roepen.

Een ander verschil dat moet worden benadrukt, is het "pure" gedrag van de functie in Ecto. Wanneer u alleen de Where-functie aanroept, heeft deze geen interactie met de database. De terugkeer van de Where-functie is een Ecto.Query struct:

iex (20)> waar (factuur, [i], is_nil (i.paid_at))
# Ecto.Query 

De database wordt alleen aangeraakt wanneer de functie Repo.all () wordt aangeroepen, waarbij de struct Ecto.Query als argument wordt doorgegeven. Met deze benadering is het mogelijk om de zoekopdracht in Ecto samen te stellen, het onderwerp van de volgende sectie.

Zoekopdracht samenstelling

Een van de krachtigste aspecten van databasequery's is de samenstelling. Het beschrijft een zoekopdracht op een manier die meer dan één voorwaarde bevat.

Als u onbewerkte SQL-query's maakt, betekent dit dat u waarschijnlijk een soort aaneenschakeling gebruikt. Stel je voor dat je twee voorwaarden hebt:

  1. not_paid = 'betaald_at IS NIET NULL'
  2. paid_with_paypal = 'payment_method = "Paypal"'

Om deze twee voorwaarden met ruwe SQL te combineren, betekent dit dat u ze moet samenvoegen met iets dat lijkt op:

SELECTEER * UIT facturen WAAR # {not_paid} EN # {paid_with_paypal}

Gelukkig hebben zowel ActiveRecord als Ecto daar een oplossing voor.

ActiveRecord

irb (main): 003: 0> Invoice.where.not (paid_at: nil) .where (payment_method: "Paypal") Factuurbelasting (8.0ms) SELECT "facturen". * VAN "facturen" WAAR "facturen". " paid_at "IS NIET NULL EN" facturen "." payment_method "= $ 1 LIMIT $ 2 [[" payment_method "," Paypal "], [" LIMIT ", 11]] => # ]>

ecto

iex (6)> Factuur |> waar ([i], niet is_nil (i.paid_at)) |> waar ([i], i.payment_method == "Paypal") |> Repo.all ()
[debug] QUERY OK source = "facturen" db = 30.0ms decoderen = 0.6ms wachtrij = 0.2ms
SELECT i0. "Id", i0. "Payment_method", i0. "Paid_at", i0. "User_id", i0. "Insert_at", i0. "Updated_at" VAN "facturen" ALS i0 WAAR (NIET (i0. "Betaald_at "IS NULL)) EN (i0." Payment_method "= 'Paypal') []
[
  % Financex.Accounts.Invoice {
    __meta__: # Ecto.Schema.Metadata <: geladen, "facturen">,
    id: 2,
    insert_at: ~ N [2018-01-03 08: 00: 00.000000],
    paid_at: #DateTime <2018-02-01 08: 00: 00.000000Z>,
    payment_method: "Paypal",
    updated_at: ~ N [2018-01-03 08: 00: 00.000000],
    gebruiker: # Ecto.Association.NotLoaded ,
    user_id: 2
  }
]

Vergelijking

Beide vragen beantwoorden dezelfde vraag: "Welke facturen zijn betaald en gebruikt Paypal?".

Zoals reeds verwacht, biedt ActiveRecord een meer beknopte manier om de query samen te stellen (voor dat voorbeeld), terwijl Ecto van ontwikkelaars verlangt dat ze een beetje meer besteden aan het schrijven van de query. Zoals gebruikelijk is Batgirl (de wees, stomme met de identiteit van Cassandra Cain) of Activerecord niet zo uitgebreid.

Laat u niet misleiden door de uitgebreidheid en schijnbare complexiteit van de hierboven weergegeven Ecto-zoekopdracht. In een echte wereldomgeving zou die zoekopdracht worden herschreven om er meer uit te zien:

Factuur
|> waar ([i], niet is_nil (i.paid_at))
|> waar ([i], i.payment_method == "Paypal")
|> Repo.all ()

Vanuit die invalshoek gezien, maakt de combinatie van de "pure" aspecten van de functie, waarbij de databaseoperaties niet zelf worden uitgevoerd, met de pijpexploitant, de samenstelling van de query in Ecto echt schoon.

Bestellen

Bestellen is een belangrijk aspect van een zoekopdracht. Hiermee kunnen ontwikkelaars ervoor zorgen dat een bepaald zoekresultaat een bepaalde volgorde volgt.

ActiveRecord

irb (main): 002: 0> Invoice.order (created_at:: desc) Factuurbelasting (1,5 ms) SELECT "facturen". * VAN "facturen" BESTELLEN BIJ "facturen". "created_at" DESC LIMIT $ 1 [["LIMIT ", 11]] => # , # , # , # ]>

ecto

iex (6)> order_by (factuur, desc:: insert_at) |> Repo.all ()
[debug] QUERY OK source = "invoices" db = 19.8ms
SELECT i0. "Id", i0. "Payment_method", i0. "Paid_at", i0. "User_id", i0. "Insert_at", i0. "Updated_at" VAN "facturen" ALS i0 BESTELLEN DOOR i0. "Insert_at" DESC []
[
  % Financex.Accounts.Invoice {
    __meta__: # Ecto.Schema.Metadata <: geladen, "facturen">,
    id: 3,
    insert_at: ~ N [2018-01-04 08: 00: 00.000000],
    paid_at: nihil,
    payment_method: nihil,
    updated_at: ~ N [2018-01-04 08: 00: 00.000000],
    gebruiker: # Ecto.Association.NotLoaded ,
    user_id: 3
  },
  % Financex.Accounts.Invoice {
    __meta__: # Ecto.Schema.Metadata <: geladen, "facturen">,
    id: 4,
    insert_at: ~ N [2018-01-04 08: 00: 00.000000],
    paid_at: nihil,
    payment_method: nihil,
    updated_at: ~ N [2018-01-04 08: 00: 00.000000],
    gebruiker: # Ecto.Association.NotLoaded ,
    user_id: 4
  },
  % Financex.Accounts.Invoice {
    __meta__: # Ecto.Schema.Metadata <: geladen, "facturen">,
    id: 2,
    insert_at: ~ N [2018-01-03 08: 00: 00.000000],
    paid_at: #DateTime <2018-02-01 08: 00: 00.000000Z>,
    payment_method: "Paypal",
    updated_at: ~ N [2018-01-03 08: 00: 00.000000],
    gebruiker: # Ecto.Association.NotLoaded ,
    user_id: 2
  },
  % Financex.Accounts.Invoice {
    __meta__: # Ecto.Schema.Metadata <: geladen, "facturen">,
    id: 1,
    insert_at: ~ N [2018-01-02 08: 00: 00.000000],
    paid_at: #DateTime <2018-02-01 08: 00: 00.000000Z>,
    payment_method: "Credit Card",
    updated_at: ~ N [2018-01-02 08: 00: 00.000000],
    gebruiker: # Ecto.Association.NotLoaded ,
    user_id: 1
  }
]

Vergelijking

Bestelling toevoegen aan een zoekopdracht is eenvoudig in beide tools.

Hoewel het Ecto-voorbeeld een factuur gebruikt als eerste parameter, accepteert de functie order_by ook Ecto.Query structs, waardoor de functie order_by kan worden gebruikt in composities, zoals:

Factuur
|> waar ([i], niet is_nil (i.paid_at))
|> waar ([i], i.payment_method == "Paypal")
|> order_by (desc:: insert_at)
|> Repo.all ()

Het beperken

Wat zou een database zonder limiet zijn? Een ramp. Gelukkig helpen zowel ActiveRecord als Ecto het aantal geretourneerde records te beperken.

ActiveRecord

irb (main): 004: 0> Invoice.limit (2)
Factuurbelasting (0,2 ms) SELECT "facturen". * VANAF "facturen" LIMIT $ 1 [["LIMIT", 2]]
=> # , # ]>

ecto

iex (22)> limiet (factuur, 2) |> Repo.all ()
[debug] QUERY OK source = "invoices" db = 3.6ms
SELECT i0. "Id", i0. "Payment_method", i0. "Paid_at", i0. "User_id", i0. "Insert_at", i0. "Updated_at" VAN "facturen" ALS i0 LIMIET 2 []
[
  % Financex.Accounts.Invoice {
    __meta__: # Ecto.Schema.Metadata <: geladen, "facturen">,
    id: 1,
    insert_at: ~ N [2018-01-02 08: 00: 00.000000],
    paid_at: #DateTime <2018-02-01 08: 00: 00.000000Z>,
    payment_method: "Credit Card",
    updated_at: ~ N [2018-01-02 08: 00: 00.000000],
    gebruiker: # Ecto.Association.NotLoaded ,
    user_id: 1
  },
  % Financex.Accounts.Invoice {
    __meta__: # Ecto.Schema.Metadata <: geladen, "facturen">,
    id: 2,
    insert_at: ~ N [2018-01-03 08: 00: 00.000000],
    paid_at: #DateTime <2018-02-01 08: 00: 00.000000Z>,
    payment_method: "Paypal",
    updated_at: ~ N [2018-01-03 08: 00: 00.000000],
    gebruiker: # Ecto.Association.NotLoaded ,
    user_id: 2
  }
]

Vergelijking

Zowel ActiveRecord als Ecto hebben een manier om het aantal records te beperken dat door een query wordt geretourneerd.

De limiet van Ecto werkt op dezelfde manier als order_by en is geschikt voor zoekopdrachtcomposities.

verenigingen

ActiveRecord en Ecto hebben verschillende benaderingen als het gaat om hoe associaties worden behandeld.

ActiveRecord

In ActiveRecord kunt u elke koppeling gebruiken die in een model is gedefinieerd, zonder dat u daar iets speciaals aan hoeft te doen, bijvoorbeeld:

irb (main): 012: 0> user = User.find (2) User Load (0.3ms) SELECT "users". * FROM "users" WHERE "users". "id" = $ 1 LIMIT $ 2 [["id" , 2], ["LIMIT", 1]] => #  irb (main): 013: 0> user.invoices Factuur laden (0.4ms) SELECT" facturen ". * VAN" facturen "WAAR" facturen " . "user_id" = $ 1 LIMIT $ 2 [["user_id", 2], ["LIMIT", 11]] => # ] >

Het bovenstaande voorbeeld laat zien dat we een lijst met gebruikersfacturen kunnen krijgen als we user.invoices bellen. Daarbij heeft ActiveRecord automatisch de database opgevraagd en de facturen geladen die aan de gebruiker zijn gekoppeld. Hoewel deze aanpak het gemakkelijker maakt, in de zin van minder code schrijven of je zorgen te maken over extra stappen, kan het een probleem zijn als je meerdere gebruikers doorloopt en de facturen voor elke gebruiker ophaalt. Dit probleem staat bekend als het "N + 1-probleem".

In ActiveRecord is de voorgestelde oplossing voor het "N + 1-probleem" om de methode include te gebruiken:

irb (main): 022: 0> user = User.includes (: invoices) .find (2) User Load (0.3ms) SELECT "users". * FROM "users" WAAR "users". "id" = $ 1 LIMIT $ 2 [["id", 2], ["LIMIT", 1]] Factuur laden (0,6 ms) SELECT "facturen". * VAN "facturen" WAAR "facturen". "User_id" = $ 1 [["user_id", 2]] => #  irb (main): 023: 0> user.invoices => # ]>

In dit geval laadt ActiveRecord gretig de factuurkoppeling bij het ophalen van de gebruiker (zoals te zien in de twee weergegeven SQL-query's).

ecto

Zoals je misschien al hebt gemerkt, houdt Ecto niet van magie of implicietheid. Het vereist van ontwikkelaars dat ze expliciet zijn over hun bedoelingen.

Laten we dezelfde aanpak proberen om user.invoices te gebruiken met Ecto:

iex (7)> ​​user = Repo.get (Gebruiker, 2)
[debug] QUERY OK source = "users" db = 18.3ms decode = 0.6ms
SELECT u0. "Id", u0. "Volledige naam", u0. "Email", u0. "Insert_at", u0. "Updated_at" VAN "gebruikers" ALS u0 WAAR (u0. "Id" = $ 1) [2]
% Financex.Accounts.User {
  __meta__: # Ecto.Schema.Metadata <: geladen, "gebruikers">,
  e-mail: "barbara@gordon.test",
  volledige_naam: "Barbara Gordon",
  id: 2,
  insert_at: ~ N [2018-01-02 10: 02: 00.000000],
  facturen: # Ecto.Association.NotLoaded ,
  updated_at: ~ N [2018-01-02 10: 02: 00.000000]
}
iex (8)> gebruiker.facturen
# Ecto.Association.NotLoaded 

Het resultaat is een Ecto.Association.NotLoaded. Niet zo handig.

Om toegang tot de facturen te krijgen, moet een ontwikkelaar Ecto hiervan op de hoogte stellen, met behulp van de vooraf geladen functie:

iex (12)> user = preload (Gebruiker,: facturen) |> Repo.get (2)
[debug] QUERY OK source = "users" db = 11.8ms
SELECT u0. "Id", u0. "Volledige naam", u0. "Email", u0. "Insert_at", u0. "Updated_at" VAN "gebruikers" ALS u0 WAAR (u0. "Id" = $ 1) [2]
[debug] QUERY OK source = "invoices" db = 4.2ms
SELECT i0. "Id", i0. "Payment_method", i0. "Paid_at", i0. "User_id", i0. "Insert_at", i0. "Updated_at", i0. "User_id" VAN "facturen" ALS i0 WAAR ( i0. "user_id" = $ 1) BESTELLEN DOOR i0. "user_id" [2]
% Financex.Accounts.User {
  __meta__: # Ecto.Schema.Metadata <: geladen, "gebruikers">,
  e-mail: "barbara@gordon.test",
  volledige_naam: "Barbara Gordon",
  id: 2,
  insert_at: ~ N [2018-01-02 10: 02: 00.000000],
  facturen: [
    % Financex.Accounts.Invoice {
      __meta__: # Ecto.Schema.Metadata <: geladen, "facturen">,
      id: 2,
      insert_at: ~ N [2018-01-03 08: 00: 00.000000],
      paid_at: #DateTime <2018-02-01 08: 00: 00.000000Z>,
      payment_method: "Paypal",
      updated_at: ~ N [2018-01-03 08: 00: 00.000000],
      gebruiker: # Ecto.Association.NotLoaded ,
      user_id: 2
    }
  ],
  updated_at: ~ N [2018-01-02 10: 02: 00.000000]
}

iex (15)> gebruiker.facturen
[
  % Financex.Accounts.Invoice {
    __meta__: # Ecto.Schema.Metadata <: geladen, "facturen">,
    id: 2,
    insert_at: ~ N [2018-01-03 08: 00: 00.000000],
    paid_at: #DateTime <2018-02-01 08: 00: 00.000000Z>,
    payment_method: "Paypal",
    updated_at: ~ N [2018-01-03 08: 00: 00.000000],
    gebruiker: # Ecto.Association.NotLoaded ,
    user_id: 2
  }
]

Net als bij ActiveRecord, omvat het vooraf laden van de bijbehorende facturen, waardoor deze beschikbaar komen wanneer u user.invoices oproept.

Vergelijking

Nogmaals, de strijd tussen ActiveRecord en Ecto eindigt met een bekend punt: explicietheid. Met beide tools hebben ontwikkelaars gemakkelijk toegang tot associaties, maar hoewel ActiveRecord het minder uitgebreid maakt, kan het resultaat ervan onverwacht gedrag vertonen. Ecto volgt de WYSIWYG-aanpak, die alleen doet wat wordt gezien in de door de ontwikkelaar gedefinieerde query.

Rails staat erom bekend cachingstrategieën te gebruiken en te promoten voor alle verschillende lagen van de applicatie. Een voorbeeld gaat over het gebruik van de "Russian doll" caching-benadering, die volledig afhankelijk is van het "N + 1-probleem" voor zijn caching-mechanisme om zijn magie uit te voeren.

validaties

De meeste validaties in ActiveRecord zijn ook beschikbaar in Ecto. Hier is een lijst met algemene validaties en hoe zowel ActiveRecord als Ecto ze definiëren:

Afronden

Daar heb je het: de vergelijking van essentiële appels met peren.

ActiveRecord richt zich op het gemak van het uitvoeren van databasequery's. De overgrote meerderheid van de functies zijn geconcentreerd op de modelklassen zelf, waardoor ontwikkelaars geen diep inzicht in de database hoeven te hebben, noch de impact van dergelijke bewerkingen. ActiveRecord doet standaard veel dingen impliciet. Hoewel dat het gemakkelijker maakt om te beginnen, maakt het het moeilijker om te begrijpen wat er achter de schermen gebeurt en het werkt alleen als je de "ActiveRecord-manier" volgt.

Ecto daarentegen vereist explicietheid die resulteert in meer uitgebreide code. Als voordeel staat alles in de schijnwerpers, niets achter de schermen en kunt u uw eigen manier bepalen.

Beide hebben hun kop, afhankelijk van je perspectief en voorkeur. Dus na het vergelijken van appels en sinaasappels, komen we aan het einde van deze BAT-tijd. Bijna vergeten je te vertellen BatGirl's codenaam (1989–2001) was…. Orakel. Maar laten we daar niet op ingaan.

Dit bericht is geschreven door gastauteur Elvio Vicosa. Elvio is de auteur van het boek Phoenix for Rails Developers.

Oorspronkelijk gepubliceerd op blog.appsignal.com op 9 oktober 2018.