Use in real world
Requirements for DDD
Many argue that you should use DDD for only the most complicated projects, since it will help you manage complexity in those projects.
This stems from reasoning that working in a DDD-way requires a lot of overhead which is unnecessary for most projects.
We would like to strike a balance between only leveraging compiled libraries and manually creating and maintaining core domain model on top of compiled libraries.
Anemic Domain Model
When your aggregates have getters and setters but almost none of the behavior, you have an Anemic Domain Model, since your objects are just a dumb data container.
In most project this is just fine and in our experience even the more complicated projects can built this way when many layers of the application can be automatically maintained.
In .NET there is a nice feature called extension method. It's basically just a static method that looks like a method on the object you are extending, but it's not an actual part of it. It's a procedural/functional style method masking as a OOP style method.
In anemic model behavior is defined outside of object. This has it own advantages such as extensibility. Domain language is still there so it's still a very powerful way to model systems. Since external behavior can appear to be part of an object, even data structure can look like an actual object.
Open/closed principle also favors anemic model. Extension methods are designed for object extensibility and are more aligned with functional design where functions operate on data structures.
Code generated by DSL Platform should not be modified. This requirements makes it mandatory to create anemic model. But DDD should focus on reasoning about the domain and its interaction. When anemic model is creating more problems that it's solving, it time to hide it behind a rich manually created objects. Data transfer objects, reporting and other infrastructure parts can still be reused. This makes it much cheaper to implement DDD in its strictest way when required. This kind of architecture will make DDD easy to use even on less complex projects.
Domain Event is sometimes explained as something that has happened in the past. This must be followed by another explanation since it doesn't actually explains what a domain event is.
When people try and implement it they realize that it's not really working for all use cases and it's violating some of best programming practices.
For example, when domain events needs to affect multiple aggregates there are multiple ways to implement it. We can introduce the concept of Saga, which can raise multiple domain events on individual aggregates. The problem is that this new concepts and events that will be used are not really from domain space. They are technical ways to maintain some basic features which are also not really part of domain. Alternatively, domain event can affect multiple aggregates and persist changes in a single transaction.
When your architecture makes programming harder, you should have a really good reason for it. Maintaining consistency across aggregate doesn't mean that you should not persist multiple aggregates within a single transaction, especially when running on top of relational database.
Writing code is not the hard part in programming, that's actually the easiest one. Reasoning about domain, process and algorithm is the hard part. Communicating it to others it the hardest part. When your architecture makes it easier to reason and communicate about the domain, you know you are on the right path. If domain is hidden inside your code, only programmers have the whole picture. Understanding that major part of the code is a reflection of the domain and thus can be automatically maintained is the first step in realizing that the model is more important than the code.
Data transfer objects are simple reflection of domain model parts. In anemic model DTO are unnecessary since they are the same as domain "objects". In more complicated scenarios with "real" object when they are built on top of anemic model, it's trivial to convert object to it's corresponding DTO.
DSL Platform, with its declarative nature, tries to capture the knowledge and contracts from the domain. If you are exposing domain functionality to the outside, this means it's a contract for your system and it should be defined and discussed outside of code.
For example, Specification is a contract by which you expose search functionality. Domain Event is a contract by which you expose state changes. Snowflake is a contract by which you expose consumption of your data. Contracts should be of declarative nature, which means you should be able to describe its features and implementation should be provided with the infrastructure.
Model driven engineering
DSL Platform works by translating platform independent model (DSL files) to various libraries and databases. Programming is done by consuming that generated model and writing additional behaviors on top of it.
It's important to realize that DSL is a fully functional specification, meaning that it has to be valid (able to compile). This is the part of the system that the most experienced programmers will maintain, but it's a communication tool for everybody.
Realizing that DSL has to describe some parts of the system in great detail, it becomes apparent that it's not the GUI that should be focus of the DSL editor.
That is just a view of the DSL that is useful when communicating with someone on the high level, but it's the details that are important.
While you will not discuss implementation of the domain event handler with your UI expert, you should discuss structure of the Domain Event.
While there have been many attempts on MDE, none has yet really succeeded. We'd like to think DSL Platform can take it all the way.
It got all the necessary ingredients:
- independent model used as ubiquitous language
- covering of wide programming patterns
- multiple languages support
- support for object oriented relational databases
- ability to evolve (everything is extensible, even the DSL grammar)
- simplifies programming for easy and complicated use cases
- used in real world systems