1

I am considering mutliple options to face a problem. I want to develop software following DDD style. The problem arise when I have to define my entities and I am working with Entity Framework Core, any other ORM would have the same problems.

ORM generates plain POCOs entities that represents the Tables in our DB. That don't fits DDD style.

Ways to accomplish a DDD style entities:

A) Implement Repository/Unit of Work patterns + Mapping: Use some kind of mapping process inside the repositories implementations, between the Ef entities and the Domain entities we work with. We got a set of repositories that makes a facade to EF Core, and all the EF implementation details remains decopupled.

Pros

  • Decopuling achieved
  • Centralized mapping configuration
  • Repositories return DDD style entities, easy to aggregate and follow DDD rules.

Cons

  • Loose of some EF features , autotracking => Less efficient
  • The possibility to launch Expression> queries=> Over-verbose methods in the repos to fit all needs.
  • Our implementation of UoW will be poor compared to Ef/ORM ones

B) Turn EF Core Entities into DDD style.: ORM usually allow us to define our classes before and then map them to a database with some configuration. Following this path you could get , business logic inside entities and DDD related practices( private setters/ aggregation) .See answer of @Flater for more detail.

Pros

  • All the features implemented by specific ORM(EF Core) => efificency, auto tracking , etc
  • We don't have to implement an "overdesign" of Repository/UoW patterns

Cons

  • We lose decoupling between layers, because we introduce dependency on the DbContext which is a specific tech related.

Do you know other approaches? Which pros and cons have them?

Engineert
  • 917
  • 1
  • 5
  • 17
X.Otano
  • 612
  • 2
  • 7
  • 17
  • An Entity Framework `DataContext` object *is a unit of work implementation.* Treat it accordingly. – Robert Harvey Mar 15 '19 at 19:32
  • Yeah it is, but how do you use properly the Ef auto generated classes as Domain entities(ddd) – X.Otano Mar 15 '19 at 19:36
  • Why do you need to seperate your entity class and model class? You can use your entity classes with EF. – Engineert Mar 15 '19 at 22:16
  • Read comments in Harvey answer. There are some disadvantages I don’t k ow how to face – X.Otano Mar 15 '19 at 22:16
  • Do you need any of these two features you said you would miss? Do you think the pros outweigth the cons? – Laiv Mar 15 '19 at 23:38
  • I would like to have that features. Launching expressions queries will help a lot too, don’t you think so? – X.Otano Mar 16 '19 at 20:10
  • @Engineert could you give an example of DDD entities used inside EF (core versión will be best) – X.Otano Mar 16 '19 at 20:12
  • @Badulake you didn't answer my question. You only "wish" to have both, but you didn't say if you "need" any of those. The question is, what are you willing to give up on? The DDD premisses regarding encapsulation and cohesion or the EF features? What's more valuable for you to take forward the application? Remember that DDD guidelines are not rules written on stone. Either EF features are always a must-have. Pragmatism prevails over dogmatism. – Laiv Mar 18 '19 at 08:23
  • @Laiv bon dies, I would prefer to have everything . Since it is not possible I am considering an approach where Ef Core will create the entities, but i will tune them into a DDD style. I found some examples in this web: https://www.thereformedprogrammer.net/three-approaches-to-domain-driven-design-with-entity-framework-core/ Maybe I ashould edit the question to add this option too. What option do you consider is the best? – X.Otano Mar 18 '19 at 09:18
  • Have you considered alternatives to EF? I'm not familiar with.Net stack and its libraries but I guess there' should be other ORMs which idiosyncrasy is less intrusive than EF. Is there any? If it was Java, we would be speaking about JPA or Hibernate and both dislike me. I would rather look at row mappers as myBatis or any other that would allow me to use constructors, factories or any other resource for the entities initialization. Or in the worse case, I would have 2 different models. One for persistence and the domain model itself. – Laiv Mar 18 '19 at 09:25
  • @Engineert I edited the question a bit , please re consider the answer – X.Otano Mar 18 '19 at 09:34
  • @Laiv the question is about which approach will be the best. Please read the redited question. Merci – X.Otano Mar 18 '19 at 09:35
  • @RobertHarvey please see the edited question, now it is more focuses in what i want to know. Many thanks – X.Otano Mar 18 '19 at 09:35
  • @Badulake: Am I correctly inferring from your comments that you are only interested in a database-first approach? (which you seem to be using based on "the Ef auto generated classes"). Are you open to switching to code first? – Flater Mar 18 '19 at 10:00
  • Totally @Flater – X.Otano Mar 18 '19 at 10:01
  • @Badulake: Is that an answer to the first question or the second? :) My bad for phrasing them opposingly I guess. – Flater Mar 18 '19 at 10:03
  • @flater, I am open to everything as it makes the development better – X.Otano Mar 18 '19 at 10:08
  • @Badulake consider Flater Fluent API example. This is exactly what I mean. – Engineert Mar 19 '19 at 15:11

