# Phoenix Contexts
Scrypath fits Phoenix best when your context is the application-facing boundary for search orchestration.
## What Belongs In The Context
Keep these responsibilities in the context:
- repo persistence
- explicit calls to `Scrypath.sync_record/3`, `Scrypath.sync_records/3`, or delete verbs
- `Scrypath.search/3` calls plus repo-backed hydration policy
- preload, filter, sort, and paging defaults that belong to the feature
- error handling and operator-visible sync semantics
## What Stays Out Of Controllers And LiveView
Do not teach controllers or LiveView modules to compose raw `Repo` and `Scrypath.*` calls as the main pattern.
That boundary matters because Phoenix web modules should stay focused on HTTP or UI concerns, while the context owns feature logic and operational decisions.
## Example
```elixir
defmodule MyApp.Content do
alias MyApp.Blog.Post
alias MyApp.Repo
def search_posts(query, opts \\ []) do
Scrypath.search(Post, query,
Keyword.merge([backend: Scrypath.Meilisearch, repo: Repo], opts)
)
end
def publish_post(post, attrs) do
with {:ok, post} <- update_post(post, attrs),
{:ok, _sync} <-
Scrypath.sync_record(Post, post,
backend: Scrypath.Meilisearch,
sync_mode: :inline
) do
{:ok, post}
end
end
end
```
`MyApp.Content.search_posts/2` is the shared read path for controllers and LiveView. `MyApp.Content.publish_post/2` is the write path that owns the sync decision.
## Why This Boundary Holds Up
- controllers stay thin
- LiveView state stays UI-focused
- sync failures and rebuild decisions stay close to the feature that owns them
- docs and snippets can derive from one canonical example instead of teaching competing architectures