skip to content

What is a RepresentationModelAssembler and why would you use one instead of building links inline in the controller?

level: middleimportance: should knowfreq 45%

answer

  1. toModel(T) -> EntityModel<T>
  2. default toCollectionModel maps each
  3. RepresentationModelAssemblerSupport.createModelWithId
  4. @Component, inject into controller
  5. PagedResourcesAssembler for pages

basics

~10 s

RepresentationModelAssembler<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 lines
java
import 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

for a junior

Knows it converts an entity into an EntityModel with links and lives in one place.

for a middle

Implements toModel, uses the default toCollectionModel, and injects the assembler into controllers.

for a senior

Uses RepresentationModelAssemblerSupport, PagedResourcesAssembler, and hangs affordances off the self link in the assembler.

for a principal

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).

context