A session on the CQRS pattern (Command Query Responsibility Separation) with C# code samples, and a discussion of what it means for your architecture and when and why you should use it. First presented at the Sydney Alt.Net group in June 2016.
11. public class UserAccount
{
//Only change state of the object via methods within the class
//State changing methods are marked internal
public bool IsActive { get; private set; }
internal void Activate()
{
IsActive = true;
}
internal void DeActivate()
{
IsActive = false;
}
}
12. public class AccountQueryService
{
IQueryable<UserAccount> db;
public AccountQueryService(IQueryable<UserAccount> db)
{
this.db = db;
}
public UserAccount FindByName(string accountName)
{
return db.Where(a => a.Name.Equals(accountName)).Single();
}
public IEnumerable<UserAccount> ActiveAccounts()
{
return db.All(a => a.IsActive);
}
}
Anti Pattern in use for
simplicity of sample code
Should return just the data
needed, not a domain object
13. public class AccountCommandHandler
{
AccountQueryService view; ILogger logger;
public AccountCommandHandler(AccountQueryService view, ILogger logger)
{
this.view = view; this.logger = logger;
}
public void ActivateAccount(string accountName)
{
try
{
var account = view.FindByName(accountName);
account.Activate();
}
catch { logger.Log("oops!"); throw; }
logger.Log("Account activated...");
}
}
32. Command Services/Handlers
UI
Commands
Domain Model
Repositories
DatabaseQuery Store
Query Service
Data Access Layer
Projections
Queries
Business Actions
Changes
Query
Store
No O/R
conversions
Describes
the user’s
intent
Optimized
for querying
Synchronous or
asynchronous
updates
Can simply be
denormalised tables
in the main DB
36. High level DDD Concepts:
Entity: discrete lifecycle and identity
Value Object: no lifecycle, identified only by attribute values.
Aggregate: one or more entities and value objects,
clustered together as a coherent whole.
Agg. Root: single entity that controls access to other
objects in the aggregate
Context: setting in which a word has specific meaning
(e.g ‘account’)
42. Why not de-normalise our data
to optimise for reads?
Why not store it in a different
database?
43. Question:
If we split the storage of our domain data and our
denormalised data…
How do we keep things in sync?
44. We could keep the data on the same
DB server…
…and use transactions/triggers to
update multiple tables at once.
45. Or we could think about eventual
consistency.
(Note: This is not required for CQRS)
46. When a Domain Entity changes state,
raise a Domain Event to describe
what happened.
47. These Domain Events can be treated
as messages and published on a
message bus
48. Event Handlers subscribe to the
events and update denormalised
views of data.
Makes transactions simpler.
Must consider the impact of Eventual Consistency
on our users.
49. Publishing Domain Events can lead to
thinking about Event Sourcing.
Note: Event Sourcing is NOT
REQUIRED for CQRS