Poor Man DDD
Poor Man DDD
Join the DZone community and get the full member experience.Join For Free
Get the Edge with a Professional Java IDE. 30-day free trial.
Some of my friends are doing great stuff with DDD but it is difficult to do DDD in legacy projects that last for years and started with a classical structure of UI+domain(anemic)+ORM/DAL, so it seems that exploring the fascinating land of DDD is an option only for people that start to work on new project, but it is really true? Clearly the main advantage of DDD is not related to technical questions, but it is to be found in the approach to problem: dialogue with DOMAIN EXPERTS, establishing an UBIQUITOUS LANGUAGE, and so on, but some of the grounding technical concepts of DDD can be inserted in legacy applications built with standard techniques to bring some of the advantages of DDD in existing projects.
If you think to CQRS it can be considered an evolution of the classic separation of databases to satisfy OLTP and OLAP needs, you usually have high normalized database for transactional processing, then you move data in a complete different database that is highly denormalized to satisfy reporting queries and if you have really complex structure you usually move to a OLAP Cube. The key concept is that you have two different model, one for reporting (and it is usually stale) and the other for all the rest of the system, and it worked damn well. Now consider CQRS is a natural evolution, because it advocates separation of a Write and Read model for the remaining part of the system. Whenever you need to modify data you should use the Write model and when it is time to Read data you should use a Read model, period.
This approach greatly simplify your code, because using a single model to read and write usually creates a lots of problem related with: ORM, mapping, profiler, and you start spending time to avoid incurring in performance penalties (Es. N+1 select), or how to create a NHibernate query for unrelated entities and so on. Life is really easier if your domain is designed for Write and you build a complete different domain to Read data, the key to reach this level of abstraction is the introduction of Domain Events. In one application I’m working on, we have a Denormalized version of data used for reporting that is result of some analysis, it usually gets populated at the morning so it contains stale data by one day. Now the software evolves, and some of the data that are taken from this denormalized database needs to be updated more often. The first solution that come in mind is running the scripts that populates this database more often, but since these script are designed to move large portion of data (one day of work) they are not meant to be run once minute or once 5 minutes.
Then I realize that this solution is not meant to solve the original problem, because talking with DOMAIN EXPERTS and asking them their needs, they simply told me: when I do this I want that data to be available as soon as possible in the report. The real need is that when a certain DOMAIN EVENT happens in the system, the analysis should start immediately generating another DOMAIN EVENT that tells that the analysis is finished and finally when the analysis is done I can update the denormalized database.
This reinforced my belief that DOMAIN EVENTS are the primary concept to move the core of your system from a legacy structure to a more DDD oriented architecture I’ve called this Poor Man DDD, because it lacks too many concepts of DDD, and most of the domain is still too interconnected and too much anemic, but at least the CORE DOMAIN is moving to a less coupled architecture. The key concepts is that Any modification that happens in the CORE DOMAIN should create a DOMAIN EVENT, this capture the When I do this part of the above sentence. This guarantee that your system has a log system written in UBIQUITOUS LANGUAGE, and this is the main advantage of introducing DOMAIN EVENTS. Now you have a lot of way to elaborate this stream of DOMAIN EVENT, you can simply attach HANDLERS that capture the I want that data to be available part and this perfectly solves your original problem.
Now we can answer to problem in more simpler way. When we need to add a behavior to the system related to some operation, people usually starts to think where to put that behavior, in the domain object? In the service call that manipulates the DOMAIN (anemic)? Create another service method and call it from the UI after the call to the original method (scattered logic)? Suppose the requirement is when I update the entity X you need to do some analysis only when the status change from Active to Confirmed. You have a lot of Choices. If your domain is not anemic probably you have a Update method directly in the entity, so you can put an if (oldstatus == active && newStatus == Confirmed) condition and write your code there. If the domain is more anemic probably you have a UpdateXXX in some service and you can put the same condition there, or you can work at UI level putting the condition after the call to UpdateXXX service method and call a new service method called DoAnalysisOnXXX() .
From a DDD perspective putting logic in the UI is the least appealing choice, putting it in Service Method call is not so good because you are creating an ANEMIC DOMAIN and the best solution seems to have an Update method in the entity with all the logic inside it because this is real DDD, after all the logic is in Domain Object, how it can be better? In the end I’m convinced that all three solution are not so good, because none of then capture the real important fact happened in the domain, the change of status of the XXX entity. If you concern regards only “where I put the logic” you are probably facing the wrong problem. The subtitle of Eric Evan’s book is takling complexity in the Heart of Software, but it does not mean to have ultra complicated Domain Classes that models a complicated business. The risk of storing all the logic inside Domain Classes is having really complex beasts that are almost unable to tame. My system should model my business, and my primary concern is not where I put the code, but how good is my code in modeling my business, and if you start to think with this perspective the question “where I put my logic” is not a problem and it is not the real goal of DDD.
The value of DDD is in speaking with DOMAIN EXPERTS, and the above requirements immediately tells you that the fact that entity X move from Active to Confirmed is something interesting for the business, and it should be modeled with a DOMAIN EVENT. Now you should start to have a better model, and maybe you discover that the important fact is that the destination status is Confirmed, because it models an important step in the real business, and you can decide to create: ConfirmedXXX and XXXUpdated distinct DOMAIN EVENTS, the first capture the Confirmation and the latter capture a generic change in the properties of the XXX entity. Now it is supereasy to decide where to put the analysis code: in an DomainEventHandler<ConfirmedXXX> handler. When you start to think in term of events you will find that you are able to solve problem with less and easier code and the complexity usually diminish.
In the end even the single introduction of DOMAIN EVENTS into a core part of a legacy system can really bring lot of benefits. The next step is Poor man CQRS.
Published at DZone with permission of Ricci Gian Maria , DZone MVB. See the original article here.
Opinions expressed by DZone contributors are their own.