Skip to content

Why Arc

Building a modern full-stack application means making the same decisions over and over: how do commands get validated, how do queries get shaped, how does the React frontend learn the exact types the backend expects, and how do you keep all of it in sync as the code changes. Most teams answer these with a stack of libraries and a layer of hand-written glue — controllers, DTOs, fetch wrappers, validation duplicated on both sides.

Arc answers them once, with conventions, so you can spend your time on behavior instead of plumbing.

For Cratis, event sourcing is usually the default persistence architecture for information systems, and Chronicle is where that lives. Arc’s job is different: it gives you the CQRS boundary for information going into and out of the system, then generates the frontend contract from that boundary. CQRS and event sourcing fit together naturally, but one does not require the other.

  • Commands and queries as the unit of work. On C#, a command is a record with a Handle() method; on the JVM and TypeScript it has a handle() method. A query can be a method on a read model. Arc maps them to HTTP automatically.
  • Generated TypeScript proxies. Every command and query becomes a typed client your React code calls. Change a command’s shape on the backend and regenerate the client; the frontend types change with it — the compiler catches the mismatch, not your users.
  • Pluggable persistence. Commands and queries use the storage you configure — for example, Chronicle for event sourcing, MongoDB or EF Core / SQL for current state on C#, Spring Data on the JVM, or MongoDB and Drizzle in the TypeScript source preview. Chronicle adds its command transaction semantics; those guarantees do not cover arbitrary external-service or database writes.
  • The cross-cutting integration points. On C#, Arc supplies validation, authorization, identity, tenant resolution, OpenAPI, and MongoDB/EF Core hooks. Check each other backend’s capability reference for its integration surface. Configure trusted authentication, membership checks, storage isolation, and business constraints for your application.

The two ideas reinforce each other. CQRS separates the thing you do (a command) from the thing you see (a query/read model), which keeps each side simple and independently optimizable. Proxy generation then makes that separation safe across the network: because the client is generated from the backend’s own declarations, there is no second source of truth to drift.

CQRS is often associated with event sourcing because events are a natural write-side model and projections are a natural read-side model. But CQRS is not event sourcing. Arc can put that boundary over current-state storage or optional Chronicle; Chronicle can store and process events without Arc. Integrations differ by backend.

generated proxy

writes

query proxy

React (Components)

Command handler

Current-state store

Read model

That diagram is the direct-database setup, useful for bounded current-state slices and for learning Arc in isolation. In the full Cratis loop, the Chronicle integration adds events and projections. Preserving the public query contract can keep the React caller unchanged, but provider code and data migration remain explicit work.

Arc doesn’t impose a folder structure, but it’s built to make one shine: organize the application by feature, with a folder per slice inside it — the shape event modeling gives you. Everything for one behavior — the command, the read model and query it serves, the React component that renders it, and the specs that prove it — then lives in one folder, and you read a feature top to bottom instead of hunting across Commands/, Handlers/, DTOs, and Clients/.