2 Answers2

2

You mention "EF auto generated classes", which suggest you're using a DB-first approach. But you mentioned in the comments that you're open to anything, so I want to suggest switching to a Code First approach.

Robert Harvey's answer isn't wrong, I simply want to offer another viable solution to the problem.

Note: I don't know much about EF Core specifically, my answer uses EF but I assume that there aren't too many differences between the two.


EF has two approaches:

  • Database First - EF generates C# based on an existing database
  • Code First - EF generates a databased based on existing C# classes.

So the idea is simple: create your classes, and then tell EF that you want it to create the database accordingly. This gives you direct control over your classes and where you store them.

A basic example

public class User
{
    public int Id { get; set; }
    public string Name { get; set; }
}

public class MyContext : DbContext
{
    public MyContext() : base("MyConnectionStringName")
    { 

    }

    public DbSet<User> Users { get; set; }
}

This is enough information for EF to generate a database for you. It will generate a table for every DbSet<T> you specify, and it will generate that table's columns based on the class definition of T.
EF will make some assumptions (e.g. that a property named Id is intended to be the PK of your table). You can change these default behaviors; more on that further in the answer.

There is a lot more information on this subject than I can provide in the answer. Plenty of tutorials and guides exist online (example), I suggest you look into these to learn about the more detailed configuration options that EF puts at your disposal.


However, there is one thing I still want to point out. When you want to adjust your db columns, e.g. putting a max length on a certain string property, there are two ways of doing so.

Firstly, and most commonly found online, you can use attributes on your class:

public class User
{
    public int Id { get; set; }

    [MaxLength(50)]
    public string Name { get; set; }
}

These attributes are mostly provided by EF. Some others (e.g. [NotMapped]) are not provided by EF but EF does observe and respond to them (in the case of [NotMapped], the property will not generate a table column).

As you may suspect, many attributes are available.

But there is a second way, using the Fluent API. This will initially feel more contrived, but it's actually the better option when you want to take a DDD approach. Instead of decorating your entity with attributes, you instead "register" these configurations on the context itself:

public class MyContext : DbContext
{
    public MyContext() : base("MyConnectionStringName")
    { 

    }

    public DbSet<User> Users { get; set; }

    protected override void OnModelCreating(DbModelBuilder modelBuilder)
    {
        modelBuilder.Entity<User>().Property(p => p.Name).HasMaxLength(50);
    }
}

As far as I'm aware, every attribute has an equivalent Fluent API alternative.

But why use Fluent for DDD?

The reason I want to suggest this is because the attributes require you to have access to the EF dependency in your class definition, which means that your entity will be defined in the data layer.

But you don't want that. You want to have a separate domain layer where your classes are defined and then consumed by the data layer. By using the Fluent API, you can ensure that this is what happens. The MyContext class is already required to be part of the data layer (since it derives from EF's DbContext), so it makes more sense to have it also take care of the EF-specific configuration of the table columns.

