Understand your users through Domain Driven Design (DDD)
Domain-Driven Design, is, according to the cover of the original book, about "Tackling Complexity in the Heart of Software".
Now what is that complexity that we're talking about?
When your client (or your boss) asks you to develop software to solve a problem, how do you start? You try to convert their description of the problem into code that does what is needed. But if you don't understand exactly what really is the problem, you'll build something that doesn't quite work as they expect.
What do you do then? When your users tell you that something doesn't work the way it should, that you misinterpreted some rule or scenario or business logic, you fix it.
When this keeps happening you might notice that you and your users are not quite thinking about the same thing. You'll be changing or expanding your logic to achieve the desired behaviour. Doing that again and again is increasing the complexity of your software.
This is an issue of communication. You and your client are speaking a different language. If you really understood each other, you wouldn't be creating software that did not solve the problem in the right way.
The purpose of DDD, Domain-Driven Design, is to overcome that barrier and to provide the building blocks to turn common understanding into software.
So that's two things: on the one hand, agree with your users what you're talking about. On the other hand, convert that agreement into working software. Those are the two core terms in DDD: Ubiquitous Language and Model-Driven Design.
The model and Ubiquitous Language
The domain is your client's business, and the software's subject area.
A domain expert is someone who works in that domain, has a problem, and wants you solve it with software. If you're a contractor, that's your customer. Otherwise, it's your boss or other people in your company.
Domain experts have limited understanding of software development. They use their own language that corresponds to their field. On the other hand, developers usually talk in functional terms about their system. When translating to functional terms, meaning is lost and the domain expert will no longer recognize the original concepts.
At the same time, you can't just use the domain expert's language to discuss the system because it is usually inexact, and you need exactitude in order to write software.
That's where the model and the closely related Ubiquitous Language come in.
A model is a systematic way of talking about the part of the domain that your software deals with.
When building a model, you look at what's happening in the relevant part of the domain and define very specific terms for each part of the process. Those terms become part of the Ubiquitous Language. The Ubiquitous Language is what you use to communicate within your team and in the code.
When you don't do that, you get fractured language: unclear boundaries between concepts, and unclear names for those concepts.
When you have a model based on a Ubiquitous Language, and you use it in communication with each other and in the code, the domain experts and the developers understand each other and it is immediately clear when an inconsistency arises.
Building blocks of models and software
Isolating the domain
Lots of code in an application has nothing to do with the domain. You want to decouple your domain model implementation from the rest of the software, so that you can look at the domain layer without being distracted. The domain layer is the heart of your software, the rest is just supporting elements.
This is Layered Architecture. In web development, we know this as MVC, although it's not always very thoughtfully implemented.
You can more or less map the diagram shown onto MVC, with the user interface layer being the view, the application layer being the controller, and the domain being layer being the model, with the ORM and other supporting code as the infrastructure.
However, because frameworks are often inflexible, you are encouraged to write your domain layer as a separate module.
Entities and Value Objects
Within the domain layer, you can divide data into entities and value objects. The difference is that with entities, identity matters, and with value objects it does not. Objects where identity matters might be people, products in a store, or books in a library.
Identity means that objects are not identical if they have the same attributes, but when they share the same reference. When a unique reference already exists in the domain (for example, a EAN for a product, or ISBN for a book), you should use that, if it fits the model. Otherwise, you have to come up with your own identifier.
Try to keep entities small, and move data that is not used to identify or find entities to separate value objects.
Value objects can keep information about entities, and a value object should be conceptually whole. For example, you won't have separate value objects for street, city, etc., but have a single value object to represent an address.
Make value objects immutable, to avoid surprises and complexity when working with them.
For a quick overview of the main concepts in DDD, have a look at Eric Evans' Domain-Driven Design Reference, freely available in PDF format.