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)

./resources/original-code-snippets-from-gregyoung-8-cqrs-class.resources/screenshot.png

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);
}
}

./resources/original-code-snippets-from-gregyoung-8-cqrs-class.resources/screenshot.1.png

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);
}
}
}

./resources/original-code-snippets-from-gregyoung-8-cqrs-class.resources/screenshot.2.png

var handler = new TransactionalHandler<DeactiveateInventoryItemCommand>(
new LoggingHandler<DeactiveateInventoryItemCommand>(
new DeactiveateInventoryItemHandler()))

./resources/original-code-snippets-from-gregyoung-8-cqrs-class.resources/screenshot.3.png

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);
}
}

./resources/original-code-snippets-from-gregyoung-8-cqrs-class.resources/screenshot.4.png

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;
}

./resources/original-code-snippets-from-gregyoung-8-cqrs-class.resources/screenshot.5.png

EventHandler

[Transactional]
public class DeactivatedEventHandler : Handles <InventoryItemDeactivatedEvent> {
public void Handle(InventoryItemDeactivatedEvent e) {
INSERT INTO FOO () VALUES e.cjjd e.ccc
}
}

./resources/original-code-snippets-from-gregyoung-8-cqrs-class.resources/screenshot.6.png

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));
}
}

./resources/original-code-snippets-from-gregyoung-8-cqrs-class.resources/screenshot.7.png

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);
}
}
}

./resources/original-code-snippets-from-gregyoung-8-cqrs-class.resources/screenshot.8.png

InventoryItemDeactivatedEvent

public class InventoryItemDeactivatedEvent : Event {
public readonly Guid Id;
public InventoryItemDeactivatedEvent (Guid id) {
Id = id;
}

./resources/original-code-snippets-from-gregyoung-8-cqrs-class.resources/screenshot.9.png

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);
}
}

./resources/original-code-snippets-from-gregyoung-8-cqrs-class.resources/screenshot.10.png

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;
}
}
}

./resources/original-code-snippets-from-gregyoung-8-cqrs-class.resources/screenshot.11.png bdd

Given a set of events
when I issue a command
expect a set of events

./resources/original-code-snippets-from-gregyoung-8-cqrs-class.resources/screenshot.12.png

Given
an inventory item created
When I
deactivate inventory item
expect
inventory item deactivated

./resources/original-code-snippets-from-gregyoung-8-cqrs-class.resources/screenshot.13.png

Given
an inventory item created
an inventory item deactivated
When I
deactivate inventory item
expect
invalidoperation exception

./resources/original-code-snippets-from-gregyoung-8-cqrs-class.resources/screenshot.14.png

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
}

./resources/original-code-snippets-from-gregyoung-8-cqrs-class.resources/screenshot.15.png

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 {

./resources/original-code-snippets-from-gregyoung-8-cqrs-class.resources/screenshot.16.png

T ConvertTo<T,V>(V v) where T:Message, V:Message


DeactivatedEvent_v2 ConvertTo(DeactivatedEvent v) {
return new DeactivatedEvent_v2(v.Id, null);
}

./resources/original-code-snippets-from-gregyoung-8-cqrs-class.resources/screenshot.17.png