Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Abstraction over MongoDb and Entity Framework

I might be on mission impossible, due to this quote by Mark Seemann:

If you have a specific ORM in mind, then be explicit about it. Don't hide it behind an interface. It creates the illusion that you can replace one implementation with another. In practice, that's impossible.

But what I am trying to accomplish is to switch my Entity Framework ORM out with the MongoDb driver just by changing my dependencies on startup.

But I keep running into problems where I do not offer enough flexibility or simply having too many new NotImplementedException(); in my MongoDb implementation.

My interfaces current structure is looking like this:

public interface IReadEntities
{
    IQueryable<TEntity> Query<TEntity>() where TEntity : Entity;
}

public interface IWriteEntities : IUnitOfWork, IReadEntities
{
    TEntity Get<TEntity>(object firstKeyValue, params object[] otherKeyValues) where TEntity : Entity;

    Task<TEntity> GetAsync<TEntity>(object firstKeyValue, params object[] otherKeyValues) where TEntity : Entity;

    IQueryable<TEntity> Get<TEntity>() where TEntity : Entity;

    void Create<TEntity>(TEntity entity) where TEntity : Entity;

    void Delete<TEntity>(TEntity entity) where TEntity : Entity;

    void Update<TEntity>(TEntity entity) where TEntity : Entity;
}

public interface IUnitOfWork
{
    int SaveChanges();

    Task<int> SaveChangesAsync();

    Task DiscardChangesAsync();

    void DiscardChanges();

    void Reload<TEntity>(TEntity entity) where TEntity : Entity;

    Task ReloadAsync<TEntity>(TEntity entity) where TEntity : Entity;
}

But already by this implementation I can not do the "complete" MongoDb implementation, since MongoDb does not utilize the unit-of-work pattern, or two-phase commit.

I then thought of moving the IUnitOfWork into extensions methods of the IWriteEntities, but then I loose my DbContext which is wired onto the Entity Framework implementation and I wont use a service-locator pattern in a static method.

So my last resort, is to ask if there is any golden path I have not yet tried? Or I should simple create two more interfaces:

public interface IEntityFrameworkWriter : IWriteEntities, IUnitOfWork {} // Move IUnitOfWork out of IWriteEntities

public interface IMongoDbWriter : IWriteEntities {}

And use these in my application. But then again, this is not what I planned on. Any feedback is appreciated.

Head explosing

like image 206
janhartmann Avatar asked Mar 04 '15 07:03

janhartmann


1 Answers

switch my Entity Framework ORM out with the MongoDb driver just by changing my dependencies on startup.

This goes far deeper than the mere problem of interfaces. This will result in a cataclysmic clash of philosophies.

MongoDB should be used with somewhat write-heavy, often de-normalized data structures. Your indexes, inserts and workers are complicated, and the queries are trivial. The schema must be carefully designed to support the queries you'll need, not based on the relation between the objects.

The classical SQL approach is reverse: The relationships alone are enough to come up with a good data structure, the schema is trivial (albeit large) there are no workers for eventual consistency, but the queries are insanely complex, usually because things that belong together must be split across two or three tables. This is why Transaction and Unit of Work are key in a typical SQL-environment, while they're not even supported in MongoDB.

Of course, I'm simplifying: There's a spectrum here, and you can abuse both MongoDB and RDBMSes as simple key-value stores; you can come up with a de-normalized data structure in SQL, and you can keep a ton of relations in MongoDB. But your MongoDB won't learn referential integrity or (distributed) transactions and your SQL Server won't let go of SQL.

But the more abstractions you use and the broader support they have, the more will you hide these key principles behind a bloated mess of code. I think one can generally say: An interface that is abstract enough can be implemented by any technology, but it frustrates the user.

like image 92
mnemosyn Avatar answered Nov 10 '22 07:11

mnemosyn