The Pattern: View + Composable Pairs
Every screen follows the same shape:
views/Books.vue ──uses──► io/useBooks.ts ──calls──► command()
views/Catalogs.vue ──uses──► io/useCatalogs.ts ──calls──► command()
- Views (
client/src/views/) render state and forward user events. They contain no fetching, no caching, and no token handling. - Composables (
client/src/io/) are the data-access layer: one per view, wrapping TanStack Query and exposing exactly what the view needs — data ascomputedrefs, plus a few named actions.
A view’s entire contract with the data layer looks like this:
const {
books, page, totalPages, total, // computed state
isFetching, error, // request status
goToPage, applyFilters, addBook, // actions
} = useBooks()
One Door to the Server
The command() helper in application/query.ts is the single call site
that talks to the agent. Every composable goes through it:
// client/src/application/query.ts
// send a command/query to the server agent and return the reply payload
export async function command<R = any, D extends Record<string, any> = Record<string, any>>(
to: string,
data: D = {} as D
): Promise<R> {
const reply = await agent.send<Msg<"command", D>, Msg<"reply", R & Record<string, any>>>({
type: "command",
to,
data,
})
return reply.data as R
}
Because everything funnels through one function, cross-cutting concerns — attaching the identity token, unwrapping the reply envelope, surfacing typed errors — live in exactly one place.
Why Repetition Beats Abstraction Here
useBooks and useCatalogs look similar, and that is deliberate. A generic
useResource() factory would save a few lines but cost the ability to shape
each composable to its view (books need pagination and filters; catalogs need
rules metadata). With the transport already unified in command(), the
composables are cheap to write and easy to read — the right trade for a
codebase meant to be understood quickly.
Styling Approach
Views use scoped component styles with a small set of shared conventions rather than a heavyweight component library — consistent with the project’s bias toward a legible, minimal surface area. The interesting design system in this project is the architecture one: the view/composable/command layering is the contract every new screen follows.