3 ms·
Fair 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
by theptip 4y ago
Fair 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.