Is an ANEMIC Domain Model really that BAD?

preview_player
Показать описание
Is an anemic domain model a bad thing? Most would probably call it an anti-pattern, and it should be avoided. But is that really true? Well, it depends on what your intent is. Are you trying to create a domain model? Or are you really just trying to create a data model?

🔗 EventStoreDB

💥 Join this channel to get access to source code & demos!

🔥 Don't have the JOIN button? Support me on Patreon!

0:00 Intro
0:31 Anemic Domain Model
4:49 Transaction Script
7:08 Domain Model

#softwarearchitecture #softwaredesign #domaindrivendesign
Рекомендации по теме
Комментарии
Автор

You’re hitting on something here that is very interesting to me. I worked mostly within this transaction script as a service methodology but have always thought that they can be hard to compose in a reusable way. I’d love to see a more detailed example of breaking down multiple services (transaction scripts) into better domain models end to end

GothenburgDon
Автор

I have to admit that while I've heard the term "transaction script" before, until watching this video I wasn't actually aware that it is indeed a transaction script pattern that I use most often, including in my current project. Thus far I was basically calling it explicitly an anemic domain model, and deliberately so. We've been implementing the exact approach that you have described, i.e. the command-handler pattern, with handlers containing the business logic and making updates on models (data bag objects). This is an approach that IMHO suits best the majority of use cases with low complexity of the business domains, and there is no need for unnecessarily complicating things by "artificially" trying to introduce domain models. As always, great video Derek.

ernest
Автор

05:30 I worked on an app that had controllers newing up other controllers inside of them. I kinda died inside when I saw that. Felt physical pain.

mabdullahsari
Автор

I really liked your concept of Services as TransactionScripts!

My believe is that most applications nowadays are representations of business processes. Business depends on transactions, transactions are better built stateless (look at funcitonal programming and stateless scalability) and for me that's the reason anemic domain model is relevant and no bad at all.

I can see rich domain model on scenarios where -stateful- is helpful, some frontend frameworks makes a really good use of mutable objects (redux could be a good example). But stateful is dangerous!! Can lead to unexpected consequences if not done right, I try to avoid it at all cost unless is strictly necessary.

Thanks for sharing knowledge! 👏🏼

vzwzrd
Автор

Spot on, once again. One of the advantages of moving the logic from a handler to the model itself, is that you can easily go for a functional core (without the IO). You convinced me to put some serious time in refactoring my project.

MrTvdS
Автор

This reminds me of the functional approach to domain modeling where there is a separation between data and behavior.

morningstarsci
Автор

Dude you SMASH with good info! appreciated.

Undoubtly you were almost lauching about Entity Service or Entity Manager - I love that soft skill coming up even in this videos! ahahahah

gds_
Автор

Awesome practical explanation!
And yes, I think one of the first rules we break when having anemic model is encapsulation. And that means other parts of our code can alter our model's state and that could translate to bugs.

FranckMercado
Автор

Before I pushed for DDD in our organization, we were using something almost identical to your transaction script example. Entities had zero methods as a rule. Most of the business rules were in command handlers or services (because these were being inherited/overridden in some cases). When our architecture was designed 10 years ago it felt like the standard, to be honest. I suspect a lot of applications are still being written this way. It's interesting to see DDD become more of the defacto standard, at least it seems that way to me (confirmation bias?).

MrBildo
Автор

I use an anemic domain because I want to share my domain entities as libraries and I want them lightweight. Is there another approach or it should be shared as they are.?

Manolete
Автор

Another great video. I've noticed so much of good software development is isolating cyclomatic complexity from IO devices.
I always start my request handlers as a transaction script invoking direct SQL. When I see a cyclomatic complexity score of > 5, calling into another script, or duplication, then I push logic down into pure functions, define a simple repository that maps produced domain events -> SQL and repeat.

Would love to see some content on modeling aggregates with pure functions that only produce events.
I think it makes modeling Event Sourced aggregates much simpler in comparison to in place mutations.

stevep
Автор

The main problem I've seen with the Services + Anemic Domain Model is, that you have these data bag objects and each of it may be handled by a bunch of different services in some way or another; they pull data from those objects through generously spread getters and make decisions based on that data. It's all fun and games until your data and business rules are changing one day. Now during refactoring you have to trace the data through all those services and figure out how it was used to make which decisions. It's a refactoring nightmare and it would have been so much easier if the object which holds the data would try to not leak it through the whole app but make the decisions on its own.

hujrfbk
Автор

