What is a RepresentationModelAssembler and why would you use one instead of building links inline in the controller?
answer
- toModel(T) -> EntityModel<T>
- default toCollectionModel maps each
- RepresentationModelAssemblerSupport.createModelWithId
- @Component, inject into controller
- PagedResourcesAssembler for pages
basics
~10 sRepresentationModelAssembler<T, D> is a component that converts a domain entity T into a link-bearing model D (usually EntityModel<T>). It centralizes link-building so controllers and other places produce identical representations instead of duplicating link code.
solid answer
~40 s`RepresentationModelAssembler<T, D extends RepresentationModel<?>>` is the Spring HATEOAS abstraction for turning a domain object into a hypermedia representation. You implement `toModel(T entity)` to wrap the entity in an `EntityModel` (or a custom model) and attach the relevant links; the interface's default `toCollectionModel(Iterable<T>)` maps each element through `toModel`. You register it as a `@Component` and inject it into controllers. The point is single-responsibility and consistency: link logic lives in one place, so a `GET /employees/{id}`, a collection endpoint, and a POST-created response all render the same links. It keeps controllers thin and makes the representation reusable across endpoints. `RepresentationModelAssemblerSupport<T, D>` is a convenience base class that provides helpers like `createModelWithId` and a pre-wired self link. For paged results you pair it with `PagedResourcesAssembler`.
code
java · 17 linesimport org.springframework.hateoas.EntityModel;
import org.springframework.hateoas.server.RepresentationModelAssembler;
import org.springframework.stereotype.Component;
import static org.springframework.hateoas.server.mvc.WebMvcLinkBuilder.*;
@Component
class EmployeeModelAssembler
implements RepresentationModelAssembler<Employee, EntityModel<Employee>> {
@Override
public EntityModel<Employee> toModel(Employee employee) {
return EntityModel.of(employee,
linkTo(methodOn(EmployeeController.class).one(employee.getId())).withSelfRel(),
linkTo(methodOn(EmployeeController.class).all()).withRel("employees"));
}
// toCollectionModel(Iterable) is inherited: it maps each Employee via toModel.
}go deeper
Knows it converts an entity into an EntityModel with links and lives in one place.
Implements toModel, uses the default toCollectionModel, and injects the assembler into controllers.
Uses RepresentationModelAssemblerSupport, PagedResourcesAssembler, and hangs affordances off the self link in the assembler.
Treats the assembler as the representation-mapping seam, ensuring consistency across endpoints and evolving contracts without controller churn.
**The problem it solves.** If every controller method builds `_links` inline, link logic gets copy-pasted across the single-resource endpoint, the collection endpoint, and creation responses. They drift, and a URI or relation change must be made in many places. `RepresentationModelAssembler` centralizes entity-to-representation mapping. **The interface.** ```java public interface RepresentationModelAssembler<T, D extends RepresentationModel<?>> { D toModel(T entity); // you implement default CollectionModel<D> toCollectionModel(Iterable<? extends T> entities) { … } // maps each via toModel } ``` - `T` is the domain type (e.g. `Employee`). - `D` is the output representation, which must extend `RepresentationModel<?>` — typically `EntityModel<Employee>`, but it can be a custom subclass when you want to reshape fields. - You only *have* to write `toModel`; `toCollectionModel` is provided as a default that wraps each element and can be overridden to add collection-level links (e.g. a `self` link to the list). **`RepresentationModelAssemblerSupport`.** An abstract base class (`RepresentationModelAssemblerSupport<T, D>`) you extend, passing the controller class and the model class to its constructor. It gives you: - `createModelWithId(id, entity)` — instantiates `D` and adds a `self` link pointing at the controller's single-item method for that id. - `instantiateModel(entity)` — override to control how `D` is created. - a `toCollectionModel` that adds a self link to the controller's collection method. **Wiring.** Annotate the assembler `@Component` and inject it: ```java @GetMapping("/employees/{id}") EntityModel<Employee> one(@PathVariable Long id) { return assembler.toModel(repository.findById(id).orElseThrow()); } @GetMapping("/employees") CollectionModel<EntityModel<Employee>> all() { return assembler.toCollectionModel(repository.findAll()); } ``` **Paging.** For `Page<T>` results, inject the framework's `PagedResourcesAssembler<T>` and call `pagedAssembler.toModel(page, entityAssembler)`. It produces a `PagedModel` with `page` metadata and `first`/`prev`/`next`/`last` navigation links — you should not hand-roll paging links. **Gotchas.** - The generic bound `D extends RepresentationModel<?>` is enforced; returning a bare POJO won't compile. - If you override `toCollectionModel`, remember to still map elements via `toModel` (or call `super`), or you'll lose per-item links. - The assembler is a natural home for the **Affordances API** (`afford(...)`): attach update/delete affordances to the self link inside `toModel` so every representation advertises the same write operations, which HAL-FORMS then renders as `_templates`. - Assemblers are stateless singletons; don't store request state in fields. - It's a *presentation* concern — keep persistence/business logic out of it. **When to use.** Any time a domain type is exposed by more than one endpoint, or when you use affordances, an assembler pays for itself. For a one-off endpoint, inline links are fine.
- Which method must you implement and which is provided by default?You must implement `toModel(T)`; `toCollectionModel(Iterable)` has a default that maps each element through `toModel`. You can override it to add collection-level links.
- How do you render pagination links without hand-writing them?Inject the framework-provided `PagedResourcesAssembler<T>` and call `toModel(page, entityAssembler)`; it emits a `PagedModel` with `page` metadata and first/prev/next/last links.
saying these in an interview costs you the question
- Saying `toModel` can return a plain POJO (it must extend `RepresentationModel`).
- Putting business or persistence logic inside the assembler.
- Hand-rolling pagination links instead of using `PagedResourcesAssembler`.
- Claiming assemblers should hold per-request state in fields (they're singletons).