Flater
  • 44,596
  • 8
  • 88
  • 122
  • I see your point. this is what I have actually done in my last project. I think this solution will be part of option B , dont you think so? My question is about anothers ways to handle that problem. I will use your anwer to enrich/explain better the question /option B. Many thanks – X.Otano Mar 18 '19 at 10:40
  • @Badulake: Yes, but A/B are not necessarily mutually exclusive. I work with what I would call a "DDD light" approach (purists will probably take heavy offense to me calling what I do DDD but I do at least try to follow the spirit of DDD's guidelines), where I use Code First _and_ repositories/unit of work. Based on the options you list and their pros and cons, I think you grasp the technical elements but aren't seeing every possible way in which to utilize them. I don't quite understand your B con; because wouldn't that apply in all cases of using an EF context, including A? – Flater Mar 18 '19 at 10:49
  • I think it is a common decission to choose, inside your application services layer, between consume custom Repo/UoW or delegate that work to DbContext. That will choose between A or B. If you choose to use both I think you will end with a overdesigned DbCOntext with little advantages, so yes i would say that they are exlusive.Why would you use repos/UoW , and, at the same time, a DbContext? Also I want to know what DDD purists will do in our situation, this is the place to see other options.Thanks by your colaboration – X.Otano Mar 18 '19 at 10:54
  • @Badulake: Because you will garner a _lot_ of friction from developers who blindly apply the "everything must be abstracted" approach. I fully agree that (uow+repositories) is just a replication of (context+dbsets). [I have written a more in depth answer where I advocate not using them and simply using the context directly](https://softwareengineering.stackexchange.com/questions/387135/should-entity-framework-6-not-be-used-with-repository-pattern/387178#387178). I stand by that notion, but many developers protest at the suggestion of doing so because they consider it a leaky abstraction. – Flater Mar 18 '19 at 10:58
  • You are wrong about EF approaches. There is Model First approach also. – Engineert Mar 18 '19 at 12:02
  • @Engineert: I always liked Model First (because I've been taught UML and that's how I approach new projects), but as far as I'm aware Model First has been deprecated in favor of Code First. [Here is a 5 year old article that suggests the same](https://www.theregister.co.uk/2014/10/23/entity_framework_goes_codefirst_only_as_microsoft_shutters_yet_another_visual_modelling_tool/). Note that what the article calls EF7 has ended up being renamed and released as EF Core. – Flater Mar 18 '19 at 12:07
  • @Flater: You can still develop Model First. EF just doesn't have an edmx or the visual designer anymore. – Robert Harvey Mar 18 '19 at 12:48
  • @RobertHarvey: Which still renders my answer ("EF has two approaches") as correct, as the Model First approach is an approach you take _outside of_ what EF provides. – Flater Mar 18 '19 at 12:49
  • @Flater: [shrug] The designer was never necessary to develop "Model First." – Robert Harvey Mar 18 '19 at 12:52
  • +1 for your Fluent API suggestion. It is what I mean to @Badulake by comment. – Engineert Mar 19 '19 at 15:10
1

Just use the EF auto-generated classes in your domain models.

For example:

public class Invoice
{
    public int InvoiceID { get; set; }
    public Address BillingAddress { get; set; } 
    public Address ShippingAddress { get; set; }

    public List<InvoiceItem> LineItems { get; set; }
}

Where Address and InvoiceItem are both auto-generated EF entities.

This approach alleviates the need to do mappings of any sort, and preserves your change tracking.

Robert Harvey
  • 198,589
  • 55
  • 464
  • 673
  • Yeah I see your point, but how do you put all your business logic inside that class? Imagine u want to follow DDD style . With private setters and multiple stuff as other child entities (aggregate roots) , Valiue objets, methods with business logic etc – X.Otano Mar 15 '19 at 20:14
  • You can do anything you want inside the `Invoice` class. If you need logic inside your EF entities, those are [partial classes](https://docs.microsoft.com/en-us/dotnet/csharp/programming-guide/classes-and-structs/partial-classes-and-methods). Just create another partial class, for example, `partial class Address`, and put your logic there. – Robert Harvey Mar 15 '19 at 20:16
  • Ok that will work for business logic, what about private setters?(ef work with them?) And handling of value objects? Have you handled them correctly with this approach? Do you know some github repo where I can have a look? – X.Otano Mar 15 '19 at 20:20
  • Let us [continue this discussion in chat](https://chat.stackexchange.com/rooms/91096/discussion-between-robert-harvey-and-badulake). – Robert Harvey Mar 15 '19 at 20:28