Domain Driver Design, little explanation and example

The DDD, Eric Evans's book its hard to read, but expose very clear ideas that helps a lot in software development process.

I've being following more or less DDD during last year and that's my contribution to the community for this one that starts now.

Basic Concepts

Context

The setting in which a word or statement appears that determines its meaning.

Domain

A sphere of knowledge (ontology), influence, or activity. The subject area to which the user applies a program is the domain of the software.

Model

A system of abstractions that describes selected aspects of a domain and solve problems related to that domain.

Ubiquitous Language

A language structured around the domain model and used by all team members to connect all the activities of the team with the software.

Code structure

The structure is based 4 main layers.

Domain

This layer contains the abstraction of business logic in an isolated system. Making this easier to test, easier to read for devs and domain experts because the use obiquitous language, and all this things make it easier to maintain.

This models sometimes needs to be persistend in a particular state, but in this layer don't apply, generate an abstraction (interface) and forget about persistence, serialization or presentation in this layer. Focus on the business, that means object has to be instantiated just if they are consistent. But, if you forget about persistence maybe you are thinking about object identifiers, the ID's. Autoincrementals are not a solution, think:

  • An Autoincremental strategy lock transactions, one by one.
  • You model it's not consistent until is persisted.
  • You cannot make multi-request transactions.
  • Your object cannot work without a database.

Use UUID instead.

Don't use model/entity as a typed array. Avoid setters(are required in certain cases, like optional parameters) and avoid anemic models (only getters).

Examples:

Value objects are this objects you don't care about witch instance are you using, and this objects are immutable.

Example: Value object

Make use of interfaces on domain for this services you need and cannot implement on the domain (repositories, factories, complex validators, etc).

Create domain exceptions, never use general exceptions.

Make use of domain events to isolate logic between subdomains.

Make use of aggregates for collection of objects that are bound together by a root entity (aggregate root).

Infrastructure

The domain don't know and never should know if you are using doctrine or propel, symfony form or Zend form. The infrastructure layer is in charge of this kind of tasks.

Example: Wallet infrastructure

If you wanna have better explanation about model isolation working with an ORM see this talk, it's Ocramius explaining how to model entities in doctrine, no one better to explain it.

Application layer

The application layer defines the use cases of the domain and has access to domain and infrastructure layer.

See: Application layer

Each use case use a DTO (data transfer object) with all the parameters required to execute a task to modify the domain state.

It's a good practice to use DTOs for the views too, decoupling the presentation layer. Up to you.

User interface

The UI can be a console, a rest api or a website. But all this cases will make use of the same services of the application layer.

Conclusions

DDD decouples the application a lot, you will create a lot of files and sometimes you will feel you're doing over engineering, and of course you are because your model will need high level of encapsulation and isolation and that has a big cost. By other hand, testing its easier, maintain the code it's easier and reduce misconceptions. Once concepts are clear the layers create consistent boundaries to the different system responsibilities.
Follow TDD it's complex, not for the theory but for the way to code, sometimes it's difficult to create a test without create few context clases, and sometimes it's just impossible. DDD seems more logical to my use cases which in more cases are complex systems.

If you are doing a simple crud DDD will not solve your problems.

DDD it's also completely compatible with other paradigms like crud and cqrs. Remember there is no holy grail.

The example of how to implement DDD on a php application it's on this repo. It's a Symfony application and contains examples about how implement DDD with forms, doctrine and serialization. Uses Behat for aceptacion tests and phpunit for unit and functional with few very useful packages. Has docker for the jail environment.

I hope you will enjoy and report what you find wrong.