Using Separate Read/Write Models with EF Core and CQRS

preview_player
Показать описание

EF Core is my favorite ORM, no competition. The central component is a DbContext. It's a logical representation of your database. However, having the same data model for writing and reading data isn't optimal. A rich domain model is used to encapsulate business logic. But, this structure is too rigid for writing application queries efficiently. So, you can introduce a separate read model to solve this. In this video, I'll show you when you might need this and how to implement it.

Using Multiple EF Core DbContexts In a Single Application:

Join my weekly .NET newsletter:

Read my Blog here:

Chapters
0:00 When should you use this?
1:29 Creating the Write DbContext
4:11 Creating and configuring the Read DbContext
8:50 Using the Read DbContext for app queries
Рекомендации по теме
Комментарии
Автор

A bonus of splitting your reads and writes like this is that you can now also easily use DBs that support read replicas. AWS Aurora is one example of this.

ttolst
Автор

This is incredibly useful for the right audience. Thanks for the content!

twstdp
Автор

This might be silly question, but can I just use ApplicationReadDbContext to commit changes to the database? Ignoring the constraints of the DDD models?

llr
Автор

hey Milan, would you consider having two different implementations for a user repository a write and read repository leveraging the new keyed services feature in .NET 8 and you can inject either one of them depending on the use case read/write? would that be simpler than using different models and maintaining them?

ahmedrizk
Автор

Hi Milan, Apart from separation of concerns or SRP, what is the benefit of separating query and commands in separate handlers ? Even if we are separating the handlers, rest service class and repository class are common for these read and write methods. Could you please tell me the actual benefit of separating the Query and Commands ?

ridz
Автор

I would be grateful for a video of DDD and CA implementation in a DB-First approach project, especially when the names of the tables and fields in the DB do not correspond to the types and properties in the domain

zhqturg
Автор

