CQRS makes alot of sense. However it seems mutually exclusive to the approach of using an ORM that provides change tracking. If I have one "channel" for queries to get my objects as RO, then theres no changes to track. If I have another channel for CUD+ commands, its more of an RPC aproach with light DTOs, then sending self-tracking entities to be merged at a lower tier.
How can these two approaches (CQRS / ORM + STEs) be reconciled?
A common way to structure a CQRS application is to act upon the domain model within command handlers.
You would send a command DTO, and in the handler you would invoke methods on your domain objects (you would not set the properties of your domain entities, as this is a major anti-pattern).
The methods in your domain objects would be responsible for modifying the internal state of your domain.
And at that point, your ORM would be responsible for persisting the changes to the internal state of your domain objects.
This way of structuring a CQRS application does not utilize Event Sourcing, but does utilize an ORM and self-tracking entities.
Here's a very simplified example.
public class AccountController
{
[HttpPost]
public ActionResult ChangePreferredStatus(Guid accountId, bool isPreferred)
{
if (isPreferred) {
// in response to user action, our controller commands our application to carry out an operation/state transition
Bus.SendCommand(new MakeAccountPreferredCommand(accountId));
}
}
}
public class MakeAccountPreferredCommandHander : IHandle<MakeAccountPreferredCommand>
{
public void Handle(MakeAccountPreferredCommand command)
{
using (var uow = new UnitOfWork()) {
var account = accountRepository.Get(command.AccountId);
if (account != null) {
// we tell the domain what we want it to do, we DO NOT fiddle with its state
account.MakePreferred();
}
uow.Accept(); // accepting the uow saves any changes to self-tracked entities
}
}
}
public class Account : SelfTrackedEntity
{
private Guid accountId;
private bool isPreferred; // ORM tracked *private* state
public void MakePreferred()
{
// entities are responsible for their own state and changing it
ValidateForMakePreferred();
isPreferred = true;
RaiseEvent(new AccountMadePreferredEvent(accountId));
}
}
Do these approaches need to be reconciled?
If you're using CQRS, why would you need or want change tracking? It's not like your objects are doing round trips.
In my current client's implementation, we have MongoDB with NoRM on the Command side and SQL Server 2008 R2 with WCF Data Services on the query side. No need for Entity Framework on the command side, no need for entity tracking, ... and it works beautifully!
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With