Fluent Assembler
As described in the assemblers page, you can inject assemblers and use them directly. However, the business framework provides a Domain-Specific Language (DSL) to do common Aggregate/DTO mapping operations.
To use it, inject the FluentAssembler
interface:
public class SomeClass {
@Inject
private FluentAssembler fluentAssembler;
}
The fluent assembler automatically uses the proper assembler to do its work. The mapping behavior must still be implemented by a default or a custom assembler.
Usage
Fluent assembler can assemble from an aggregate to a DTO:
public class SomeClass {
@Inject
private FluentAssembler fluentAssembler;
public void someMethod(Person person) {
PersonDto personDto = fluentAssembler
.assemble(person)
.to(PersonDto.class);
}
}
Or it can merge a DTO back into an aggregate:
public class SomeClass {
@Inject
private FluentAssembler fluentAssembler;
public void someMethod(PersonDto personDto, Person person) {
fluentAssembler
.merge(personDto)
.into(person);
}
}
Automatic aggregate creation
When merging DTO to aggregates, fluent assembler DSL can use a factory to automatically create aggregates as necessary:
public class SomeClass {
@Inject
private FluentAssembler fluentAssembler;
public void someMethod(PersonDto personDto) {
fluentAssembler
.merge(personDto)
.into(Person.class)
.fromFactory();
}
}
To find a matching factory method for the aggregate, the @FactoryArgument
must be used on DTO getter(s), specifying the position of the factory method parameter it corresponds to:
public class PersonDto {
private String firstName;
private String lastName;
@FactoryArgument(index = 0)
public String getFirstName() {
return firstName;
}
@FactoryArgument(index = 1)
public String getLastName() {
return lastName;
}
}
These annotations will make fluent assembler DSL find a method on the factory corresponding to the following signature:
public interface PersonFactory extends GenericFactory<Person> {
Person createPersonFromName(String firstName, String lastName);
}
If multiple methods on the factory match the parameters derived from the @FactoryArgument
annotations, an exception will be thrown.
Automatic aggregate retrieval
When merging DTO to aggregates, fluent assembler DSL can use a repository to automatically retrieve aggregates from persistence as necessary:
public class SomeClass {
@Inject
private FluentAssembler fluentAssembler;
public void someMethod(PersonDto personDto) {
fluentAssembler
.merge(personDto)
.into(Person.class)
.fromRepository()
.orFail();
}
}
The orFail()
method indicates that an AggregateNotFoundException
will be thrown if the aggregate is not found in the repository.
To retrieve the aggregate from its repository, the @AggregateId
annotation must be used on the DTO getter providing the aggregate identifier:
public class PersonDto {
private String id;
private String firstName;
private String lastName;
@AggregateId
public String getId() {
return id;
}
}
This annotation will use the value return by the getId()
method to retrieve the aggregate from its repository.
If the identifier is a complex value object created from multiple fields, you can add a method to the DTO for creating
the value-object. The @AggregateId
annotation will then be put
on that method.
Combination of automatic retrieval and creation
When merging DTO to aggregates, fluent assembler DSL can combine repository retrieval with creation by the factory:
public class SomeClass {
@Inject
private FluentAssembler fluentAssembler;
public void someMethod(PersonDto personDto) {
fluentAssembler
.merge(personDto)
.into(Person.class)
.fromRepository()
.orFromFactory();
}
}
If the aggregate is not found in the repository, it is created with the factory. Both @AggregateId
and @FactoryArgument
annotations must be present on the relevant DTO
getters.
Working with multiple objects
All the operations described above also work on multiple objects at once.
Assembling or merging from the following types is supported:
- An array,
- A
Stream
, - A
List
, - A
Set
, - An arbitrary
Collection
, - A
Slice
(for offset-based and key-based pagination), - A
Page
(for page-based pagination).
The following example is for assembling from an array of aggregates to a stream of DTO:
public class SomeClass {
@Inject
private FluentAssembler fluentAssembler;
public void someMethod(Person... persons) {
Stream<PersonDto> personDtoStream = fluentAssembler
.assemble(persons)
.toStreamOf(PersonDto.class);
}
}
The following example is for merging a list of DTO into a stream of aggregates created from the factory:
public class SomeClass {
@Inject
private FluentAssembler fluentAssembler;
public void someMethod(List<PersonDto> personDtoList) {
Stream<Person> personStream = fluentAssembler
.merge(personDtoList)
.into(Person.class)
.fromFactory()
.asStream();
}
}
Specifying an assembler qualifier
If a qualified assembler implementation is required, specify the qualifier class using the with()
method.
The following example uses a ModelMapper default assembler:
public class SomeClass {
@Inject
private FluentAssembler fluentAssembler;
public void someMethod(Person person) {
PersonDto personDto = fluentAssembler
.assemble(person)
.with(ModelMapper.class)
.to(PersonDto.class);
}
}
Example
Mapping a Product
to a ProductRepresentation
and back:
public class SomeClass {
@Inject
private FluentAssembler fluentAssembler;
@Inject
private ProductFactory productFactory;
public void someMethod() {
Product product = productFactory.createProduct(1, 1, "Product #1");
product.changeDescription("Some description");
ProductRepresentation productRepresentation = fluentAssembler
.assemble(product)
.to(ProductRepresentation.class);
productRepresentation.setDescription("Changed description");
fluentAssembler
.merge(productRepresentation)
.into(product);
}
}