Code snippets from Video: GregYoung 8 CQRS Class
public class DeactivateInventoryItemCommand {
public readonly Guid InventoryItemId;
public readonly string Comment;
public DeactivateInventoryItemCommand (Guid id, string comment) {
InventoryItemId = id;
Comment = comment;
}
void DeactivateInventoryItem(Guid, string comment)
CommandHandler contains no logic, logic is in domain object delegates to domain object
public class DeactivateInventoryItemHandler : Handles<DeactivateInventoryItem> {
public DeactivateInventoryItemHandler(InventoryItemRepository rep){}
void Handle(DeactivateInventoryItem cmd) {
var item = repository.GetById(cmd.Id);
item.Deactivate(cmd.Comment);
}
}
Application Services cross-cutting concerns (logging, authentication)
public class TransactionalHandler<T> : Handles<T> where T:Message {
private Handles<T> next;
public TransactionalHandler(Handles<T> Next) {
next = Next;
}
public Handle(T cmd) {
using(new UnitOfWork()) {
next.Handle(cmd);
}
}
}
public class LoggingHandler<T> : Handles<T> where T:Message {
private Handles<T> next;
public TransactionalHandler(Handles<T> Next) {
next = Next;
}
public Handle(T cmd) {
using(new UnitOfWork()) {
next.Handle(cmd);
}
}
}
var handler = new TransactionalHandler<DeactiveateInventoryItemCommand>(
new LoggingHandler<DeactiveateInventoryItemCommand>(
new DeactiveateInventoryItemHandler()))
CommandHandler only 1
[RequiresPermission("Admin")]
[Transactional]
[LogsTo("C:\\foo.txt")]
public class DeactivateInventoryItemHandler : Handles<DeactivateInventoryItem> {
public DeactivateInventoryItemHandler(InventoryItemRepository rep){}
void Handle(DeactivateInventoryItem cmd) {
var item = repository.GetById(cmd.Id);
item.Deactivate(cmd.Comment);
}
}
command and event
public class DeactivateInventoryItem {
public readonly Guid InventoryItemId;
public readonly string Comment;
public DeactivateInventoryItemCommand (Guid id, string comment) {
InventoryItemId = id;
Comment = comment;
}
public class InventoryItemDeactivated {
public readonly Guid InventoryItemId;
public readonly string Comment;
public InventoryItemDeactivated (Guid id, string comment) {
InventoryItemId = id;
Comment = comment;
}
EventHandler
[Transactional]
public class DeactivatedEventHandler : Handles <InventoryItemDeactivatedEvent> {
public void Handle(InventoryItemDeactivatedEvent e) {
INSERT INTO FOO () VALUES e.cjjd e.ccc
}
}
AggregateRoot
public class InventoryItem : AggregateRoot {
WarehouseLocationInformation info;
bool activated;
public InventoryItem() {
RegisterHandler(typeof(DeactivatedEvent), ApplyDeactivatedEvent);
RegisterHandler(typeof(WarehouseLocationInformationRegistered), ApplyWareh...Registered);
}
private void Apply(DeactivatedEvent e) {
activated = false;
}
private void Apply(WarehouseLocationInformationRegistered e) {
info = new WarehouseLocationInformation(e.PhysicalLocation, e.GeoLocation, e.PaymentRate);
}
private void Deactivate() {
if(!activated) throw new InvalidOperationException();
if(comment==null) throw ...();
ApplyEvent(new InventoryDeactivatedEvent(id));
}
}
public class AggregateRoot : IAggregateRoot {
private Dictionary<Type, appliesEvent> lookup = new Dictionary<Type, appliesEvent>();
protected void RegisterHandler<T>(appliesEvent handler) {
lookup.add(typeof(T), handler);
}
public IEnumerable<Event> GetChanges() {
foreach(Event e in events) yield return e;
events.Clear();
}
protected void ApplyEvent(Event e) { ApplyEvent(e, true); }
private void ApplyEvent(Event e, bool add) {
appliesEvent handler;
if(!lookup.TryGetValue(e.getType(), out handler)) {
throw new HandlerNotFoundException();
}
handler(e);
if(add) UnitOfWork.GetCurrent().Enregister(e);
}
public void LoadFromHistory(IEnumerable<Event> events) {
foreach(Event e in events) {
ApplyEvent(e, false);
}
}
}
InventoryItemDeactivatedEvent
public class InventoryItemDeactivatedEvent : Event {
public readonly Guid Id;
public InventoryItemDeactivatedEvent (Guid id) {
Id = id;
}
Test
[TestFixture]
public class when_an_inventory_item_is_deactivated : AggregateRootTestFixture<InventoryItem> {
private Guid id = Guid.newGuid();
protected override IEnumerable<Event> Given() {
yield return New.InventoryItemCreated.WithName("Test");
}
protected override void When() {
sut.Deactivate();
}
[Test]
public void should_publish_deactivated_Event() {
Assert.Inconclusive();
}
[Test]
public void should_publish_event_with_correct_id() {
Assert.AreEquel(id, ((InventoryDeactivatedEvent) events[0]).AggregateId);
}
}
public abstract class AggregateRootTestFixture<T> : where T:IAggregateRoot, new() {
protected IAggregateRoot root;
protected Exception caught = null;
protected T sut;
protected List<Event> events;
protected abstract IEnumerable<Event> Given();
protected abstract void When();
[SetUp]
public void Setup() {
root = new T();
root.LoadFromHistory(Given());
try{
When();
events = new List<Event>(root.GetChanges());
} catch(Exception Ex) {
caught = Ex;
}
}
}
bdd
Given a set of events
when I issue a command
expect a set of events
Given
an inventory item created
When I
deactivate inventory item
expect
inventory item deactivated
Given
an inventory item created
an inventory item deactivated
When I
deactivate inventory item
expect
invalidoperation exception
View
public class InventoryCountView : Handles<InventoryAddedEvent>, Handles<InventoryItemRemovedEvent>, Handles<InventoryItemCreatedEvent>
public void Handle(InventoryItemAddedEvent) {
update InventoryCountView set currentcount = currentcount + e.Count
}
public void Handle(InventoryItemCreatedEvent e) {
insert into InventoryCountView currentcount values 0
}
Versioning
public class InventoryItemDeactivatedEvent : Event {
public readonly Guid Id;
public InventoryItemDeactivatedEvent (Guid id) {
Id = id;
}
}
public class InventoryItemDeactivatedEvent_v2 : Event {
public readonly Guid Id;
public readonly string Comment;
public InventoryItemDeactivatedEvent (Guid id, string comment) {
Id = id;
Comment = commment;
}
}
public class InventoryItemDeactivatedEvent_v3 : Event {
T ConvertTo<T,V>(V v) where T:Message, V:Message
DeactivatedEvent_v2 ConvertTo(DeactivatedEvent v) {
return new DeactivatedEvent_v2(v.Id, null);
}