The main challenge is indeed a need to maintain both models aligned. Whenever we make changes in domain model, we are forced to have corresponding changes in read model. There's no type safety here ='(. I'm currently asking EF team to add some features which can make life easier and let reuse rich model for queries without polluting them with unnecessary navigation properties. It is issue #32658 in EF repo.

VoroninPavel
Автор

Hello! Nice video. So if we have another models for reading, we can change properties to private fields in our domain models. It will make them more protected and business logic will be really in one place. For example your entity has states. You cant check this state in Command so Command cant have decision what to do

vanook
Автор

Crazy, I was just trying to figure out how to solve this in an app I'm working on. The configurations I set up for the write database were the ones I used for my read database. I have slim application layer DTOs that I use for my read db and a query service that defines the contract for my queries, which is implemented in my infrastructure.

My read database is set up with no tracking. But I'm wondering what the downside is to using the same configurations for both the read and write models? I was under the impression that adding a read db context with no tracking would suffice, but now I feel like I've taken the wrong path.

Either way, great content. I seem to run into issues, and sometime within the next 24 hours, I get a notification from your channel with a solution to my exact problem 😂

adamlazar
Автор

Usually we separate read and write models for full text search, pro indexing, fetching huge amount of records, etc. for this need, it’s better to choose another database for read model like Elasitcsearch, choosing SQL Server for read model does not give us any advantage

meyam
Автор

@3:50 - I like using the type system and would probably compare one of the known configuration types' namespaces to see if it matches the type in question instead of hardcoding a magic string there. Namespaces should in theory match the folder structure of your project.

ytejgbq
Автор

How would you enable replication, so that the databases are in sync.

hjbbmyf
Автор

tnx a lot, i have a question actually it isn't for this post, suppose i have a ddd app and for a insert command, we should many rules that need some data from db, http, queue and ..., and this data will be manipulate in different time and different place, and the key is that this data should check on entity before save, what is the best practice for this condition, add some property in entity and in create entity method fill those(it is bad because use db, http and ... in entity)?? or in service before call Repository.Add(entity) it is so ugly, for more details we can discuss about it if you prefer using email or social app), thank you so much

mostafashoja
Автор

Hey Milan, honestly I struggle to understand the reasoning for this. ORMs are quite useful for mapping rich domain models to database models and vice versa. In your case, those are Value Objects so you can use ComplexProperty method in your entity configurations. And by defining your read models in the application layer, you can fetch data in any form via query handlers in the infrastructure layer. If it's somehow not enough, the old way to achieve this is to create separate domain and database models. IMHO, these options would solve the problem.

adem_sahin_
Автор

Would love to get your take on this stackoverflow question I posted (5 upvotes, 500 views, with zero response so far haha):
I am unsure on whether I should let EF use my domain models or not.

Right now I have three types of models:

Core.Models (System wide)
API.Models (Used only in controllers)
Infrastructure.Entities (Only used for persistence)
Every method in my handlers/service classes look like this:

Get entity from DB
Change properties on entity based on request
Save changes
This works great when using the domain entities, as EF starts tracking the changes and can persist only what's been changed.

If I instead map to domain models when retrieving from the DB, so that my services/handlers only work on domain models, my changes aren't tracked of course:

Get entity from DB, as untracked, and map to domain model
Change properties on domain model based on request
Map back to entity
Do complex graph diff (set state manually on what has been changed since changes aren't tracked)
Save changes

The entities contain no data annotations or nothing, I'm using the Fluent API. I see no point in separating domain models and entities. I'm looking at examples from Jimmy Bogard, Steve 'Ardalis' Smith, Jason Taylor, and they are all using their domain models as their EF entities. Are they doing that to keep the code simple for demo purposes or is not beneficial to separate models and entities?

RynWTF
Автор

Thank you for this useful trick ! How can you accomplish this without breaking the dependency rule of clean architecture ? (Application Infrastructure))

maxencemartin
Автор

Back in the day, it was bad to model your domain model after your persistence layer. EF entities were always about mapping the persistence layer. Its a shortcut and most often a cost effective one to start giving those same entities a dual responsibility of being a domain object. Some more solutions might be: 1. Use a repository pattern and have a base "read repository" that establishes default query behaviours like AsNoTracking. If a query is "difficult" because of using custom types (like Value Types etc) as ef properties then consider removing them.. EF is never going to solve DDD in my opinion and its probably not its responsibility which leads into solution 2) Create your own domain model. For persistence you can inject DbContext and when you save your aggregate it uses EF at that point. When you load your aggregate same thing - however EF entities are mapped to seperate domain layer objects. Yes this is a pain to get right but its the cleanest solution.. for most apps arguably not worth the effort though, but for those apps you probably dont need DDD in the first place.

Wouldntyouliketoknow
Автор

I suppose there are 4 tables in total, right? 2 writting tables and 2 reading tables? It'd be cool showing the process of syncing the data between all of these tables.

lpsoldier
Автор

The video does not explain the problem you are trying to solve by introducing another layer of code that will need to be maintained.
EF Core already lets you setup a mapper from value objects to database types via .HasConversion methods/ValueConverters.
The navigation properties problem I do not understand at all - Shouldn't we have database models that include the navigation properties already, and then map them while querying to the domain model? Domain model can easily be persisted using context with navigation properties, even if it does not contain them. So I really do not understand what problem this approach solves.

What it introduces though is double amount of database models to maintain, double amount of configuration objects to maintain. It's fine with your example - 2 tables, and one relationship. But a database with 100 tables and many relationships? I suspect you also follow Database First model here, so which context you use for migration purposes?

DemoBytom
Автор

It is much simpler and much less code to put your context behind and interface and use ISP for read and write segregation when created from your context factory and overriding the save changes on the read context to throw. Maintaining all this code is an unneeded overhead, sounds good on paper but is a bad idea that only gets worse the larger the application grows.

fifty-plus