Skip to content

Coming from Spring MVC

Arc replaces the controller layer, not Spring itself. Dependency injection, @Configuration, @Bean, and every other ordinary Spring Boot mechanism still work exactly as before — the difference is what you write to expose a command or a query over HTTP.

Spring MVCArc
@RestController class with a @PostMapping method@Command class with a public handle method
@RestController class with a @GetMapping method@ReadModel class with a static or @JvmStatic companion query method
@RequestBody CreateTaskRequest bodyThe command’s own constructor properties — the command is the request body
@RequestParam / @PathVariableUnannotated query method parameters
@Autowired field or constructor injection into the controllerUnannotated handle parameters resolved from Spring by type; a query method needs @FromServices on each service parameter, because an unannotated query parameter is a client argument
@Valid @RequestBody X body, BindingResult resultAutomatic Jakarta validation on the command or query-argument graph when a Validator bean exists — see Add validation
ResponseEntity<T>A CommandResult<T> or QueryResult<T> JSON envelope — see the HTTP contract reference
@PreAuthorize("hasRole('ADMIN')")@Authorize(roles = ["admin"]) or @Roles("admin") on the class or the operation — see Protect the command
A hand-written OpenAPI annotation set, or springdocArc generates OpenAPI 3.1 from the same metadata — see Publish an OpenAPI document
A hand-written or generated fetch/axios TypeScript clientA generated, strict-mode TypeScript client — see Generate TypeScript proxies
SseEmitter or a WebSocketHandler for live dataA query returning Flow<T> or Flow.Publisher<T> — Arc hosts HTTP snapshots, SSE, and WebSocket for it automatically — see Declare observable queries
// Spring MVC
@RestController
public class TaskController {
private final TaskRepository repository;
public TaskController(TaskRepository repository) {
this.repository = repository;
}
@PostMapping("/api/create-task")
public ResponseEntity<TaskCreated> create(@Valid @RequestBody CreateTaskRequest request) {
var task = repository.create(request.title());
return ResponseEntity.ok(new TaskCreated(task.id(), task.title()));
}
}

View Java snippet source on GitHub

// Arc
@Command
@AllowAnonymous
public record CreateTask(String title) {
public TaskCreated handle(TaskRepository repository) {
var task = repository.create(title);
return new TaskCreated(task.id(), task.title());
}
}

View Java snippet source on GitHub

There is no TaskController and no separate CreateTaskRequest DTO. CreateTask is the request body, repository is resolved from Spring the same way constructor injection would resolve it, and the route (POST /api/create-task by convention) is derived and generated by KSP rather than declared with @PostMapping.

// Spring MVC
@RestController
public class TaskQueryController {
private final TaskRepository repository;
public TaskQueryController(TaskRepository repository) {
this.repository = repository;
}
@GetMapping("/api/tasks")
public List<TaskView> all() {
return repository.all();
}
}

View Java snippet source on GitHub

// Arc
@ReadModel
@AllowAnonymous
public record TaskView(String id, String title) {
@Path("/api/tasks")
public static List<TaskView> all(@FromServices TaskRepository repository) {
return repository.all();
}
}

View Java snippet source on GitHub

@FromServices exists precisely to distinguish repository — a dependency — from an unannotated parameter, which a caller supplies as a query argument. Without a query taking caller arguments, every parameter here happens to be a dependency; see Add a query to a read model for a query that takes both.

  • Spring Security, @ConfigurationProperties, @Scheduled, @Transactional outside a command handler, and every other Spring Boot mechanism keep working exactly as they do today.
  • You can still write ordinary @RestController endpoints alongside Arc’s generated ones in the same application — Arc adds routes, it does not remove Spring MVC.
  • Spring Data JPA and MongoDB repositories are injected the same way; see Use Spring Data read models for the paging, sorting, and read-model resolution Arc adds on top of them.