Sponsor: Using RabbitMQ or Azure Service Bus in your .NET systems? Well, you could just use their SDKs and roll your own serialization, routing, outbox, retries, and telemetry. I mean, seriously, how hard could it be?
When designing Aggregate Roots do you eager load all navigational properties? Any articles/pointers for practices around this? cc @julielerman@ardalis
This question posted on Twitter (and the subsequent thread) caught my attention. I had to pipe in with my own opinion on having queries without repositories.
Or don't use an aggregate root for reads and only use for writes. If you generally have low writes, then load the aggregate by eager loading the navigation that you need for the write action
To summarize, the question is about using Aggregate Roots with an ORM like Entity Framework. Should you eager load all navigation properties or alternatively I guess, use lazy loading?
My answer is if you’re only using an aggregate root for commands (to change state) and you generally have a higher read to write ratio, then eager load the navigation properties. As a general rule of thumb.
eShopOnWeb
I recently heard about eShipOnWeb from Steve Smith. It’s a reference application using ASP.NET Core 3.1 using a layered monolithic architecture.
I decided to take a look around and try and refactor a spot that was using a repository for a query.
There is a query to return all of the orders for a signed-in user. It’s pretty simple but is a good example of it using the OrderRepository along with a specification to fetch out the relevant orders. From there it creates a ViewModel that is used in a Razor view.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Looking at the Razor view, you can see that it doesn’t use some of the properties on OrderViewModel. The OrderItems or ShippingAddress properties are never used.
The Repository with the specification is retrieving the entire Order Aggregate Root and eager loading the OrderItems. We’re fetching in a lot of data that we simply aren’t using.
Refactoring Queries without Repositories
The refactor is rather simple. Just remove the repository and instead inject the Entity Framework DbContext (CatalogContext). The second part is creating a separate ViewModel (MyOrdersViewModel ) that only contains the data we need for our Razor view. Then use this new ViewModel as a projection in our EF Query.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
In many situations, I prefer to create a SQL Database View that I will create a new class to represent and use that as a DbSet in my DbContext. And most often I have a completely different DbContext for writes vs reads. My Reads DbContext is what will have a model that represents the SQL view. This allows you to do that projection as an SQL View rather than doing the projection in your code.
If you were using a SQL Database View and created an OrderSummary class that was on your DbContext, that now might look like this:
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters