Domain-Driven Design foundations
Almost all software is built by reasoning about its domain and various problems one encounters in it. What differentiates DDD projects from others is that insight into domain which results in deep model changes are implemented so that software reflects current understanding of the domain.
Often projects stop evolving after they have passed threshold of largish code base. It's much easier to use much simpler, but incorrect model than to evolve your current model to reflect new understanding of the domain.
To understand why DDD is useful Eric Evan's book provide very good introduction, so we will not try to explain that in detail, but will discuss some of the more advanced concepts.
Basic building blocks
Basic building blocks which are introduced in EE book are:
- value object - immutable object without identity
- entity - mutable object with identity
- aggregate root - entity which encapsulates multiple entities and values to create a single cohesive object
- specification - predicate which describes condition in the system
- validation - specification applied to object which define their integrity conditions
- domain event - an action that has happened in the system
- repository - abstraction over data access. Should expose access to aggregate roots
Value object immutability is something that is nice to have, but we don't enforce it in all languages. We try to offer developers access to DDD based on best practice in those languages.
Object identity is represented by their URI which is a string that represents their primary key. Most of the time primary key is just a single column, but for composite primary keys URI is combination of their values.
Specifications are concepts that are pervasive through the system, even more than aggregates and values. Combining LINQ with specifications allows for very expressive, yet type-safe way to work with data sources.
Domain events are another awesome concept, but they require backend code which reacts to them.
Model offered by Platform is an anemic one in a sense that most of the properties on objects have setters. While you can go very far using just this model, if you wish to do a real DDD it's time to create actual domain objects and its API.
Model maintained by Platform should be used for persistence, communication, reporting and other tedious infrastructure requirements.
This kind of domain evolution allows you to use the same development process for least important part of the applications like CRUD.
More complicated parts of the application can be built using domain events with transaction scripts on top of anemic model. You can even have object-like behaviors if extension methods are utilized to add external behavior on top of auto-maintained model.
And the most complicated ones can be shielded with DDD around the automatically maintained model.
Sometimes it feels kind of odd that you can maintain you database layer and application model layer from a single DSL model. But our experience has thought us that besides obvious advantages such as:
- smaller code count
- reduced error rate
- and easier maintenance
there are non-obvious ones such as:
- code evolvability
- enforced programming conventions
- reuse in the small and in the large
- transparent performance
While one could argue that code evolvability doesn't come for free since our hand written code which uses compiler generated objects will break on some changes, error count are order of magnitude smaller.
If one uses a type safe environment almost all changes can be fixed in small amount of time.
Industry practice is to empower developers with code generator tools and templates to allow them to tweak generated output.
The problem is that while this approach works for simple use cases it becomes too complicated for more complex ones. DSL Platform goal is not to just create code based on some template. It also tries to optimize that code and it's interaction with the system. Template based code generation falls short in such scenario.
While DSL Platform is highly extensible, which helps when customers have special needs, our primary focus is not to provide code generator, but to provide standardized access to features and patterns. This results in best practices enforced by default and allows code evolution.