# Should I keep query business rules in Eloquent local scopes or in a repository layer?

> Keep query rules in local scopes and test them against a real database; add a repository only when the domain genuinely has to decouple from Eloquent.

- Asked: 2026-10-22
- Answered: 2026-10-27
- Asked by: Gizem
- Tags: Laravel, Eloquent, Architecture
- Source: https://muhammetsafak.com/just-ask/keep-query-rules-eloquent-local-scopes-repository-layer/
- Language: en-US
- Author: Muhammet Şafak

---
**Question:** I'm building a Laravel app and query logic is starting to spread across my controllers; I'm repeating the same `where` blocks in several places and it bothers me.

I'm torn between two options: should I use fluent Eloquent local scopes, or introduce a repository layer for testability? I keep hearing that repositories abstract the query logic and make it mockable, but I'm also worried that's overkill for this project.


## Short answer

For most Laravel apps the right default is local scopes — they're testable, composable and idiomatic.

## Context

Reach for a repository only when you have a real reason (swapping the data source, or protecting a domain boundary), not reflexively for "testability".

## Why

1. **The real problem is query logic in controllers, and both fix that.** Getting the `where` blocks out of the controller is 80% of the win. Scopes do it with far less ceremony than a repository layer.
2. **Local scopes: idiomatic, composable, cheap.** `Order::active()->forCustomer($id)->recent()` reads like the domain. You test them easily against a real test DB (in-memory sqlite or a test transaction); you don't need to mock Eloquent to test your query.
   ```php
   class Order extends Model
   {
       public function scopeActive(Builder $query): void
       {
           $query->where('status', 'active');
       }

       public function scopeForCustomer(Builder $query, int $customerId): void
       {
           $query->where('customer_id', $customerId);
       }
   }
   // Usage: Order::active()->forCustomer($id)->get();
   ```
3. **The "repository for testability" argument is weaker than it sounds in Laravel.** Eloquent is an Active Record by design, tightly coupled to the DB. Mocking a repository interface tests that you called a method, not that your query is correct. The more valuable test is [the one that hits a real database](https://muhammetsafak.com/just-ask/should-i-enable-model-preventlazyloading-only-locally-or-in-production-too/).

## What to do

1. **When a repository does earn its place:** when you genuinely need to swap the backing store (Eloquent → external API / search index), when you're doing DDD with a domain model separate from the Eloquent model, or when you want to forbid query-building from leaking into the domain layer. Those are architectural decisions, not testing ones.
2. **Don't write a repository that wraps Eloquent 1:1.** `UserRepository::find()` calling `User::find()` adds a layer and buys you nothing but indirection. That's the anti-pattern I see most.
3. **A middle path: query classes / actions.** For complex, multi-clause queries, a dedicated query class (or Spatie's query builder) gives you a named, testable object without going all the way to a full repository abstraction.

**Bottom line:** personally I'd keep the query logic in scopes and test them against a real (sqlite/pgsql) test DB. I only introduce repositories when the domain genuinely needs to decouple from Eloquent — and when I do, they return domain objects, not Eloquent models. Adding a repository purely to enable mocking usually trades a real test for a fake one.

## Related Reading

- [For comments across many models should I use a polymorphic relation or separate tables, given indexing and foreign keys?](https://muhammetsafak.com/just-ask/comments-polymorphic-relation-separate-tables-indexing-foreign-keys/) — Just Ask
- [Should I use chunkById instead of chunk when the same job also updates the rows it iterates?](https://muhammetsafak.com/just-ask/should-i-use-chunkbyid-instead-of-chunk-when-the-same-job/) — Just Ask
- [Should I bind a Money value object to Eloquent with a custom cast or with accessors/mutators?](https://muhammetsafak.com/just-ask/should-i-bind-a-money-value-object-to-eloquent-with-a/) — Just Ask
