spring-data-mongodb
spring-data-mongodb copied to clipboard
Add ability to configure custom repository entity type mapping
I had trouble coming up with an issue title that captures the ask here so feel free to edit/suggest
Despite the myriad of customization options in spring-data-mongodb I believe the ask described below is currently not possible even though it'd be desirable :
Motivation
Given the types
class MyModel {
UUID id;
...
}
@Document
class MyDocument {
@Id
String id;
...
}
where MyModel specifies the entity as it is used in the service tier or any other layer downstream of the components responsible for persistence or schema mapping and MyDocument defines the storage schema for the MyModel entity (different primary ID types as an artificially introduced difference that can obviously be mitigated more easily in this case but just to illustrate the point below).
Currently, when using MongoRepository<T, D> a developer specifies the repository as follows :
interface MyRepository extends MongoRepository<MyDocument, String> {
}
which means that either a repository would have to add default methods to do the mapping and essentially specifiy the repository public API twice :
interface MyRepository extends MongoRepository<MyDocument, String> {
default Optional<MyModel> getModelById(UUID id) {
var conversionService = ...
var documentId = conversionService.convert(id, String.class);
var matchingDocument = findById(documentId);
var model = convert(matchingDocument, MyModel.class);
return model;
}
}
or even more suboptimally, a service with the above repository would inherit the problem of having to convert from the database schema defining type to the service model type :
@Service
class MyService {
MyRepository repository;
ConversionService conversionService; // Assume appropriate converters have been configured
MyModel getModelById(UUID id) {
var documentId = conversionService.convert(id, String.class);
var matchingDocument = repository.findById(documentId).orElse(...);
var model = convert(matchingDocument, MyModel.class);
return model;
}
}
And of course both of the solutions above would scale with the amount of repository or service methods respectively which is exactly why one would want to define this conversion once, and ideally transparently to downstream uses.
I understand it's a subjective discussion on whether or not it's a significant problem to "leak" schema definitions into a service layer and this might very well be one of those "pragmatism over correctness" situations but I'd like to propose adding an option to support the more ideal situation.
Proposal Allow a developer to configure Spring Data MongoDB such that it will use a different type for the entity/schema mapping than the entity type defined on the repository such that the above could be transformed into :
@EntityMapping(MyDocument.class) // Or some other way to configure this
interface MyRepository extends MongoRepository<MyModel, UUID> {
}
@Service
class MyService {
MyRepository repository;
MyModel getModelById(UUID id) {
return repository.findById(documentId).orElse(...);
}
}
along with some configuration, be it a repository type level annotation (e.g. an optional @EntityMapping(MyDocument.class)), MongoConfig level adjustments, adding callbacks, etc.
If this is already possible despite my fiddling and research then I apologize for making anyone read through my novel and I'd much appreciate a pointer in the right direction :) I couldn't get it to work with (my understanding of) the currently available customization options.
Thanks for your elaborate report. Let's take a step back and discuss what actual problem you're trying to solve. Probably you've noticed there are customization options where you can tweak store representation settings for individual properties as well as the Converter<MyDocument, Document> to customize the entire document serialization.
Yes I noticed the Converter<MyModel, org.bson.Document> options based on the documentation but they do not appear to supply a route out of this. This customization would only allow me to manually (de)serialize some type T to a custom BSON document and as a result would not utilize any of the value of the metadata annotations and typesafe schema definitions in general. Adding a converter Converter<MyModel, MyDocument> and the reverse would be a good solution but does not (seem to?) work for the type T as defined in the repository declaration but only for property types within T.
The problem I'm trying to solve is to enable some conversion between types M and D once where M is some service type and D is a type defining the storage schema for M and contains all repository/DB specific metadata, mapping information and so on as per my example. In other words, I do want to leverage the rich API (primarily the annotations) for the document mapping without committing to merging M and D into a single type which appears to be the expectation.
Essentially the ask is to allow MongoRepository to do just-in-time conversion between M and D so its public API has M as the entity type whilst the mapping layer uses D for any and all mapping/ORM work such that :
interface MyRepository extends MongoRepository<M, MID> {}
along with some config that forced M<->D conversion defines the following :
Mis the entity type exposed to downstream componentsMIDis this entities primary ID typeDis the type the entity mapping layer in Spring MongoDB uses as ifMongoRepository<D, MID>for all actual persistence purposesMID<->@Idannotation field type inDeither matches or is converted using configured Mongo conversions
The value is that it allows developers a very straightforward route to define some service entity M, separately define how this entity is stored in type safe way through D only once (including the M<->D conversion) and avoid having either D in downstream code or having to add an abstraction in between.