Advanced Features

Choosing the Right Abstraction Pattern

Guide to selecting the appropriate pattern for your use case

Guide to selecting the appropriate pattern for your use case

Category: Advanced Features

Before diving into specific patterns, use this guide to select the right approach for your use case.

Decision Tree

Do multiple queries share the same 2+ joins?
โ”œโ”€โ”€ YES โ†’ Use Bundle + Destructure + Extend (see Abstraction Patterns)
โ”‚         Define shared joins ONCE in a composite type, destructure in each query
โ”‚
โ””โ”€โ”€ NO โ†’ Do you need to navigate a single relationship (A โ†’ B)?
          โ”œโ”€โ”€ YES โ†’ Use composeFrom extensions (see SQL Fragments)
          โ”‚         Define `fun A.withB()` and call `from(a.withB())`
          โ”‚
          โ””โ”€โ”€ NO โ†’ Do you need chainable filters on the same query type?
                    โ”œโ”€โ”€ YES โ†’ Use extension function fragments
                    โ”‚         Define `fun SqlQuery<T>.filtered(): SqlQuery<T>`
                    โ”‚
                    โ””โ”€โ”€ NO โ†’ Use basic sql.select { } with inline joins

Quick Reference Table

SituationPatternKey Construct
3 queries share Order โ†’ Customer โ†’ Item joinsBundle + Destructuredata class OrderBase(val o, val c, val i) + destructure with val (o, c, i) = from(base())
Query 2 adds Product, Query 3 adds Product + ShipmentExtend from destructuredval p = from(oi.product()) after destructuring
Single Invoice โ†’ Subscription navigationcomposeFrom extensionfun Invoice.subscription() = sql { composeFrom.join(...) }
Filter variations on same joined structureExtension function fragmentsfun SqlQuery<T>.paidOnly(): SqlQuery<T>

Common Mistake

Wrong: Using composeFrom extensions for everything, repeating the same base joins in every query:

// โŒ Repeats i.warehouse() and i.product() in every query
val query1 = sql.select {
    val i = from(Table<Inventory>())
    val w = from(i.warehouse())
    val p = from(i.product())
    // ...
}
val query2 = sql.select {
    val i = from(Table<Inventory>())
    val w = from(i.warehouse())  // Repeated!
    val p = from(i.product())    // Repeated!
    val s = from(p.supplier())
    // ...
}

Right: Bundle shared joins, extend only what varies:

// โœ“ Base joins defined once
@SqlFragment
fun inventoryBase(): SqlQuery<InvBase> = sql.select {
    val i = from(Table<Inventory>())
    val w = join(Table<Warehouse>()) { w -> w.id == i.warehouseId }
    val p = join(Table<Product>()) { p -> p.id == i.productId }
    InvBase(i, w, p)
}

val query1 = sql.select {
    val (i, w, p) = from(inventoryBase())
    // ...
}
val query2 = sql.select {
    val (i, w, p) = from(inventoryBase())
    val s = from(p.supplier())  // Only the extension
    // ...
}

See Also