Reputation: 141
I have a Data layer (Contains connection to MongoDB), a domain layer (contains repos and entities) and a service layer (contains services and models)
Now because my entities use ObjectIds, they require knowledge of MongoDB (Is this fine?)
My services get calls the repositories which returns these entities, and then converts them to models. This is causing my service layer to require knowledge of MongoDB because of the ObjectId Property on the entities.
Is there a way I can avoid this? I have heard I can use my Id as type string and when storing the data MongoDB will convert this to a ObjectId?
Upvotes: 3
Views: 537
Reputation: 16066
Sometimes to mapping only the Id could be confusing and also what happens in the case that you may have another objectId (maybe a reference) in your same entity?
well you can make a map for objectId using a map convention over configuration pattern. look at the following implementation:
public static class MongoDbConventionRegistry
{
public static void Register()
{
var conventionPack = new ConventionPack {new StringObjectIdMemberMapConvention()};
ConventionRegistry.Register("CustomConventions", conventionPack, t => t.FullName.StartsWith("YourNamespace.Model.Entities.etc"));
}
}
public class StringObjectIdMemberMapConvention : IMemberMapConvention
{
private readonly Regex _memberMatchRegex = new Regex(@"(^Id$)|(.+ObjectId?$)",RegexOptions.Compiled); // you can change this regex to match the convention you want
public string Name {
get { return "StringObjectId"; }
}
public void Apply(BsonMemberMap memberMap)
{
if (memberMap == null)
return;
if(memberMap.MemberType == typeof(string) && _memberMatchRegex.IsMatch(memberMap.ElementName) )
memberMap.SetRepresentation(BsonType.ObjectId);
}
}
so for this case any Id and any other property that ends with objectId will be mapped into objectId, so you can leave your entities ids as string and the driver will handle the conversion for you, it's more convenient when you don't want carry the mongodb dependencies between most of your layers in your system.
you can change the convention to any you want, I just wanted to highight the feature.
Upvotes: 1
Reputation: 11662
Short version : yes, use String everywhere.
If you are ok with annotations, then use :
[BsonId]
[BsonRepresentation(BsonType.ObjectId)]
public string Id { get; set; }
Otherwise you can use a class map :
BsonClassMap.RegisterClassMap<i_YourModel>(cm =>
{
cm.AutoMap();
cm.SetIdMember(cm.GetMemberMap(x => x.Id)
.SetIdGenerator(StringObjectIdGenerator.Instance));
}
);
Long version :
It is advisable to use something opaque, that is not directly connected to the underlying database implementation, in your model and service layer as much as possible (when possible).
Previously, primary key ids where usually big numbers, that were then mapped to a number primary key column on the database. However, when assigning a new id to a new entity, a check on the database had to be done to ensure to have an unique id. Many techniques exists, from LO-HI id generators, to auto_increment columns, to sequences etc..
With NoSQL, and the need for more parallelism, most applications are now using UUIDs or variations of it, because the ID can be generated with reasonable probabilities it will be unique without having to ask the database if it is really unique, or use sequences or the like, that are bottlenecks in an application that scale horizontally.
MongoDB is no difference, and uses ObjectId that are a kind of UUIDs.
These ids (both mongo and others) can always be represented as Strings, usually a HEX representation of the bytes composing the key. So, in your model use String as ids, in your service layer the same, in you data layer convert it to whatever format is better for your underlying database implementation, MongoDB in this case.
Upvotes: 3