Saturday, October 24, 2009

Thoughs on how to refactor an ugly ActiveRecord into something more flexible.

Separate command and query in your domain layer.

Command Accepting entities should not have public state.

Command Accepting entities probably have a bunch of overloads to Accept() with the parameter being the type.

Might be a good idea to have a domain event aggregator. This is like an internal message bus. There are several good articles out there about how to build one. Udi has one that I've used.

So, when a Command-Entity accepts a command it should publish internal state changes as events to the event aggregator. You can then take care of different concerns in different event handlers. Concerns might be:

  • Bring the eventually consistent reporting and/or query stores up to date.

  • The event logger should capture this too if you are using event streaming. Somehow this has to be fail-proof. Not sure how to ensure this yet. If it fails, issue compensating command? Logger has first chance to process and if fails, don't allow events to be publish?




Probably a good idea to make commands idempotent. If you are using a distributed system timestamps on the commands aren't very good. Guid probably can only help if you keep a blacklist of already process commands. This sort of has risks in multi-threaded scenarios. I saw Ayende comment about this. Another good technique is to ensure commands are handled serially and compare a version number.

Still haven't found any good examples of how to deal with large event stream stores. The DBA on a project is recommending Oracle indexed table with advanced compression using Oracle Xml DB. Maybe using a GUID-comb for the entity id.

A command accept method might look like:

  1. Acquire lock. Unless you are sure this command wont step on the currently executing one.

  2. ValidationEngine.Validate(command, entityDTO)

  3. I guess ValidationEngine would be a domain service injected into the Entity. Some folks say this is bad at all costs. Some say domain services are ok just don't inject infrastructure services. I'd love to see some real world examples of how to determine which is which. In that case the command handler is external to the entity. This makes it infrastructure. So then you can inject query services to get the DTO and use a ValidationService in combination with command and DTO data.

  4. PerformComputation(command)

  5. PublishStateChanges() and other events if necessary.

  6. Release lock if necessary



The command distributor would be responsible for logging the command. Also ensuring security concerns. Guess security is an infrastructure concern?

Not all business rules are handled inside the domain. Things like ensuring uniqueness of a given key is handled the simplest way closest to set itself. If using a SQL database, use a unique key constraint on the on the reporting/query store. If this fails, issue a compensating command. This sounds fine and all about what about the usable state of the entity in between that time? What about other listeners of the event? How do they get compensation? I suppose entity version in commands would prevent any old query derived commands from executing but what if the query store succeeds but the event logs fails? That could allow version OK commands, that really should get processed but timing could allow it. Can the event log talk to the query store? Maybe answered above.

No comments: