Skip to content

Query data across slices

Goal: a screen needs data that doesn’t live in a single slice — authors with their books, an order with its line items, anything where one thing owns a list of another.

In a database-backed Arc slice, model the relationship the same way you would in the domain: the child carries the parent’s id, and the query filters by it. Arc’s part is making that query a typed, live endpoint and generating the React proxy that calls it.

These are fragments of the standalone MongoDB book lesson; use its domain declarations, imports, replica-set configuration, and generated proxies. Neither the relationship nor the live query requires Chronicle.

For an owns-a-list relationship, keep the child keyed to its parent. A book belongs to an author, so the Book read model carries AuthorId:

[ReadModel]
public record Book(BookId Id, AuthorId AuthorId, BookTitle Title)
{
public static ISubject<IEnumerable<Book>> BooksForAuthor(AuthorId authorId, IMongoCollection<Book> books) =>
books.Observe(book => book.AuthorId == authorId);
}

View C# snippet source on GitHub

No attribute marks the identity — by convention the Id property is the read model’s identity. Arc separates the query method’s parameters into data and services: authorId is data, so it becomes a query parameter; the collection or MongoObservableQuery is a service, so it’s injected. C# tells them apart by type; the JVM marks service parameters with @FromServices and leaves caller arguments unannotated, and puts the query on the read model’s companion object as a @JvmStatic function. The generated proxy takes the data parameter either way:

// authorId is the generated Guid value, not String(author.id).
const [books] = BooksForAuthor.use({ authorId });

With MongoDB change streams configured, Observe(...) publishes updated data. The filter does not enforce parent existence; use server validation and database constraints for that separate requirement. Add a book for that author and the row updates without polling.

For a list-with-details screen, let the author list call AllAuthors.use() and each row call BooksForAuthor.use({ authorId: author.id }). That keeps each slice small: the author slice owns authors, the book slice owns books, and the screen composes their generated proxies.