Freely admitting my naïveté and rookie/booter/redshirt status , I ask a simple question:
Why are messaging services such as logging often implemented via a pattern employing some kind of globally accessible object, yet similar services for passing messages from a domain model (perhaps using a Notification pattern) up to a client typically shown with handling inside of a controller/presenter try...catch statement?
I realize this is one of those error-prone 'one-size-fits-all' questions. It just seems like logging is messaging and so is notification, so why do we pass strings to calling clients for notifying them of the results of their actions while we have frameworks and static objects writing our log messages?
I experimented with an Notification-like object which was bound to my Unit of Work so that events/messages that needed to get communicated to the user would get aggregated and flushed after the unit of work was complete. This 'worked' by letting me simplify and standardize but I himmed-n-hawwed (redneck design technique) about the wisdom of that and in my current app haven't implemented it again.
Consider something like this, where we are using a globally accessibly Log object and have a 'Client' method that writes the message not to a log file, but is aggregated for consumption later depending on the client type:
Log.For(MyObject.GetType()).Client('The biz rule prevents this from happening.');
The objections I can come up with are:
- Doing something like this in your Domain Model violates keeping a clean separation between Domain and Client.
- If something can't happen as expected it IS an exception and so should be handled in traditional try..catch block
But then my answers are
- I am logging things that happen in my domain, so how is that different than sending client messages out? Obviously I won't be passing stack traces to this 'Client' and the messages would be 'prettier' but I'd want to notify things like specification constraint violations, side-effects which were intended and noteworthy, and so on, so why is throwing and exception for that (so that my client will catch it) better?
- I know the common view of exceptions is that things that can't be executed as expected are exceptional and so should be in a try..catch and handled appropriately. Ok, but I expect users to attempt some operations that due to some rule are not valid. I am not talking about invalid or missing data, but rather rules which perhaps due to the results of other data make the current request invalid. Or perform some operation that changes state in many objects which is not invalid or exceptional.
I use DTOs to pass from my Tasks layer (application services in DDD-speak I believe) up to my controller and its not overwhelming to just standardize and implement these common message carriers on up the chain, but there are many times when I'd like to notify the client of many changes that occurred due to the action they just took . For example, we have Materials Tests that can have retests, creating a parent-child relationship. Assigning a retest automatically removes it from one parent to another parent. This all happens and the new DTO is passed to the controller, but I want to let them know all the side-effects this action caused. Something like 'Test# 32 removed from Test#45. Sample Numbers have been regenerated for this project. '
I commonly am writing repetitive code to place messages into some kind of hash that is parsed and displayed to the user, and page design changes can make a lack of common practice painful when there is much repetition like this. Even though I keep actions as atomic as possible, there are still bound to be many state changes that are worth reporting to the user, no?
There are a ton of things I am sure I am not considering and I really don't want a slough of staticy global "Manager"-type objects doing infrastructure stuff invading my domain, but I'd love to hear how other folks implement client messages up the chain while promoting code-reuse in the controller/client layers or point out considerations I am no doubt missing.
02-11-2008 1:23 AM