4 ms·
I had the exact opposite reaction to this book. It seemed like great stuff when I first read it, but over time, I realized it's really misguided in a lot of way
by feoren 4y ago
I had the exact opposite reaction to this book. It seemed like great stuff when I first read it, but over time, I realized it's really misguided in a lot of ways:
- Aggregates are too heavy. You need to make the decision about what is or is not an aggregate way too early in the design process. Boundaries are fuzzy.
- Actual concepts don't exist in nicely packaged bounded contexts. Concepts overlap a lot. You need to make the decision about which concept fits into which bounded context too early in the design process. Boundaries are fuzzy. Things are kinda like other things. The definition of "Employee" is not the same in the Scheduling context as the HR context as the Payroll context, yet they do overlap a lot, and you can't just treat them as completely separate things. If you break everything down into tiny contexts to deal with this, you just make Contexts and Aggregates the same.
- Repositories are not original to DDD and I think are very likely to foster absolutely horrific SELECT N+1 or even SELECT N^2 or N^3 performance. You simply can't let one bounded context do all its expensive operations in a vacuum; not when you have lots of contexts and lots of operations. In a complex system, most parts need to be planning, not doing. The results of most operations should be a plan that you can compose with other plans, analyze, and possibly even have an "optimization pass" if you need one.
- Ubiquitous language is the right idea. If you take nothing else from DDD, take this.
- theptip 4y agoFair enough; YMMV. I've not found that Aggregates need to be designed at the beginning; I've found it works fine to define an Aggregate after you start seeing performance issues with query patterns (i.e. define an Aggregate and forbid direct access to sub-objects when you see pathological access patterns/deadlocking). Personally I've found Repositories to be a good way of enforcing that N+1 queries DO NOT happen. For example, in Django you can have the repository run select_related/prefetch_related and `django-seal` on the ORM query to forbid unexpected queries. This somewhat neuters the flexibility of the ORM, which can be a big cost, but lets you build much more restrictive queries that are guaranteed to perform well. It's a trade-off and I don't think it's a clear win for every use-case, but particularly when dealing with Aggregates I think having a limited number of ways to query is beneficial. (This might mean you're running some sub-optimal SQL queries, over-fetching etc., but for most line of business applications, that's actually a viable trade in exchange for simpler domain interfaces and protection against N+1 queries.) Regarding "planning" vs. "doing", that seems to fit quite well with doing most of your work in POJO/POPO domain models, then only rendering those into DB writes at the edges. I think Repos can help with that. (IME you get N+1 selects when you use ORM models that abstract away the SQL queries and have application code interacting directly with ORM models; if you remove the ORM from your core domain logic and force it to live at the edges in Repos, this is not possible.)
- feoren 4y agoI suspect these different design "paradigms" all look somewhat similar when done really well and thoughtfully, but tend to have different gotchas and failure modes. Happy vs. unhappy families and all that. The worst design decisions we've seen are very salient in our minds, and these bad decisions are more shaped by the paradigm than the good ones are, so they look like the fault of the paradigm. Therefore we tend to compare paradigms based on how they go wrong, and people see bad design and think "wow we need a new paradigm". In that sense: YMMV indeed, and if DDD works for you, go for it. It's definitely worth learning, even if only so you can disagree with it! There are a lot of good smaller points in the DDD book even if you disagree with the larger paradigm. As an example, when I think "Repository", I think a class with methods like "GetEmployee(employeeId)" that hit the database immediately, and probably call other GetSingleThing methods in other repositories, possibly in a loop. That's how you get SELECT N^2s and up. That is, of course, their worst possible use, and you can have much better-thought-out services and still call them "repositories". That all being said, there are some near-universal "good ideas" I've learned, but they probably don't make a good textbook, and they certainly don't make a singular "paradigm" to follow. Things like: make decisions as late as possible (but no later). Do less, plan more ("do" meaning something with a side effect; planning is side-effect free). Work with sets instead of individuals. Separate identity from data. Raise the ceiling (empower smart people), not the floor (don't design for stupid people), but also realize that everyone is both sometimes. I use these general rules to evaluate paradigms, and I think DDD strongly fails the "make decisions as late as possible" rule, and doesn't tend to foster the "do less, plan more" rule unless you really reinterpret a lot of its directives.
- theptip 4y agoI strongly agree with the general principles you're putting forth, and agree it's hard to build an architectural philosophy which bakes in "good sense" as well. To some extent these can be orthogonal. (At the very least, your architectural philosophy had better not be opposing good sense, but I think it's fine for the philosophy to not encode every best-practice of good sense.) > all look somewhat similar when done really well and thoughtfully, but tend to have different gotchas and failure modes. I do agree with this to an extent, although I also think that "what does this theory tell me to do that differs from some other theory" is a quite valuable question; I do think there are some structural aspects of DDD which could be "bad" in other approaches. Perhaps most obviously, the meta-approach of having well-known named concepts in your architectural framework is in some sense opposed to the "pragmatic design" or "framework-less" approach of just using good taste and experience to come up with the right design for each situation. The latter perhaps being "better" for experienced architects, but harder to teach IMO. > when I think "Repository", I think a class with methods like "GetEmployee(employeeId)" Not to beat a dead horse, but on this object-level point -- in my understanding DDD explicitly contemplates both singular fetches like `GetEmployee(employeeId)` and plural like `GetEmployees(createdAfter)`. I think the book (Evans, 2003) has examples of each (though I could be mis-remembering and drawing from some of the other practical DDD-by-example books). But I can see where a team might use a sub-optimal combination of existing Repo queries rather than writing their own new ones.
- scruple 4y ago> Aggregates are too heavy. You need to make the decision about what is or is not an aggregate way too early in the design process. Boundaries are fuzzy. I've had the exact same complaint. I think there's a lot of great stuff to take away from DDD -- I also hear myself frequently making the same point about it's ubiquitous language -- but going all-in on some of its concepts, especially early in a projects life, is probably always a mistake that may well end in disaster.