Comments

Why I don’t like IEntity interfaces

One comment that keeps popping up in regards to my YouTube video about repositories (Repository Pattern, Done Right) is why I don’t like IEntity interfaces. So, let’s see.

The case for IEntity is to apply a constraint in the repository interfaces.

public interface IRepository<T> where T : IEntity 

I need to write another post about why you should avoid generic repositories. But for now, let’s just ignore it and focus on IEntity.

So, what’s the value of this constraint here? Does this force a developer from accidentally creating a repository for an integer?

public class IntRepository : IRepository<int>

Probably not! Those familiar with domain-driven design (DDD) argue that repositories are should be created for aggregate roots. So, this will stop a developer from creating a repository for an entity that is not an aggregate root, or a value object. So, perhaps it’s more accurate to call this interface IAggregateRoot (which is even worse than IEntity, as you’ll find out shortly).

With either IEntity or IAggregateRoot, a junior developer can bypass this “constraint” in just a second:

public class Money : IEntity
{
}

So, these marker interfaces don’t really stop anyone from creating a repository for the wrong type!

With these interfaces, every time you want to create a class, you need to write some additional code (noise) to mark it as an entity or aggregate. What is the value of this? I’ve never seen it! Hopefully, you can drop a comment and enlighten me.

What is an interface really?

An interface is a contract. When you declare an interface and implement it, all those implementations follow that contract. This gives you two benefits: extensibility and testability.

Think of the classic polymorphism example you read in your first object-oriented book.

IEnumerable<IShape> shapes = GetShapes();
foreach (var shape in shapes)
    shape.Draw(); 

Here, the type of the actual objects in our enumerable doesn’t really matter. We can “extend” this design, and define new shapes that implement this contract (IShape). At a minimum, they all will have a Draw method.

Another benefit of these contracts is that they help us mock dependencies of objects during unit testing. For example, we can unit test a controller or a service by providing a mock or fake repository. This way, we don’t need a database up and running to test the controller / service.

The case for IEntity

Look at this IEntity declaration. This is what I call a hollow interface.

public interface IEntity // or IAggregateRoot
{
}

What is the point of this interface? It’s an empty contract. What’s that supposed to mean “conceptually”? A contract that doesn’t enforce anything?! Really? Again, back to the first example, if you think by using these interfaces you prevent an inexperienced developer creating the wrong repository, you’re just fooling yourself.

Wait, I have a better solution!

Ok, Mosh, how about this interface?

public interface IEntity // or IAggregateRoot
{
    int Id { get; set; }
}

Not a hollow interface anymore! At least it enforces that every entity or aggregate root should have an Id, and that Id should be integer.

Wait… what if one of our entities needs a Guid as an Id?

Ok, what about IEntity<T>?

Here is another form of IEntity:

public interface IEntity<T> // or IAggregateRoot<T>
{
    T Id { get; set; }
}

This interface is even worse. Why? Look at how we use it:

public class Student : IEntity<int>

Read the code in plain English: class Student which is an entity of integer. What?! Entity of integer? (*my head spinning*).

Nonetheless, what value do you get from an interface like that? That every entity or aggregate root follows a contract? Would you be working with those IEntity or IAggregateRoot instances without caring what object is actually under the interface at runtime (like the polymorphism example I showed earlier)? Most likely not! You work with concrete types, like Student, Course, etc.

So, one more time, what’s the point of creating these contracts? To the best of my knowledge, nothing but extra noise in your code.

Unfortunately, interfaces are one of the least understood constructs of object-oriented programming languages. If you want to learn more about interfaces, check out my C# course: Classes, Interfaces and Object-0riented programming.

Related Posts

Tags: , , ,

6 responses to “Why I don’t like IEntity interfaces”

  1. Stilgar says:

    While I agree with the general premise (I wouldn’t put this interface on my entities) there is a case for it. It may grow into something more than an interface holding an ID. For example there might be a requirement to track history for all entities and there might be a method GetHistoryInfo() used when saving to the database. Things like these tend to appear in big codebases but I do think YAGNI applies here.

  2. Vadim says:

    You should go deeper.
    In case you use EntityFramework you do not need Repositories at all. EF already implement repository pattern.

  3. Bartek says:

    Ok, Mosh, how about this approach?

    public interface IEntity
    {
    }

    public interface IIdableEntity : IEntity
    {
    int Id { get; set; }
    }

    public interface IGuidableEntity : IEntity
    {
    Guid Id { get; set; }
    }

    public interface IKeyValueEntity : IEntity
    {
    string Key { get; set; }
    string Value { get; set; }
    }

    public class MyEntity : IIdableEntity
    {
    int Id { get; set; }
    // ….
    }

    public static class FrameworkExtensions
    {
    public static bool Exists(this IQueryable source, int id)
    {
    return source.Any(e => e.Id == id);
    }

    public static bool Exists(this IQueryable source, string key)
    {
    return source.Any(e => e.Key == key);
    }
    }

    I’m using it in my side project. I get that empty IEntity in this case is actually redundant (but it may change in the future), but implementing IIdableEntity or IKeyValueEntity allows me to create some common extension methods. Then all entities are IEntity, and implement second, specific interface

    • Bartek says:

      Submitting removed generics in my extensions 🙂
      IQueryable should be respectively IQueryable(IIdableEntity) and IQueryable(IKeyValueEntity)

  4. Leonardo says:

    I think generic repositories or units of work are cool. We usually repeat and repeat the same code over thousands of services.
    A framework to persist any entity disregarding its type is great and provide us room to take care of business logic and validation.
    We can complete, validate and transform an entity and then say: “hey dude, please persist this.”

Leave a Reply