Using MongoDB with Entity Framework Core in DDD: Generating ObjectId for Entity Ids while Maintaining Abstraction

I’m implementing a Domain-Driven Design (DDD) architecture in my application using MongoDB with Entity Framework Core. My goal is to maintain clear separation between the domain logic and the database infrastructure. To achieve this, I have structured my solution with separate projects for the domain and infrastructure layers.

In my domain project, I have defined a Customer entity and a CustomerService. The Customer entity has a string property Id:

public class Customer
{        
    public string Id { get; set; } 
}

public class MyDbContext : DbContext
{
  public MyDbContext(DbContextOptions options) : base(options)
  {
  }

  public DbSet<Customer> Customers { get; set; }      
}

public class CustomerService
{
   private readonly MyDbContext _mydbContext;
   
   public CustomerService(MyDbContext mydbContext)
   {
      _mydbContext = mydbContext
   }   
   
   public Customer Get(string id)
   {
      return _mydbContext.Customers.Find(id);
   }    
   
   public void Create(Customer customer)
   {
      _mydbContext.Customers.Add(customer);
      _mydbContext.SaveChanges();
   } 
}

In the infrastructure project, I have extended the MyDbContext with MongoDbContext, using the MongoDB.EntityFrameworkCore package:

public class MongoDbContext : MyDbContext
{
    public static MongoDbContext Create(IMongoDatabase database) =>
        new(new DbContextOptionsBuilder<MongoDbContext>()
            .UseMongoDB(database.Client, database.DatabaseNamespace.DatabaseName)
            .Options);

    public MongoDbContext(DbContextOptions options) : base(options)
    {
    }

    protected override void OnModelCreating(ModelBuilder modelBuilder)
    {
        base.OnModelCreating(modelBuilder);

        modelBuilder.Entity<Customer>(c =>
        {
            c.ToCollection("customers");
            c.HasKey(c => c.Id);
            c.Property(p => p.Id)
                .HasConversion(id =>ObjectId.Parse(id),oid => oid.ToString());
        });        
    }    
}

It’s important to note that the Id property in the Customer entity is currently defined as a string. While I could redefine it as type MongoDB.Bson.ObjectId, doing so would introduce a dependency on the MongoDB.Bson package in the domain layer, which contradicts the abstraction I’m aiming for. The objective is to keep the domain layer unaware of the underlying database implementation.
Furthermore, note that I’m working with pre-existing MongoDB documents where the Id field is of type ObjectId. Otherwise, I might have opted for Guid or other primitive data types.

Fetching customers works seamlessly since the HasConversion() method handles the conversion from ObjectId to string. However, when creating a new customer, the Id property needs to be populated during the addition of the entity to the DbContext.
My preference is, if possible, to utilize the ObjectId generated by MongoDB for this purpose, instead of creating in C# code.

How can I address this challenge while preserving the abstraction between the domain and the infrastructure layers?

1 Like

I don’t have the answer to your question, but if you talk about DDD, do not use the term ‘entity’ to refer to an Aggregate Root (the only one that needs a repository). This differentiation will help many newbies avoid confusion between Aggregate Roots and Entities (and Value Objects, but you didn’t mention those).

I have a similar issue, but I am using GUID. My problem is more around the GuidRepresentation.

When I use the collection directly, I can read without any issues, but if I use the DbContext, I get the following message:

MongoDB.Bson.BsonSerializationException: GuidSerializer cannot serialize a Guid when GuidRepresentation is Unspecified.

It seems that the database created internally by the DbContext extension from Mongo is not aware of the serializers’ global configuration.

I will create a post about it in a moment, but I wanted to share it here in case you encounter it.

Thanks!