A very nice wrap-up about onion architecture, thank you for sharing the presentation!
However, there’s a something that’s bothering me. Sometimes, you may have need to interact with some infrastructure parts from the inner layers. A good example will be logging – domain service may need to log some details or exceptions. How you would approach that?
To keep the right direction for dependencies (if you’re really committed to it) I see two possible options:
– Incorporating abstractions for required infrastructure parts into your domain. For example a generic ILog definition that will be implemented in outer layers.
– The outer layers will be “watching” the inner layers through domain specific messages/callbacks/errors/events and reacting to it accordingly.
In your experience is this a problem? How you will solve it?
Here is my answer:
That example is something that I discussed also during my presentation. I think we have to differentiate two types of logging and I’m not talking about auditing. From my point of view there is the logging which sole purpose is to support the analysis of the software for development and operational purpose in technical failure conditions. Let’s call it technical logging for lack of a better descriptive term. On the other hand there is the logging which tracks when an exceptional business condition occurs. Let’s call it business logging for lack of a better descriptive term. The technical logging I solve in an onion architecture the same as I would always do. Treat it as a cross cutting concern and use adapters, decorators and aspects to log boundaries in your system, for example when you are crossing subsystem boundaries. For the business logging I use the following approaches in an onion architecture:
- For event sourced domain models you explicitly model domain state transitions as events. A subscriber can be a business logger or even a technical logger which is either only partially interested in certain business edge cases or subscribes to everything in case of the technical logger.
- For non event-source domain models I think about business focused notifications. So for example if I have a domain model Customer which has a command method to make the customer preferred you need to think about what possible failure scenarios are there and who would be interested to see those. In that case you could simply pass an ICustomerCannotBeMadePreferredNotifier into the domain model by method injection. The domain model calls this interface when the business failure occurs. With that approach you have a use case centric interface which describes what actually is happening instead of a meaningless ILog interface.
I’m well aware that this generated a bit more interfaces but this also clearly separates the various concerns in your domain. What do you think?