Coming from the Java world, this bothers me for 15 years now, but i haven't seen a single complex application which was able to implement a good and clean domain model approach. It's always this stateless transactional script approach (or a kinda dirty mix) because that's how most of the business works (processes/state machines) and ORM + other frameworks are even hard to use otherwise.
Developing and maintaining an intuitive understandable domain model in situations where you might have dozens of connected "Entities" is very hard and requires constant thoughtful naming and refactoring. But the IT business grows too fast and if you need many new people every year (and you are not google) and can't hire only the best developers, this ends in an unreadable and unmaintainable mess fast. The transactional script approach is harder to mess up (still many developers need only weeks to ruin it, if you don't double check everything) and at least your datamodel stays "clean".

Also, considering handlers, API-resources, repositories etc are often already following this "service/manager" approach, it often seems pointless to move a tiny bit of logic into the domain especially because often, the domain logic requires further database access or calls to external systems, which makes it hard to test and understand because logic is so distributed.

Not hating on the approach, i just haven't seen a good complex implementation, which improved readability/maintainability, but i saw many transactional script structured applications with 20+ years of runtime and thousands of people working on it at the heart of the business of global players and it is still maintainable, so i kinda gave up on the idea.

vanivari
Автор

On my end I’ve always been using the anemic model and as you pointed out the issue is often when there is services interdependencies. You can run into circular dependency issues. The way I am doing things today is that I got repositories that return data to the services. Each services is responsible for its entitie’s logic nothing else. Then if services need to call each other to me it is a use case (I call them features). Theses leave on the application layer outside the domain and contains the logic that combine multiple services together. That worked so far but I will definitely try the rich model pattern in a future complex project

alexas
Автор

Excellent content. Even as a vet myself, I still learn a lot from your vids. Do you have a video where you show how how to access the database while at the same time using DDD? Thanks.

carnelyve
Автор

Does a domain model imply mutation? I think that's a bit of sticking point. In the video's example, it is mutating the properties from within the domain model. If we all agree that mutation of state should _generally_ be avoided (which is a large, separate discussion), then what would a mutation-free domain model look like? Would the domain methods still live inside the domain model or would it begin to resemble the anemic pattern, where functions take in "domain POCOs" (for lack of a better term) and produce new versions of them?

WillFuqua
Автор

Hi Derek, thanks for the very informative videos, what if you receive an order from an event but for the order to be idempotent there is a check to see if that order has been created before because the domain layer is not allowed to have any IO operations would that check still need to be done on the service/manager layer before passing the order to the aggregate root?

RoughSubset
Автор

Can anyone explain to me how a rich domain model is any different to an anemic style model

In the anemic model we group our shared functionality by folder

In the rich domain model you group it in one file

I don't see a difference or the benefit of doing a rich domain model over anemic, especially when you have layers of complexity and relationships

I've seen many people do rich domain modelling really really badly, so it may be good if its done right, but doing it right is very very difficult. It's very easy to do the anemic version and very easy to fix it if it goes wrong

I've also found rich domain models extremely rigid to changes

Finally if you start changing the implementation, if everything is in a rich domain model it keeps sucking in more and more logic into it, making it more and more complicated to understand, in the anemic version you can just create a different implementation and use dependency injection. Isn't the point of classes just to structure things into logical places? On your file system we use a folder for that.

The problem with the rich domain model is you have to really understand what you are doing in order to keep that design on track, it can very quickly spiral out of control. If it does spiral it goes very very bad, because only the rich domain model has the knowledge to do certain work and it will naturally to the untrained eye keep puilling in more and more functionality. Now maybe that isn't a flaw in the rich domain model, its a flaw in us, but something that is so hard to get right, the pay off has to be worth it and I just don't think it is.

mikestock
Автор

I have two basic principles I try to follow, when choosing whether to put some logic in an entity or a service. First, if it's validation of any sort, I put it in the entity - preferably in the constructor, but if the entity is mutable, I put it in setters, so they protect against getting your entities into an invalid state; objects that don't protect themselves against invalid states are "too anemic". Second, if a function involves only a single entity, I put it in that entity - if it has no dependencies on any other part of the model, there is no point in making someone search for that functionality elsewhere, or risk them duplicating functionality they couldn't find; objects that aren't in charge of operations exclusively involving themselves are "too anemic". Everything else probably belongs in services or commands etc. - if an operation involves two entities, how do you even decide which entity should own that? I've known developers who would accuse me of writing "anemic" models because I follow these principles. In my opinion, a model is not really "anemic" if it guarantees it's own integrity - if it's not leaking it's own internal concerns anywhere else, it's not a problem. 🙂

RasmusSchultz
Автор

Hey Derek,

What about domain events, should we trigger them inside the handler or the aggregate root / entity?

MsTArray