I can give you hopefully a simplified version of DDD that you can start practicing right away:
For starters, DDD is NOT a bunch of code conventions and patterns. DDD uses patterns (like Repositories, Commands etc) to accomplish its goal of implementation, but DDD itself actually has nothing to do with writing code.
I want to stress that last point again: DDD has nothing to do with writing code.
DDD is a process by which you figure out what you're going to build, and how it needs to function to solve your client's problem. How you go about implementing it is actually a minor detail. What's important is developing the model - the "template" for your code implementation.
So what is this model? It's a conceptual representation of your client's business problem / business rules, and the model is expressed in what is known as a ubiquitous language that both you (the developer) the client understand, and are on the same page about.
So how do you go about practicing or applying Domain Driven Design? It's quite simple actually:
1. Know the Problem, Understand the Client
You can't model a solution until you FULLY (and I do mean fully) understand the problem that you're trying to solve. You know how some actors do method acting where they get completely immersed in preparing for the role they're going to be playing? Like Heath Ledger locking himself in a room for a month to prepare for his role as the Joker? Well some developers go to similar extremes to understand the problem space and business space. Some become certified accountants or get some other such qualification to really understand the industry. You don't have to do that, but it illustrates how important is it to really "get" the problem and business you're building a solution for.
This involves lots, and lots, and lots, and lots of questions and probing and discovery. Make the client go into excruciating detail about their process. Make them SHOW you their process, as you can likely spot inefficiencies or weirdness that they've become accustomed to or blind to. These are important to catch so that you're not needlessly modeling redundant or needless processes.
BTW, the client's processes and business rules is known as the domain, and your client is supposedly the domain expert. Though in reality, they often are not, and it's up to you to know their processes better than they do! They should help you become an expert - make their problem, your problem.
And it goes without saying, that if you're building an app for yourself, you're also the client and domain expert, so make yourself think carefully about the problem you're trying to solve, before you attempt to start modeling a solution!
2. Develop the Domain Model & Ubiquitous Language
As said above, the domain model is a conceptual representation of the client's business rules or problem space. It's basically how you would describe the system you need to build, before writing any code. The domain model is effectively a bridge between the real-world space in your client's domain, and the digital solution space you're ultimately going to build. It describes how the different parts of the system talk to and communicate with each other, what information they expose to each other, what entities or components are involved, and the behaviors that will reflect the client's business processes. What's key here is not to think of this in terms of how you would build it in code just yet, your model has to be expressed in terms of business intent.
It's important to note that the language used to describe this model should be carefully chosen, and won't necessarily semantically reflect the client's domain. Here's a real-world example from a composting company I did (am doing... please end... :( ) a site for. The client currently has manual account management through phone/email and lots of paper work, so they want to digitize it.
The client domain is the collection of processes used for managing customer accounts, pickup schedules, billing etc. This is what I had to model (e.g. create the domain model), and the context of the model was obviously a website for handling that.
One of the processes of that model was to allow customers to manage their accounts. This model already has two concepts now: "customers", and "accounts". Except here the term "accounts" is overloaded. Accounts means two things in this digital space: accounts the customers log into, and the billing accounts that represent the services they're actually paying for. In the domain space this distinction doesn't exist, but in the model, it has to.
So after explaining this to the client, we arrived at this language to describe that part of the model: "Customers can log in to their User Accounts to manage their Billing Accounts". Boom. Now I have a clear separation of those concepts, and I now know I have to model some kind of communication / behavior that allows Customers to interact with their User Accounts (e.g. email and password) as well as their Billing Accounts (phone, credit card, service address etc).
So now the terms "User Account" and "Billing Account" have specific meanings that have become part of the ubiquitous language. Also, in that phrase you'll notice there are two actions "log in", and "manage". "Log in" is pretty clear, but "manage" is ambiguous. After more back and forth, I pulled out several smaller processes that "manage" encompasses, and those processes became behaviors in my model.
Btw, it's perfectly normal for this model to evolve and change over time. Sometimes it takes a few tries before it makes sense. What's important here is that the model:
- Is clearly expressed: you can describe it and its behavior in plain words.
- Uses consistent, distinct terminology. Terminology is important - without consistent or distinct usage, the model is hard to understand.
- Captures the business intent. This goes back to fully understanding the problem.
- Is an unambiguous game plan for what you need to build. Even though it may change and evolve, you aren't guessing at what you need to build or how it should work.
3. Start Writing Code
Now that you:
- Know the problem fully
- Have modeled a clear solution
- Have a ubiquitous language to describe the pieces/behaviors of that solution
You can start writing code using whatever SOLID principles, patterns, and architectures you see fit. What's important here is that your code solutions follow the model as if it was a template, and the names you choose for classes and methods are identical to the ubiquitous language you've chosen to express / describe the model. Think of it as the "venacular" for the project.
Using this ubiquitous language is vital. It helps guarantee the code you write matches the behavior and properties of the solution you've modeled. It means the tests you write also match the acceptance criteria for the model, and it means if you have a QA department, what they're testing is aligned with how things should be functioning. It also means your documentation lines up with what's in the code. Everyone is on the same page.
TL;DR
DDD is a design process by which you fully understand the client's business rules / problem space (aka their domain), you develop a domain model - a conceptual representation of the solution, you collate a ubiquitous language to describe the components / processes / behaviors / properties of that model, and then you write code that follows that model like a template and uses the same ubiquitous language so that nobody is confused about why your code doesn't line up with the model.