Use This Pattern To Design a Rich Domain Model With DDD

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

Domain driven design helps you develop rich domain models. But sometimes, moving all of the business logic to the Domain is challenging. I'll show you how to use the double dispatch pattern with Domain driven design to move all of the business logic into the Domain layer.

Join my weekly .NET newsletter:

Read my Blog here:

Subscribe for more:

Chapters
0:00 Introducing a new business rule
2:17 Adding the repository implementation
2:55 Problem: Coupling methods to enforce a business rule
3:25 Solution: Moving logic into Domain with Double Dispatch
5:46 How Double Dispatch works
8:31 Why this is valuable
Рекомендации по теме
Комментарии
Автор

Not really seeing the value here.. The aggregate should have all line items in memory and can check the item count without any repository usage, right?

DylinMaust
Автор

As a rule of thumb, we should try to avoid the use of Repositories from inside Aggregates, if at all possible. - Vernon, Vaughn (2013-02-06). Implementing Domain-Driven Design

arunnair
Автор

I normally like your videos. If your argument is performance, and you are willing to make compromises and hacks like this, it would be much cleaner to use a stored procedure or custom SQL. You are doing 3 round trips (1. Fetch Order with one order line, 2. Check order line count, 3. Save changes), so performance is clearly not your concern. Personally, I would always fetch the entire aggregate before updating it and enforcing logic in the domain using POCO. It should be very rare you would need to do these kinds of performance optimizations.

thomasjespersen
Автор

You are already performing a join in the database to get the line you need, even if you are only hydrating one line, and then you need an aditional query to do the count. I'm not sure this is more performant than just loading all the lines in memory. Also, passing the IRepository as a parameter to a domain method smells bad to me.

pablolopezponce
Автор

Can't you just check the amount of line items of the order aggregate, without additional roundtrip to the DB?

cwevers
Автор

Why would the Domain know what the (definition of the) Repository would look like? This seems like introducing another problem for an existing problem. Not much added value.

xskyaflake
Автор

I used the same pattern in a project but i have more than one contract (IRepository), there is a contract for the validation and the math operation (IChecker and ICalculator for each domain aggregate), so thank you so mush Milan for this great Pattern

niezebenmansour
Автор

You're overloading the Repository pattern of concerns that aren't in its scope: repository should provide means to manage the persistence and fetching of aggregates respecting their integrity. Aggregates should be treated as an whole: you should rehydrate them in their entirety and then perform any operation over them. Your design is problematic and complex and doesn't scale very well. Domain services are the best place to orchestrate logic between repositories and aggregates. Avoid to pass repository to aggregates, your design will be more clean and maintainable.

Imagine the case where two concurrent transactions operate on the same aggregate and your operations works on "slices" of it's full state… it'll be a nightmare to manage integrity and DDD's AggregateRoot tactical pattern is designed just to "simplify" the management of such a complexity…

If you want play with DDD you have to recognize that there's a price to pay to achieve that elegance.

GiovanniCostagliola
Автор

The idea with Repositories is to act like a Black Box for the domain Entries not to be used within them, it's not fully an antipattern but as close you can get. The problem is related to cohesion and coupling. You do want to have high cohesion within your entities, and as much low coupling as you can. In this case you force the entity to know about it's own back box component, the repository, and make it depended to that one. And in that case you increase the Orders domain cohesion, and also make it dependent on a layer it should not really know exist the repo, and make it more coupled with the underlayer. A better way is to get the order and just ask the order domain line items if any, or you can add a Validation to the command, to verify if you are shall / can perform, the command at all. It's easy to see if the coupling and cohesion is a problem by start with Unit tests. It will direct tell you that you need to mock a repository to get your domain to handle it's a business' rule when a domain shall be designed to handle it's own states and just that. There is also more long lived problem to this approach. Even if I do not like the idea to split up a solution with separate projects, but if you should go the whole way and add the repo in a project too you will directly see the dependency problems. Repo shall have dependency to the domain. But the domain can't have dependencies to the repos. It will make it circular. And that's also a part of separation of concerns. Even if you but repo in the same project as the domain, we shall always consider references and try to avoid circular.

johannormen
Автор

Hmm, there may be some situations where an aggregate has a huge number of child entities (not sure order lines is a good example of this) and this approach might be more practical for performance reasons, but in general my default is to always eagerly load all child entities enforce invariants "natively" without requiring dependencies.

I do however employ basically the same approach for inter-aggregate rules, where I create a cohesive domain service method that verifies state of 1 or more other aggregates

davemasters
Автор

Order is aggregate so all items should be read in repository to protect data and domain consistency. Also aggregates should not access repository and factory objects, it creates circular dependencies.

tolgatoganduz
Автор

Great video, like even before watching)

artemvolsh
Автор

nice video, so as I can see IOrderRepository acts like Domain Service. If we separate Domain and Core, IOrderRepository interface will be defined in Domain project and implementation will be upper in Core layer.

slobodanmikaric
Автор

This seems like very bad practice to allow repos in your domain models, which means that you are referencing your infrastructure in your domain. This violates single responsibility. Repositories handle persistence and domain models handle state. If Order is an aggregate root, let the order look at its line items. In the remove line item function in the Order model, you check to see if there is only one line item left in the collection and if so, then do not remove it. You obviously already have the line items collection populated. Why make an unnecessary call to a repository when you already have the data you need?

JonnyItunes
Автор

You lose the purity of the domain, i prefer to lose the completeness.. the domain is enough complex to overhead it with calls to infrastructure, that could be done from application...

rapha
Автор

what if double check be double checks? i mean if you make a model for email and create method need to validation. you add a IEmailValidator to your create method but if we need more validations so we need to create more interfaces to check more validations. the method signature need to change every time and we need to fix references

yeganehaym
Автор

I am trying to learn DDD and that domain logic (HasOneLineItem) inside the OrderRepository confuses me. Shouldn't repositories only return instances of the AR it represents?

asierpaz
Автор

Isn't this what use-case would we for, where would we want to remove line item again? Just "Remembering" seems to go into DRY too much and put repos into the Domain ?

Downicon
Автор

Passing in the OrderRepository as a param to another class / method is a bit of a code smell. You could just create some sort of composite class that handles the aggregate logic and inject that into your handler instead of the repository. Repository would only be needed in that new class

SuperJB
Автор

why not _lineItem.count()==1 in RemoveLineItem instead injecting OrderRepository nad calling HasOneLineItem?

piotrc