DDD/PoEAA implicitly are about building long-lived, stateful thick clients -- i.e. a Java/C++ desktop application, because that's what people built by default in 2003. In the past 22 years, things have changed. The Bezos "API mandate" has either explicitly or implicitly been adopted by many organizations. Front-ends are much more likely to be websites/Chromium operating with stateless HTTP requests, instead of long-lived desktop applications. (React has "won" as the default JS framework over the past 10 years, and that has its own set of architectural patterns which don't mesh with DDD.) Backend software is built with a cloud-first mentality: servers can fall over at any minute, the application should scale up by adding more servers, etc...
All this leads to a much different approach. Statelessness is nonnegotiable, since you're dealing with stateless HTTP/GRPC requests that expose most or all of your functionality. The "anemic domain model"[0] is derided as an antipattern by DDD, yet it's the most sensible approach for dealing with objects that get loaded and disposed of with every request. Complex ORM schemes and patterns like "unit of work" are fine when dealing with small amounts of data, but break down when you start needing to care about e.g. splitting transactions into smaller chunks because writing 200,000 rows in a single tx is a bad idea.
I don't think DDD is useless, but its application is much narrower than advertised. It's a shame that even today people are still being taught from DDD as if it's the "right" way to code when for a large amount of modern software it's a terrible fit.
I don't get how an an anaemic data model makes more sense just because of database persistence. You save/load the data. It's completely irrelevant where the logic is.
Not at all. As I say, DDD isn't totally inapplicable now; if you're writing a small, stateful application with no need to expose functionality externally, it can be a good fit. But you're a lot more likely to be dealing with data volumes that require 200k row transactions, or a requirement to expose functionality and data via APIs, in 2024 than you were in 2003. The scale of software has gone up significantly in 20 years.
> I don't get how an an anaemic data model makes more sense just because of database persistence. You save/load the data. It's completely irrelevant where the logic is.
DDD largely treats the database as an impure, imperfect implementation detail that needs to be worked around, rather than an essential part of the application. I've seen examples of creating "entities" called like `UserTransaction` in order to retain the "purity" of using OOP for everything. There's also a lot of literature where people suggest using database CASCADE to deal with updates/deletions which, IMO, is awful and outdated. It's totally ridiculous and is just creating more work in order to avoid the bogeyman of (gasp) stateless procedural programming that was very out of fashion at the time DDD was written.
I guess they don't make em like they did in 2018 anymore!
> DDD is against the idea of having a single unified model
Is wrong: DDD is not against the idea of an application having a single unified model.
Bounded contexts exist to help you understand the organization, and, specifically, that different people may use the same word with a different meaning. But if your application serves a single group in the company where definitions are shared, there is no reason for your code to leverage multiple bounded contexts, and their existence may be limited to a footnote in the project’s business glossary doc.
Not surprising, it lacks clear and rigorous definitions and almost everything is example-driven.
It's like lacking both the rigour of academia, and the practicality of seasoned engineers at the same time.
In fact, the comment I made wasn't even about DDD, it was more about how I appreciated the construction of the argument. Agree or disagree (I happen to be a fan of DDD) I learned a lot from the article and I wish more articles were constructed like this.
It's also something that pushed me towards functional programming, I could finally understand concepts by understanding their definitions.
But there are good things there - data first for example, and the correct identification of interfaces - which will help avoid useless indirections often misunderstood as abstraction.
DDD does not have the concept of a table or database, let alone requires it. There's some degree of confusion expressed in your comment.
In a somewhat simplistic explanation, Domain-Driven design focuses on creating a model of the application domain, and with that define the necessary and sufficient operations that implement your requirements. All your operations are implemented in terms of your domain model. The tricky part is putting together a domain model that's consistent and doesn't allow the system to generate invalid data or be left in an inconsistent state. This leads us to the concept of root entities, and on top of them use aggregates to express transactions that involve multiple entities and values.
In the context of mostly crud operations unsurprisingly yes.
The value lies in knowing where and how you'll find the relevant pieces you need to touch to e.g. add new features or extend business rules, without ending up putting business logic in the wrong place (such as misplacing it in a service responsible for communicating with the db e.g.).
The secret of bolting on a good AMQP pattern is embedding signatures, GUID, UTC time, and expected state pre-conditions. The notion a problem domain even fits into a monolithic structure is laughably naive.
Usually, toy naive designs devolve into a dynamic object storage model on SQL, and it is ridiculously silly in this age.
Best regards =3
However I think there are some good nuggets in it that are worth taking away and using. The idea of ubiquitous language is useful. The best software teams I’ve worked on were great at talking with business users and customers. A ubiquitous language helped us avoid “programmer speak,” where we talked using names for things nobody had heard of but us and nouns and concepts nobody else understands. It allowed us to communicate more precisely and avoid mistakes throughout the process.
Good article. There’s always going to be counter-points and folks who prefer one way over another. I think there are some useful nuggets in this one too.
This is part of Domain Driven Design btw. I fully agree with this too. Makes it easier.
1. A model represents a record of a table (or a view). If a validation on a field is as low-level as null/not-null, it is better defined at a database level. Additional business logic related validations (that solely require the properties of the model as input), can be part of the model.
2. A repository provides facilities to interact with the database for either returning or creating instances of model and associated actions. Basically db-related actions live here.
3. A service can provide ways to perform operations related to the model with the help of the repository. E.g. fetch all records with a given area code.
4. For business-logic/workflows that span multiple models, higher-level services can invoke multiple lower-level services to orchestrate the workflow.
5. Controllers host the user-facing request/response related methods that invoked the higher-level services.
There can be variations of this with their pros and cons but this approach has worked well for me (mostly using Spring Boot).
If you're actually talking to other departments and have a very tricky domain I fully believe that DDD can solve some major problems. If not, I think you just want to show off how smart you are. And if you want to show me how smart you are do it by solving problems for people. Especially the problems that others can't solve.
I skimmed the article a bit, because it's structured in a strange way, and I couldn't quite find my footing. Then I came across the phrase
> it was everybody else that was wrong
in a section on composition vs inheritance. I know that it's a completely superficial thing to say, but I find it hard to take texts that sincerely use this phrase seriously.
- In domain-driven design, the Domain layer is one of the common layers in an object-oriented multi-layered architecture.
- DDD is against the idea of having a single unified model; instead it divides a large system into bounded contexts, each of which have their own model.
I do not know what these mean and I have found no real good explanations. In my 20+ years of experience I have not seen any problem which is solved elegantly with DDD based development and/or CQRS etc.
My simple rule of new tech / random things such is these is - what is the adoption rate and what are the major success stories. With technologies like RoR, React, Spring Boot, Kotlin etc. it is easy to see the reason for their adoption and success. I have heard no such stories for DDD. In fact, I have found the developers which advocate these have been the hardest to work with (rich on theory and opinion and poor in actual written software).
If you have a large system it makes sense to divide it into smaller independent pieces. If you have a (micro)-service architecture you have distinct services, in a monolith you might have modules.
It is a hard problem to know where exactly the boundaries between services or modules should be, in any case DDD calls the things that make sense to decouple bounded contexts.
If you have an SQL schema and your domain is money, some numbnuts is going to get the rounding wrong, accidentally calculate with floating point, forget to store currency, forget what the minor unit of each currency is, mix up credit/debit, and/or use a negative sign when there's an agreement not to.
You won't find out about the mistakes until after you've been running in prod for a while. And fixing the code won't fix the problem, because you only find out about the bad code after you've already produced bad data. But you still have to struggle to fix the bad code anyway. Use migrations to introduce new columns, while the system is running, etc.
What if instead of everyone updating one central table in-place, they just put their updates into some persistent queue, in some loose json format? If you were to keep that data around, you could at any time create new SQL tables and populate them from the source data. You could even afford to get it wrong a bunch of times, because you can throw away the tables.
After all that, I think the concept of a ubiquitous language is excellent. Everyone should standardize on a common set of vocabulary. We even took it so far as to have a dictionary of our business domain terms. It's so obvious in hindsight that it seems completely foolish to do anything else.
The rest of the book is basically just one guy's preferred way to organize a CRUD codebase. You can use his preferred terms (entities, repositories, value objects) and put things in the "right places" based on how he really liked to organize the codebase on this one job he did. After trying sincerely to follow this book for five years, I now think this has very little value. I don't particularly care what he thinks is the difference a service and a repository. When we had a need that wasn't obvious from the book, we'd sit in on countless meetings of people arguing the definitions while quoting the DDD book like it's a sacred text. Endless bike shedding about one guy's favorite way to organize a CRUD app isn't a high value use of a team's time. Neither is spending weeks renaming and moving everything to match the perfect terms.
We bought into the idea that if we followed this guy's favorite organization, everything would be better. It wasn't. We spent a huge amount of time reorganizing 1.5 million lines of c#. I don't think we ever even gained a week back of that time. It was a huge waste of time for what? To feel good that we were following some book? We didn't reduce or remove any bugs. We didn't find it magically easier to add new domain logic, if anything it was harder because we had to debate every feature to ensure it was designed "correctly" according to the will of Eric Evans.
For example, look at this snippet from the article
> "Objects outside the aggregate are allowed to hold references to the root but not to any other object of the aggregate. The aggregate root checks the consistency of changes in the aggregate."
Imagine trying to read an entire book filled with this kind of language. Lots of new terms, special rules, and vague reasoning. It's perfect for creating an environment of religious debate.
Today I think that books (Riting Software is another) that just prescribe an organization are a waste of time. They promise benefits for just using new special terms and putting logic in the "right place" but then don't deliver. It's just one dude's preferred names for things, not a magic recipe.
A better option for learning architecture in my experience is books like "Structure and Interpretation of Computer Programs" that teach excellent fundamentals. Data structures, algorithms, functions, state lifecycles, asynchronous code, dynamic code generation, and DSLs are far more valuable and people rarely give them enough time. Once I got very comfortable with those fundamentals did architecture appear to me as just the same concepts repeated at a larger scale. Drilling fundamentals was highly valuable, and I wished I'd spent more time on those early instead of reading books about DDD.
I started doing this after working on ANSI/ISO standards. It feels like a hassle until there's an 'ah-ha' moment where (e.g.) everyone realizes that what Group A called 'foo' was what Group B thought was a reference to a 'foo', and not the thing itself.
SICP is more general and definitely includes the really powerful bits, namely using abstraction to build higher-level languages (like DDD's domain model). DDD is more of an opinionated way to build business software specifically.
But then again this story has probably happened thousands of times...
Pretty much most frameworks can be described like this. I think its worth choosing one when you start then sticking with it. Like you say, refactoring down the road is a huge effort and not a huge benefit.
See, while I have very limited experience actually writing code in DDD style (and at the time, because I hadn't read the book or followed any course, terms like "aggregate" were pretty alien to me), I can really see the benefit of the DDD approach to mapping out and defining a problem space or company; ubiquitous language, bounded contexts (a "customer" may be different depending on whether you ask frontline customer support vs corporate support), and just mapping out the flows and processes of how a company or part thereof are just super valuable. Because a lot of the ones I've worked for as a consultant (read: code robot for hire) seem to rely a lot on a number of key people or departments that have this knowledge in their head, while IMO it should be a central "database" of the One Truth that everything else in the company follows from, including software.
And if the processes and requirements are clear, then software will follow, whether they do DDD practices or not.
I applaud the author for their intellectual pursuit, but I don't think it's fair to compare this to codebases where one does not simply pass the POST request body into the database.
> I have been designing and building enterprise applications for four decades, with the last two being dedicated to web-based enterprise applications using an object oriented language [...]
I don't see any applicability of this to my current work as I exited enterprise app dev decades ago. I still use OO languages but not in an OOP way.
I found success with this practice, too.
My approach in development is in general to give a working prototype as soon as possible to the relevant users and stakeholders and keep refining those.
The con of my approach is that then user think the project is in a much advanced state than it is.