Tuesday, November 22, 2011

DDD Kata, part 2 (Add second aggregate root to domain. Service method stage 1)

Pre-requisite: DDD Kata part 1

Completed kata example on github: DDD Kata Part 2 sample code (github)

Kata Focus
1) A second aggregate root (Inventory).
2) Business methods in each aggregate root to transfer a Product's Item from Inventory to Invoice
3) Service method, stage 1: Non-persistent, no mocks; only to verify service method created, and that it passes Item across the aggregate roots.

NOTE   The next kata (part 3) will introduce design of service orchestration through mocks, repository interfaces, and IUnitOfWork with IUnitOfWorkFactory.

The Kata
Time goal: under 30 minutes

Domain: Inventory

Note   Steps 1 through 5 work through the same concepts (object identity, read-only collections, sets) as were explored in DDD kata part 1. You may wish to build to the end of step 5 only once, and then use this as a jumping off point for the newer material in kata 2. 

1. M: New test classes for Inventory, Product and Item--start by verifying that they are instances of DomainEntityBase.
2. M: Verify that Inventory.Products is read-only collection (instance of IEnumerable<Product>).3. M: Verify that Inventory.AddProduct() increments Inventory.Products collection property.
4. M: Verify that Product.Items is read-only collection (instance of IEnumerable<Item>).
5. M: Verify that Product.AddItem() increments Product.Items collection property.

New concepts begin here.
Domain: DomainEntityBase
Changes to DomainEntityBase are necessary for proper collection add/remove behaviour on transient objects (with Id = 0).
1. M: Verify that TransientId is of type System.Guid.
2. M: Verify that TransientId has a non-empty value (i.e. not = Guid.Empty).
3. B: Verify that two instances of DomainEntityBase with 0 Id, but matching TransientId values are equal.

Domain: Inventory
4. M: Verify that Inventory.GetNewOrExistingProductBy(string productCode) returns product with matching code.

Hint   The methods in tests 7 and 8 will both call to the method created in test 6.

5. M: Verify that Inventory.StockItemBy(string productCode, int serialNumber):
  • increments count of Inventory.Products.First().Items
  • sets Product.ProductCode and Item.SerialNumber
6. M: Verify that Inventory.PullItemBy(string productCode):
  • returns Item from Product.Items
  • decrements Product.Items (i.e. the Item has been removed)
Domain: Invoice
7. M: Refactor LineItem. Change its Product property to reference Item instead. Fix and update any broken tests.
8. B: Verify that Invoice.BillItem(Item item) increments Invoice.LineItems, and that the LineItem references the billed Item.

Service layer, Stage 1
Non-persistent, just verifying the football pass of Item from one aggregate root to another.

1. M: Create new class libraries:
  • Kata.Services.Tests.Unit
  • Kata.Services
2. B: Verify that the Item pulled from inventory by productCode is the same item billed to the Invoice.

Part 2 of the kata is complete.

Continue with DDD Kata Part 3